// ReadKeyFileToECPriv returns an extendedkey from a file. // If there's no file there, it'll make one. If there's a password needed, // it'll prompt for one. One stop function. func ReadKeyFileToECPriv( filename string, p *chaincfg.Params) (*hdkeychain.ExtendedKey, error) { key32 := new([32]byte) _, err := os.Stat(filename) if err != nil { if os.IsNotExist(err) { // no key found, generate and save one fmt.Printf("No file %s, generating.\n", filename) rn, err := hdkeychain.GenerateSeed(32) if err != nil { return nil, err } copy(key32[:], rn[:]) err = SaveKeyToFileInteractive(filename, key32) if err != nil { return nil, err } } else { // unknown error, crash fmt.Printf("unknown\n") return nil, err } } key, err := LoadKeyFromFileInteractive(filename) if err != nil { return nil, err } rootpriv, err := hdkeychain.NewMaster(key[:], p) if err != nil { return nil, err } return rootpriv, nil }
func TstCreateMasterKey(t *testing.T, seed []byte) *hdkeychain.ExtendedKey { key, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) if err != nil { t.Fatal(err) } return key }
// New creates a new key manager and generates a master key from the provided // seed. The seed must be between 128 and 512 bits and should be generated by a // cryptographically secure random generation source. Refer to docs for // hdkeychain.NewMaster and hdkeychain.GenerateSeed for more info. func New(seed []byte) (*Manager, error) { // Generate master key. mKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) if err != nil { return nil, err } mgr := &Manager{ db: newDb((*MasterKey)(mKey), latestFileVersion), importedIDs: make([]string, 0, dbInitSize), derivedIDs: make([]string, 0, dbInitSize), } return mgr, nil }
// newMemWallet creates and returns a fully initialized instance of the // memWallet given a particular blockchain's parameters. func newMemWallet(net *chaincfg.Params, harnessID uint32) (*memWallet, error) { // The wallet's final HD seed is: hdSeed || harnessID. This method // ensures that each harness instance uses a deterministic root seed // based on its harness ID. var harnessHDSeed [chainhash.HashSize + 4]byte copy(harnessHDSeed[:], hdSeed[:]) binary.BigEndian.PutUint32(harnessHDSeed[:chainhash.HashSize], harnessID) hdRoot, err := hdkeychain.NewMaster(harnessHDSeed[:], net) if err != nil { return nil, nil } // The first child key from the hd root is reserved as the coinbase // generation address. coinbaseChild, err := hdRoot.Child(0) if err != nil { return nil, err } coinbaseKey, err := coinbaseChild.ECPrivKey() if err != nil { return nil, err } coinbaseAddr, err := keyToAddr(coinbaseKey, net) if err != nil { return nil, err } // Track the coinbase generation address to ensure we properly track // newly generated bitcoin we can spend. addrs := make(map[uint32]btcutil.Address) addrs[0] = coinbaseAddr return &memWallet{ net: net, coinbaseKey: coinbaseKey, coinbaseAddr: coinbaseAddr, hdIndex: 1, hdRoot: hdRoot, addrs: addrs, utxos: make(map[wire.OutPoint]*utxo), chainUpdateSignal: make(chan struct{}), reorgJournal: make(map[int32]*undoEntry), }, nil }
// This example demonstrates how to generate a cryptographically random seed // then use it to create a new master node (extended key). func ExampleNewMaster() { // Generate a random seed at the recommended length. seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) if err != nil { fmt.Println(err) return } // Generate a new master node using the seed. key, err := hdkeychain.NewMaster(seed) if err != nil { fmt.Println(err) return } // Show that the generated master node extended key is private. fmt.Println("Private Extended Key?:", key.IsPrivate()) // Output: // Private Extended Key?: true }
// TestZero ensures that zeroing an extended key works as intended. func TestZero(t *testing.T) { tests := []struct { name string master string extKey string net *chaincfg.Params }{ // Test vector 1 { name: "test vector 1 chain m", master: "000102030405060708090a0b0c0d0e0f", extKey: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", net: &chaincfg.MainNetParams, }, // Test vector 2 { name: "test vector 2 chain m", master: "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542", extKey: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", net: &chaincfg.MainNetParams, }, } // Use a closure to test that a key is zeroed since the tests create // keys in different ways and need to test the same things multiple // times. testZeroed := func(i int, testName string, key *hdkeychain.ExtendedKey) bool { // Zeroing a key should result in it no longer being private if key.IsPrivate() != false { t.Errorf("IsPrivate #%d (%s): mismatched key type -- "+ "want private %v, got private %v", i, testName, false, key.IsPrivate()) return false } parentFP := key.ParentFingerprint() if parentFP != 0 { t.Errorf("ParentFingerprint #%d (%s): mismatched "+ "parent fingerprint -- want %d, got %d", i, testName, 0, parentFP) return false } wantKey := "zeroed extended key" serializedKey := key.String() if serializedKey != wantKey { t.Errorf("String #%d (%s): mismatched serialized key "+ "-- want %s, got %s", i, testName, wantKey, serializedKey) return false } wantErr := hdkeychain.ErrNotPrivExtKey _, err := key.ECPrivKey() if !reflect.DeepEqual(err, wantErr) { t.Errorf("ECPrivKey #%d (%s): mismatched error: want "+ "%v, got %v", i, testName, wantErr, err) return false } wantErr = errors.New("pubkey string is empty") _, err = key.ECPubKey() if !reflect.DeepEqual(err, wantErr) { t.Errorf("ECPubKey #%d (%s): mismatched error: want "+ "%v, got %v", i, testName, wantErr, err) return false } wantAddr := "1HT7xU2Ngenf7D4yocz2SAcnNLW7rK8d4E" addr, err := key.Address(&chaincfg.MainNetParams) if err != nil { t.Errorf("Addres s #%d (%s): unexpected error: %v", i, testName, err) return false } if addr.EncodeAddress() != wantAddr { t.Errorf("Address #%d (%s): mismatched address -- want "+ "%s, got %s", i, testName, wantAddr, addr.EncodeAddress()) return false } return true } for i, test := range tests { // Create new key from seed and get the neutered version. masterSeed, err := hex.DecodeString(test.master) if err != nil { t.Errorf("DecodeString #%d (%s): unexpected error: %v", i, test.name, err) continue } key, err := hdkeychain.NewMaster(masterSeed, test.net) if err != nil { t.Errorf("NewMaster #%d (%s): unexpected error when "+ "creating new master key: %v", i, test.name, err) continue } neuteredKey, err := key.Neuter() if err != nil { t.Errorf("Neuter #%d (%s): unexpected error: %v", i, test.name, err) continue } // Ensure both non-neutered and neutered keys are zeroed // properly. key.Zero() if !testZeroed(i, test.name+" from seed not neutered", key) { continue } neuteredKey.Zero() if !testZeroed(i, test.name+" from seed neutered", key) { continue } // Deserialize key and get the neutered version. key, err = hdkeychain.NewKeyFromString(test.extKey) if err != nil { t.Errorf("NewKeyFromString #%d (%s): unexpected "+ "error: %v", i, test.name, err) continue } neuteredKey, err = key.Neuter() if err != nil { t.Errorf("Neuter #%d (%s): unexpected error: %v", i, test.name, err) continue } // Ensure both non-neutered and neutered keys are zeroed // properly. key.Zero() if !testZeroed(i, test.name+" deserialized not neutered", key) { continue } neuteredKey.Zero() if !testZeroed(i, test.name+" deserialized neutered", key) { continue } } }
// TestErrors performs some negative tests for various invalid cases to ensure // the errors are handled properly. func TestErrors(t *testing.T) { // Should get an error when seed has too few bytes. net := &chaincfg.MainNetParams _, err := hdkeychain.NewMaster(bytes.Repeat([]byte{0x00}, 15), net) if err != hdkeychain.ErrInvalidSeedLen { t.Errorf("NewMaster: mismatched error -- got: %v, want: %v", err, hdkeychain.ErrInvalidSeedLen) } // Should get an error when seed has too many bytes. _, err = hdkeychain.NewMaster(bytes.Repeat([]byte{0x00}, 65), net) if err != hdkeychain.ErrInvalidSeedLen { t.Errorf("NewMaster: mismatched error -- got: %v, want: %v", err, hdkeychain.ErrInvalidSeedLen) } // Generate a new key and neuter it to a public extended key. seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen) if err != nil { t.Errorf("GenerateSeed: unexpected error: %v", err) return } extKey, err := hdkeychain.NewMaster(seed, net) if err != nil { t.Errorf("NewMaster: unexpected error: %v", err) return } pubKey, err := extKey.Neuter() if err != nil { t.Errorf("Neuter: unexpected error: %v", err) return } // Deriving a hardened child extended key should fail from a public key. _, err = pubKey.Child(hdkeychain.HardenedKeyStart) if err != hdkeychain.ErrDeriveHardFromPublic { t.Errorf("Child: mismatched error -- got: %v, want: %v", err, hdkeychain.ErrDeriveHardFromPublic) } // NewKeyFromString failure tests. tests := []struct { name string key string err error neuter bool neuterErr error }{ { name: "invalid key length", key: "xpub1234", err: hdkeychain.ErrInvalidKeyLen, }, { name: "bad checksum", key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EBygr15", err: hdkeychain.ErrBadChecksum, }, { name: "pubkey not on curve", key: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ1hr9Rwbk95YadvBkQXxzHBSngB8ndpW6QH7zhhsXZ2jHyZqPjk", err: errors.New("pubkey isn't on secp256k1 curve"), }, { name: "unsupported version", key: "xbad4LfUL9eKmA66w2GJdVMqhvDmYGJpTGjWRAtjHqoUY17sGaymoMV9Cm3ocn9Ud6Hh2vLFVC7KSKCRVVrqc6dsEdsTjRV1WUmkK85YEUujAPX", err: nil, neuter: true, neuterErr: chaincfg.ErrUnknownHDKeyID, }, } for i, test := range tests { extKey, err := hdkeychain.NewKeyFromString(test.key) if !reflect.DeepEqual(err, test.err) { t.Errorf("NewKeyFromString #%d (%s): mismatched error "+ "-- got: %v, want: %v", i, test.name, err, test.err) continue } if test.neuter { _, err := extKey.Neuter() if !reflect.DeepEqual(err, test.neuterErr) { t.Errorf("Neuter #%d (%s): mismatched error "+ "-- got: %v, want: %v", i, test.name, err, test.neuterErr) continue } } } }
// TestBIP0032Vectors tests the vectors provided by [BIP32] to ensure the // derivation works as intended. func TestBIP0032Vectors(t *testing.T) { // The master seeds for each of the two test vectors in [BIP32]. testVec1MasterHex := "000102030405060708090a0b0c0d0e0f" testVec2MasterHex := "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" hkStart := uint32(0x80000000) tests := []struct { name string master string path []uint32 wantPub string wantPriv string net *chaincfg.Params }{ // Test vector 1 { name: "test vector 1 chain m", master: testVec1MasterHex, path: []uint32{}, wantPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", wantPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", net: &chaincfg.MainNetParams, }, { name: "test vector 1 chain m/0H", master: testVec1MasterHex, path: []uint32{hkStart}, wantPub: "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", wantPriv: "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", net: &chaincfg.MainNetParams, }, { name: "test vector 1 chain m/0H/1", master: testVec1MasterHex, path: []uint32{hkStart, 1}, wantPub: "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", wantPriv: "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", net: &chaincfg.MainNetParams, }, { name: "test vector 1 chain m/0H/1/2H", master: testVec1MasterHex, path: []uint32{hkStart, 1, hkStart + 2}, wantPub: "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", wantPriv: "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", net: &chaincfg.MainNetParams, }, { name: "test vector 1 chain m/0H/1/2H/2", master: testVec1MasterHex, path: []uint32{hkStart, 1, hkStart + 2, 2}, wantPub: "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV", wantPriv: "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", net: &chaincfg.MainNetParams, }, { name: "test vector 1 chain m/0H/1/2H/2/1000000000", master: testVec1MasterHex, path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000}, wantPub: "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", wantPriv: "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", net: &chaincfg.MainNetParams, }, // Test vector 2 { name: "test vector 2 chain m", master: testVec2MasterHex, path: []uint32{}, wantPub: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", wantPriv: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", net: &chaincfg.MainNetParams, }, { name: "test vector 2 chain m/0", master: testVec2MasterHex, path: []uint32{0}, wantPub: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", wantPriv: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", net: &chaincfg.MainNetParams, }, { name: "test vector 2 chain m/0/2147483647H", master: testVec2MasterHex, path: []uint32{0, hkStart + 2147483647}, wantPub: "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", wantPriv: "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", net: &chaincfg.MainNetParams, }, { name: "test vector 2 chain m/0/2147483647H/1", master: testVec2MasterHex, path: []uint32{0, hkStart + 2147483647, 1}, wantPub: "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", wantPriv: "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", net: &chaincfg.MainNetParams, }, { name: "test vector 2 chain m/0/2147483647H/1/2147483646H", master: testVec2MasterHex, path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646}, wantPub: "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", wantPriv: "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", net: &chaincfg.MainNetParams, }, { name: "test vector 2 chain m/0/2147483647H/1/2147483646H/2", master: testVec2MasterHex, path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646, 2}, wantPub: "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", wantPriv: "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", net: &chaincfg.MainNetParams, }, // Test vector 1 - Testnet { name: "test vector 1 chain m - testnet", master: testVec1MasterHex, path: []uint32{}, wantPub: "tpubD6NzVbkrYhZ4XgiXtGrdW5XDAPFCL9h7we1vwNCpn8tGbBcgfVYjXyhWo4E1xkh56hjod1RhGjxbaTLV3X4FyWuejifB9jusQ46QzG87VKp", wantPriv: "tprv8ZgxMBicQKsPeDgjzdC36fs6bMjGApWDNLR9erAXMs5skhMv36j9MV5ecvfavji5khqjWaWSFhN3YcCUUdiKH6isR4Pwy3U5y5egddBr16m", net: &chaincfg.TestNet3Params, }, { name: "test vector 1 chain m/0H - testnet", master: testVec1MasterHex, path: []uint32{hkStart}, wantPub: "tpubD8eQVK4Kdxg3gHrF62jGP7dKVCoYiEB8dFSpuTawkL5YxTus5j5pf83vaKnii4bc6v2NVEy81P2gYrJczYne3QNNwMTS53p5uzDyHvnw2jm", wantPriv: "tprv8bxNLu25VazNnppTCP4fyhyCvBHcYtzE3wr3cwYeL4HA7yf6TLGEUdS4QC1vLT63TkjRssqJe4CvGNEC8DzW5AoPUw56D1Ayg6HY4oy8QZ9", net: &chaincfg.TestNet3Params, }, { name: "test vector 1 chain m/0H/1 - testnet", master: testVec1MasterHex, path: []uint32{hkStart, 1}, wantPub: "tpubDApXh6cD2fZ7WjtgpHd8yrWyYaneiFuRZa7fVjMkgxsmC1QzoXW8cgx9zQFJ81Jx4deRGfRE7yXA9A3STsxXj4CKEZJHYgpMYikkas9DBTP", wantPriv: "tprv8e8VYgZxtHsSdGrtvdxYaSrryZGiYviWzGWtDDKTGh5NMXAEB8gYSCLHpFCywNs5uqV7ghRjimALQJkRFZnUrLHpzi2pGkwqLtbubgWuQ8q", net: &chaincfg.TestNet3Params, }, { name: "test vector 1 chain m/0H/1/2H - testnet", master: testVec1MasterHex, path: []uint32{hkStart, 1, hkStart + 2}, wantPub: "tpubDDRojdS4jYQXNugn4t2WLrZ7mjfAyoVQu7MLk4eurqFCbrc7cHLZX8W5YRS8ZskGR9k9t3PqVv68bVBjAyW4nWM9pTGRddt3GQftg6MVQsm", wantPriv: "tprv8gjmbDPpbAirVSezBEMuwSu1Ci9EpUJWKokZTYccSZSomNMLytWyLdtDNHRbucNaRJWWHANf9AzEdWVAqahfyRjVMKbNRhBmxAM8EJr7R15", net: &chaincfg.TestNet3Params, }, { name: "test vector 1 chain m/0H/1/2H/2 - testnet", master: testVec1MasterHex, path: []uint32{hkStart, 1, hkStart + 2, 2}, wantPub: "tpubDFfCa4Z1v25WTPAVm9EbEMiRrYwucPocLbEe12BPBGooxxEUg42vihy1DkRWyftztTsL23snYezF9uXjGGwGW6pQjEpcTpmsH6ajpf4CVPn", wantPriv: "tprv8iyAReWmmePqZv8hsVZzpx4KHXRyT4chmHdriW95m11R8Tyi3fDLYDM93bq4NGn1V6eCu5cE3zSQ6hPd31F2ApKXkZgTyn1V78pHjkq1V2v", net: &chaincfg.TestNet3Params, }, { name: "test vector 1 chain m/0H/1/2H/2/1000000000 - testnet", master: testVec1MasterHex, path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000}, wantPub: "tpubDHNy3kAG39ThyiwwsgoKY4iRenXDRtce8qdCFJZXPMCJg5dsCUHayp84raLTpvyiNA9sXPob5rgqkKvkN8S7MMyXbnEhGJMW64Cf4vFAoaF", wantPriv: "tprv8kgvuL81tmn36Fv9z38j8f4K5m1HGZRjZY2QxnXDy5PuqbP6a5TzoKWCgTcGHBu66W3TgSbAu2yX6sPza5FkHmy564Sh6gmCPUNeUt4yj2x", net: &chaincfg.TestNet3Params, }, } tests: for i, test := range tests { masterSeed, err := hex.DecodeString(test.master) if err != nil { t.Errorf("DecodeString #%d (%s): unexpected error: %v", i, test.name, err) continue } extKey, err := hdkeychain.NewMaster(masterSeed, test.net) if err != nil { t.Errorf("NewMaster #%d (%s): unexpected error when "+ "creating new master key: %v", i, test.name, err) continue } for _, childNum := range test.path { var err error extKey, err = extKey.Child(childNum) if err != nil { t.Errorf("err: %v", err) continue tests } } privStr := extKey.String() if privStr != test.wantPriv { t.Errorf("Serialize #%d (%s): mismatched serialized "+ "private extended key -- got: %s, want: %s", i, test.name, privStr, test.wantPriv) continue } pubKey, err := extKey.Neuter() if err != nil { t.Errorf("Neuter #%d (%s): unexpected error: %v ", i, test.name, err) continue } // Neutering a second time should have no effect. pubKey, err = pubKey.Neuter() if err != nil { t.Errorf("Neuter #%d (%s): unexpected error: %v", i, test.name, err) return } pubStr := pubKey.String() if pubStr != test.wantPub { t.Errorf("Neuter #%d (%s): mismatched serialized "+ "public extended key -- got: %s, want: %s", i, test.name, pubStr, test.wantPub) continue } } }
// TestBIP0032Vectors tests the vectors provided by [BIP32] to ensure the // derivation works as intended. func TestBIP0032Vectors(t *testing.T) { // The master seeds for each of the two test vectors in [BIP32]. testVec1MasterHex := "000102030405060708090a0b0c0d0e0f" testVec2MasterHex := "fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542" hkStart := uint32(0x80000000) tests := []struct { name string master string path []uint32 wantPub string wantPriv string }{ // Test vector 1 { name: "test vector 1 chain m", master: testVec1MasterHex, path: []uint32{}, wantPub: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", wantPriv: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", }, { name: "test vector 1 chain m/0H", master: testVec1MasterHex, path: []uint32{hkStart}, wantPub: "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", wantPriv: "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", }, { name: "test vector 1 chain m/0H/1", master: testVec1MasterHex, path: []uint32{hkStart, 1}, wantPub: "xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ", wantPriv: "xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs", }, { name: "test vector 1 chain m/0H/1/2H", master: testVec1MasterHex, path: []uint32{hkStart, 1, hkStart + 2}, wantPub: "xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5", wantPriv: "xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM", }, { name: "test vector 1 chain m/0H/1/2H/2", master: testVec1MasterHex, path: []uint32{hkStart, 1, hkStart + 2, 2}, wantPub: "xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV", wantPriv: "xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334", }, { name: "test vector 1 chain m/0H/1/2H/2/1000000000", master: testVec1MasterHex, path: []uint32{hkStart, 1, hkStart + 2, 2, 1000000000}, wantPub: "xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy", wantPriv: "xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76", }, // Test vector 2 { name: "test vector 2 chain m", master: testVec2MasterHex, path: []uint32{}, wantPub: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", wantPriv: "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U", }, { name: "test vector 2 chain m/0", master: testVec2MasterHex, path: []uint32{0}, wantPub: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", wantPriv: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt", }, { name: "test vector 2 chain m/0/2147483647H", master: testVec2MasterHex, path: []uint32{0, hkStart + 2147483647}, wantPub: "xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", wantPriv: "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9", }, { name: "test vector 2 chain m/0/2147483647H/1", master: testVec2MasterHex, path: []uint32{0, hkStart + 2147483647, 1}, wantPub: "xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", wantPriv: "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef", }, { name: "test vector 2 chain m/0/2147483647H/1/2147483646H", master: testVec2MasterHex, path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646}, wantPub: "xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", wantPriv: "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc", }, { name: "test vector 2 chain m/0/2147483647H/1/2147483646H/2", master: testVec2MasterHex, path: []uint32{0, hkStart + 2147483647, 1, hkStart + 2147483646, 2}, wantPub: "xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", wantPriv: "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j", }, } tests: for i, test := range tests { masterSeed, err := hex.DecodeString(test.master) if err != nil { t.Errorf("DecodeString #%d (%s): unexpected error: %v", i, test.name, err) continue } extKey, err := hdkeychain.NewMaster(masterSeed) if err != nil { t.Errorf("NewMaster #%d (%s): unexpected error when "+ "creating new master key: %v", i, test.name, err) continue } for _, childNum := range test.path { var err error extKey, err = extKey.Child(childNum) if err != nil { t.Errorf("err: %v", err) continue tests } } privStr := extKey.String() if privStr != test.wantPriv { t.Errorf("Serialize #%d (%s): mismatched serialized "+ "private extended key -- got: %s, want: %s", i, test.name, privStr, test.wantPriv) continue } pubKey, err := extKey.Neuter() if err != nil { t.Errorf("Neuter #%d (%s): unexpected error: %v ", i, test.name, err) continue } // Neutering a second time should have no effect. pubKey, err = pubKey.Neuter() if err != nil { t.Errorf("Neuter #%d (%s): unexpected error: %v", i, test.name, err) return } pubStr := pubKey.String() if pubStr != test.wantPub { t.Errorf("Neuter #%d (%s): mismatched serialized "+ "public extended key -- got: %s, want: %s", i, test.name, pubStr, test.wantPub) continue } } }