github.com/status-im/status-go/extkeys@v1.1.2/hdkey_test.go (about)

     1  package extkeys
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/hex"
     6  	"errors"
     7  	"fmt"
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/btcsuite/btcd/chaincfg"
    12  
    13  	"github.com/ethereum/go-ethereum/crypto"
    14  )
    15  
    16  const (
    17  	masterPrivKey1 = "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi"
    18  	masterPrivKey2 = "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U"
    19  )
    20  
    21  func TestBIP32Vectors(t *testing.T) {
    22  	// Test vectors 1, 2, and 3 are taken from the BIP32 specs:
    23  	// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vectors
    24  	tests := []struct {
    25  		name    string
    26  		seed    string
    27  		path    []uint32
    28  		pubKey  string
    29  		privKey string
    30  	}{
    31  		// Test vector 1
    32  		{
    33  			"test vector 1 chain m",
    34  			"000102030405060708090a0b0c0d0e0f",
    35  			[]uint32{},
    36  			"xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
    37  			masterPrivKey1,
    38  		},
    39  		{
    40  			"test vector 1 chain m/0H",
    41  			"000102030405060708090a0b0c0d0e0f",
    42  			[]uint32{HardenedKeyStart},
    43  			"xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw",
    44  			"xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7",
    45  		},
    46  		{
    47  			"test vector 1 chain m/0H/1",
    48  			"000102030405060708090a0b0c0d0e0f",
    49  			[]uint32{HardenedKeyStart, 1},
    50  			"xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ",
    51  			"xprv9wTYmMFdV23N2TdNG573QoEsfRrWKQgWeibmLntzniatZvR9BmLnvSxqu53Kw1UmYPxLgboyZQaXwTCg8MSY3H2EU4pWcQDnRnrVA1xe8fs",
    52  		},
    53  		{
    54  			"test vector 1 chain m/0H/1/2H",
    55  			"000102030405060708090a0b0c0d0e0f",
    56  			[]uint32{HardenedKeyStart, 1, HardenedKeyStart + 2},
    57  			"xpub6D4BDPcP2GT577Vvch3R8wDkScZWzQzMMUm3PWbmWvVJrZwQY4VUNgqFJPMM3No2dFDFGTsxxpG5uJh7n7epu4trkrX7x7DogT5Uv6fcLW5",
    58  			"xprv9z4pot5VBttmtdRTWfWQmoH1taj2axGVzFqSb8C9xaxKymcFzXBDptWmT7FwuEzG3ryjH4ktypQSAewRiNMjANTtpgP4mLTj34bhnZX7UiM",
    59  		},
    60  		{
    61  			"test vector 1 chain m/0H/1/2H/2",
    62  			"000102030405060708090a0b0c0d0e0f",
    63  			[]uint32{HardenedKeyStart, 1, HardenedKeyStart + 2, 2},
    64  			"xpub6FHa3pjLCk84BayeJxFW2SP4XRrFd1JYnxeLeU8EqN3vDfZmbqBqaGJAyiLjTAwm6ZLRQUMv1ZACTj37sR62cfN7fe5JnJ7dh8zL4fiyLHV",
    65  			"xprvA2JDeKCSNNZky6uBCviVfJSKyQ1mDYahRjijr5idH2WwLsEd4Hsb2Tyh8RfQMuPh7f7RtyzTtdrbdqqsunu5Mm3wDvUAKRHSC34sJ7in334",
    66  		},
    67  		{
    68  			"test vector 1 chain m/0H/1/2H/2/1000000000",
    69  			"000102030405060708090a0b0c0d0e0f",
    70  			[]uint32{HardenedKeyStart, 1, HardenedKeyStart + 2, 2, 1000000000},
    71  			"xpub6H1LXWLaKsWFhvm6RVpEL9P4KfRZSW7abD2ttkWP3SSQvnyA8FSVqNTEcYFgJS2UaFcxupHiYkro49S8yGasTvXEYBVPamhGW6cFJodrTHy",
    72  			"xprvA41z7zogVVwxVSgdKUHDy1SKmdb533PjDz7J6N6mV6uS3ze1ai8FHa8kmHScGpWmj4WggLyQjgPie1rFSruoUihUZREPSL39UNdE3BBDu76",
    73  		},
    74  		// Test vector 2
    75  		{
    76  			"test vector 2 chain m",
    77  			"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
    78  			[]uint32{},
    79  			"xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB",
    80  			masterPrivKey2,
    81  		},
    82  		{
    83  			"test vector 2 chain m/0",
    84  			"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
    85  			[]uint32{0},
    86  			"xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
    87  			"xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
    88  		},
    89  		{
    90  			"test vector 2 chain m/0/2147483647H",
    91  			"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
    92  			[]uint32{0, HardenedKeyStart + 2147483647},
    93  			"xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a",
    94  			"xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9",
    95  		},
    96  		{
    97  			"test vector 2 chain m/0/2147483647H/1",
    98  			"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
    99  			[]uint32{0, HardenedKeyStart + 2147483647, 1},
   100  			"xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon",
   101  			"xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef",
   102  		},
   103  		{
   104  			"test vector 2 chain m/0/2147483647H/1/2147483646H",
   105  			"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
   106  			[]uint32{0, HardenedKeyStart + 2147483647, 1, HardenedKeyStart + 2147483646},
   107  			"xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL",
   108  			"xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc",
   109  		},
   110  		{
   111  			"test vector 2 chain m/0/2147483647H/1/2147483646H/2",
   112  			"fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542",
   113  			[]uint32{0, HardenedKeyStart + 2147483647, 1, HardenedKeyStart + 2147483646, 2},
   114  			"xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt",
   115  			"xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j",
   116  		},
   117  		// Test vector 3
   118  		{
   119  			"test vector 3 chain m",
   120  			"4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be",
   121  			[]uint32{},
   122  			"xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13",
   123  			"xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6",
   124  		},
   125  		{
   126  			"test vector 3 chain m/0H",
   127  			"4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be",
   128  			[]uint32{HardenedKeyStart},
   129  			"xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y",
   130  			"xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L",
   131  		},
   132  	}
   133  
   134  tests:
   135  	for i, test := range tests {
   136  		seed, err := hex.DecodeString(test.seed)
   137  		if err != nil {
   138  			t.Errorf("DecodeString #%d (%s): %v", i, test.name, err)
   139  			continue
   140  		}
   141  
   142  		extKey, err := NewMaster(seed)
   143  		if err != nil {
   144  			t.Errorf("NewMasterKey #%d (%s): %v", i, test.name, err)
   145  			continue
   146  		}
   147  
   148  		if !extKey.IsPrivate {
   149  			t.Error("Master node must feature private key")
   150  			continue
   151  		}
   152  
   153  		extKey, err = extKey.Derive(test.path)
   154  		if err != nil {
   155  			t.Errorf("cannot derive child: %v", err)
   156  			continue tests
   157  		}
   158  
   159  		privKeyStr := extKey.String()
   160  		if privKeyStr != test.privKey {
   161  			t.Errorf("%d (%s): private key mismatch (expects: %s, got: %s)", i, test.name, test.privKey, privKeyStr)
   162  			continue
   163  		} else {
   164  			t.Logf("test %d (%s): %s", i, test.name, extKey.String())
   165  		}
   166  
   167  		pubKey, err := extKey.Neuter()
   168  		if err != nil {
   169  			t.Errorf("failed to Neuter key #%d (%s): %v", i, test.name, err)
   170  			return
   171  		}
   172  
   173  		// neutering twice should have no effect
   174  		pubKey, err = pubKey.Neuter()
   175  		if err != nil {
   176  			t.Errorf("failed to Neuter key #%d (%s): %v", i, test.name, err)
   177  			return
   178  		}
   179  
   180  		pubKeyStr := pubKey.String()
   181  		if pubKeyStr != test.pubKey {
   182  			t.Errorf("%d (%s): public key mismatch (expects: %s, got: %s)", i, test.name, test.pubKey, pubKeyStr)
   183  			continue
   184  		} else {
   185  			t.Logf("test %d (%s, public): %s", i, test.name, extKey.String())
   186  		}
   187  
   188  	}
   189  }
   190  
   191  func TestChildDerivation(t *testing.T) {
   192  	type testCase struct {
   193  		name    string
   194  		master  string
   195  		path    []uint32
   196  		wantKey string
   197  	}
   198  
   199  	// derive public keys from private keys
   200  	getPrivateChildDerivationTests := func() []testCase {
   201  		// The private extended keys for test vectors in [BIP32].
   202  		testVec1MasterPrivKey := masterPrivKey1
   203  		testVec2MasterPrivKey := masterPrivKey2
   204  
   205  		return []testCase{
   206  			// Test vector 1
   207  			{
   208  				name:    "test vector 1 chain m",
   209  				master:  testVec1MasterPrivKey,
   210  				path:    []uint32{},
   211  				wantKey: masterPrivKey1,
   212  			},
   213  			{
   214  				name:    "test vector 1 chain m/0",
   215  				master:  testVec1MasterPrivKey,
   216  				path:    []uint32{0},
   217  				wantKey: "xprv9uHRZZhbkedL37eZEnyrNsQPFZYRAvjy5rt6M1nbEkLSo378x1CQQLo2xxBvREwiK6kqf7GRNvsNEchwibzXaV6i5GcsgyjBeRguXhKsi4R",
   218  			},
   219  			{
   220  				name:    "test vector 1 chain m/0/1",
   221  				master:  testVec1MasterPrivKey,
   222  				path:    []uint32{0, 1},
   223  				wantKey: "xprv9ww7sMFLzJMzy7bV1qs7nGBxgKYrgcm3HcJvGb4yvNhT9vxXC7eX7WVULzCfxucFEn2TsVvJw25hH9d4mchywguGQCZvRgsiRaTY1HCqN8G",
   224  			},
   225  			{
   226  				name:    "test vector 1 chain m/0/1/2",
   227  				master:  testVec1MasterPrivKey,
   228  				path:    []uint32{0, 1, 2},
   229  				wantKey: "xprv9xrdP7iD2L1YZCgR9AecDgpDMZSTzP5KCfUykGXgjBxLgp1VFHsEeL3conzGAkbc1MigG1o8YqmfEA2jtkPdf4vwMaGJC2YSDbBTPAjfRUi",
   230  			},
   231  			{
   232  				name:    "test vector 1 chain m/0/1/2/2",
   233  				master:  testVec1MasterPrivKey,
   234  				path:    []uint32{0, 1, 2, 2},
   235  				wantKey: "xprvA2J8Hq4eiP7xCEBP7gzRJGJnd9CHTkEU6eTNMrZ6YR7H5boik8daFtDZxmJDfdMSKHwroCfAfsBKWWidRfBQjpegy6kzXSkQGGoMdWKz5Xh",
   236  			},
   237  			{
   238  				name:    "test vector 1 chain m/0/1/2/2/1000000000",
   239  				master:  testVec1MasterPrivKey,
   240  				path:    []uint32{0, 1, 2, 2, 1000000000},
   241  				wantKey: "xprvA3XhazxncJqJsQcG85Gg61qwPQKiobAnWjuPpjKhExprZjfse6nErRwTMwGe6uGWXPSykZSTiYb2TXAm7Qhwj8KgRd2XaD21Styu6h6AwFz",
   242  			},
   243  
   244  			// Test vector 2
   245  			{
   246  				name:    "test vector 2 chain m",
   247  				master:  testVec2MasterPrivKey,
   248  				path:    []uint32{},
   249  				wantKey: masterPrivKey2,
   250  			},
   251  			{
   252  				name:    "test vector 2 chain m/0",
   253  				master:  testVec2MasterPrivKey,
   254  				path:    []uint32{0},
   255  				wantKey: "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt",
   256  			},
   257  			{
   258  				name:    "test vector 2 chain m/0/2147483647",
   259  				master:  testVec2MasterPrivKey,
   260  				path:    []uint32{0, 2147483647},
   261  				wantKey: "xprv9wSp6B7cXJWXZRpDbxkFg3ry2fuSyUfvboJ5Yi6YNw7i1bXmq9QwQ7EwMpeG4cK2pnMqEx1cLYD7cSGSCtruGSXC6ZSVDHugMsZgbuY62m6",
   262  			},
   263  			{
   264  				name:    "test vector 2 chain m/0/2147483647/1",
   265  				master:  testVec2MasterPrivKey,
   266  				path:    []uint32{0, 2147483647, 1},
   267  				wantKey: "xprv9ysS5br6UbWCRCJcggvpUNMyhVWgD7NypY9gsVTMYmuRtZg8izyYC5Ey4T931WgWbfJwRDwfVFqV3b29gqHDbuEpGcbzf16pdomk54NXkSm",
   268  			},
   269  			{
   270  				name:    "test vector 2 chain m/0/2147483647/1/2147483646",
   271  				master:  testVec2MasterPrivKey,
   272  				path:    []uint32{0, 2147483647, 1, 2147483646},
   273  				wantKey: "xprvA2LfeWWwRCxh4iqigcDMnUf2E3nVUFkntc93nmUYBtb9rpSPYWa8MY3x9ZHSLZkg4G84UefrDruVK3FhMLSJsGtBx883iddHNuH1LNpRrEp",
   274  			},
   275  			{
   276  				name:    "test vector 2 chain m/0/2147483647/1/2147483646/2",
   277  				master:  testVec2MasterPrivKey,
   278  				path:    []uint32{0, 2147483647, 1, 2147483646, 2},
   279  				wantKey: "xprvA48ALo8BDjcRET68R5RsPzF3H7WeyYYtHcyUeLRGBPHXu6CJSGjwW7dWoeUWTEzT7LG3qk6Eg6x2ZoqD8gtyEFZecpAyvchksfLyg3Zbqam",
   280  			},
   281  
   282  			// Custom tests to trigger specific conditions.
   283  			{
   284  				// Seed 000000000000000000000000000000da.
   285  				name:    "Derived privkey with zero high byte m/0",
   286  				master:  "xprv9s21ZrQH143K4FR6rNeqEK4EBhRgLjWLWhA3pw8iqgAKk82ypz58PXbrzU19opYcxw8JDJQF4id55PwTsN1Zv8Xt6SKvbr2KNU5y8jN8djz",
   287  				path:    []uint32{0},
   288  				wantKey: "xprv9uC5JqtViMmgcAMUxcsBCBFA7oYCNs4bozPbyvLfddjHou4rMiGEHipz94xNaPb1e4f18TRoPXfiXx4C3cDAcADqxCSRSSWLvMBRWPctSN9",
   289  			},
   290  		}
   291  
   292  	}
   293  
   294  	// derive public keys from other public keys
   295  	getPublicChildDerivationTests := func() []testCase {
   296  		// The public extended keys for test vectors in [BIP32].
   297  		testVec1MasterPubKey := "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8"
   298  		testVec2MasterPubKey := "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB"
   299  
   300  		return []testCase{
   301  			// Test vector 1
   302  			{
   303  				name:    "test vector 1 chain m",
   304  				master:  testVec1MasterPubKey,
   305  				path:    []uint32{},
   306  				wantKey: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8",
   307  			},
   308  			{
   309  				name:    "test vector 1 chain m/0",
   310  				master:  testVec1MasterPubKey,
   311  				path:    []uint32{0},
   312  				wantKey: "xpub68Gmy5EVb2BdFbj2LpWrk1M7obNuaPTpT5oh9QCCo5sRfqSHVYWex97WpDZzszdzHzxXDAzPLVSwybe4uPYkSk4G3gnrPqqkV9RyNzAcNJ1",
   313  			},
   314  			{
   315  				name:    "test vector 1 chain m/0/1",
   316  				master:  testVec1MasterPubKey,
   317  				path:    []uint32{0, 1},
   318  				wantKey: "xpub6AvUGrnEpfvJBbfx7sQ89Q8hEMPM65UteqEX4yUbUiES2jHfjexmfJoxCGSwFMZiPBaKQT1RiKWrKfuDV4vpgVs4Xn8PpPTR2i79rwHd4Zr",
   319  			},
   320  			{
   321  				name:    "test vector 1 chain m/0/1/2",
   322  				master:  testVec1MasterPubKey,
   323  				path:    []uint32{0, 1, 2},
   324  				wantKey: "xpub6BqyndF6rhZqmgktFCBcapkwubGxPqoAZtQaYewJHXVKZcLdnqBVC8N6f6FSHWUghjuTLeubWyQWfJdk2G3tGgvgj3qngo4vLTnnSjAZckv",
   325  			},
   326  			{
   327  				name:    "test vector 1 chain m/0/1/2/2",
   328  				master:  testVec1MasterPubKey,
   329  				path:    []uint32{0, 1, 2, 2},
   330  				wantKey: "xpub6FHUhLbYYkgFQiFrDiXRfQFXBB2msCxKTsNyAExi6keFxQ8sHfwpogY3p3s1ePSpUqLNYks5T6a3JqpCGszt4kxbyq7tUoFP5c8KWyiDtPp",
   331  			},
   332  			{
   333  				name:    "test vector 1 chain m/0/1/2/2/1000000000",
   334  				master:  testVec1MasterPubKey,
   335  				path:    []uint32{0, 1, 2, 2, 1000000000},
   336  				wantKey: "xpub6GX3zWVgSgPc5tgjE6ogT9nfwSADD3tdsxpzd7jJoJMqSY12Be6VQEFwDCp6wAQoZsH2iq5nNocHEaVDxBcobPrkZCjYW3QUmoDYzMFBDu9",
   337  			},
   338  
   339  			// Test vector 2
   340  			{
   341  				name:    "test vector 2 chain m",
   342  				master:  testVec2MasterPubKey,
   343  				path:    []uint32{},
   344  				wantKey: "xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB",
   345  			},
   346  			{
   347  				name:    "test vector 2 chain m/0",
   348  				master:  testVec2MasterPubKey,
   349  				path:    []uint32{0},
   350  				wantKey: "xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH",
   351  			},
   352  			{
   353  				name:    "test vector 2 chain m/0/2147483647",
   354  				master:  testVec2MasterPubKey,
   355  				path:    []uint32{0, 2147483647},
   356  				wantKey: "xpub6ASAVgeWMg4pmutghzHG3BohahjwNwPmy2DgM6W9wGegtPrvNgjBwuZRD7hSDFhYfunq8vDgwG4ah1gVzZysgp3UsKz7VNjCnSUJJ5T4fdD",
   357  			},
   358  			{
   359  				name:    "test vector 2 chain m/0/2147483647/1",
   360  				master:  testVec2MasterPubKey,
   361  				path:    []uint32{0, 2147483647, 1},
   362  				wantKey: "xpub6CrnV7NzJy4VdgP5niTpqWJiFXMAca6qBm5Hfsry77SQmN1HGYHnjsZSujoHzdxf7ZNK5UVrmDXFPiEW2ecwHGWMFGUxPC9ARipss9rXd4b",
   363  			},
   364  			{
   365  				name:    "test vector 2 chain m/0/2147483647/1/2147483646",
   366  				master:  testVec2MasterPubKey,
   367  				path:    []uint32{0, 2147483647, 1, 2147483646},
   368  				wantKey: "xpub6FL2423qFaWzHCvBndkN9cbkn5cysiUeFq4eb9t9kE88jcmY63tNuLNRzpHPdAM4dUpLhZ7aUm2cJ5zF7KYonf4jAPfRqTMTRBNkQL3Tfta",
   369  			},
   370  			{
   371  				name:    "test vector 2 chain m/0/2147483647/1/2147483646/2",
   372  				master:  testVec2MasterPubKey,
   373  				path:    []uint32{0, 2147483647, 1, 2147483646, 2},
   374  				wantKey: "xpub6H7WkJf547AiSwAbX6xsm8Bmq9M9P1Gjequ5SipsjipWmtXSyp4C3uwzewedGEgAMsDy4jEvNTWtxLyqqHY9C12gaBmgUdk2CGmwachwnWK",
   375  			},
   376  		}
   377  	}
   378  
   379  	runTests := func(tests []testCase) {
   380  		for i, test := range tests {
   381  			extKey, err := NewKeyFromString(test.master)
   382  			if err != nil {
   383  				t.Errorf("NewKeyFromString #%d (%s): unexpected error creating extended key: %v", i, test.name, err)
   384  				continue
   385  			}
   386  			extKey, err = extKey.Derive(test.path)
   387  			if err != nil {
   388  				t.Errorf("cannot derive child: %v", err)
   389  				continue
   390  			}
   391  
   392  			gotKey := extKey.String()
   393  			if gotKey != test.wantKey {
   394  				t.Errorf("Child #%d (%s): mismatched serialized extended key -- got: %s, want: %s", i, test.name, gotKey, test.wantKey)
   395  				continue
   396  			} else {
   397  				t.Logf("test %d (%s): %s", i, test.name, extKey.String())
   398  			}
   399  		}
   400  	}
   401  
   402  	runTests(getPrivateChildDerivationTests())
   403  	runTests(getPublicChildDerivationTests())
   404  }
   405  
   406  func TestErrors(t *testing.T) {
   407  	// Should get an error when seed has too few bytes.
   408  	_, err := NewMaster(bytes.Repeat([]byte{0x00}, 15))
   409  	if err != ErrInvalidSeedLen {
   410  		t.Errorf("NewMaster: mismatched error -- got: %v, want: %v",
   411  			err, ErrInvalidSeedLen)
   412  	}
   413  
   414  	// Should get an error when seed has too many bytes.
   415  	_, err = NewMaster(bytes.Repeat([]byte{0x00}, 65))
   416  	if err != ErrInvalidSeedLen {
   417  		t.Errorf("NewMaster: mismatched error -- got: %v, want: %v",
   418  			err, ErrInvalidSeedLen)
   419  	}
   420  
   421  	// Generate a new key and neuter it to a public extended key.
   422  	mnemonic := NewMnemonic()
   423  
   424  	phrase, err := mnemonic.MnemonicPhrase(128, EnglishLanguage)
   425  	if err != nil {
   426  		t.Errorf("Test failed: could not create seed: %s", err)
   427  	}
   428  
   429  	password := "badpassword"
   430  	extKey, err := NewMaster(mnemonic.MnemonicSeed(phrase, password))
   431  	if err != nil {
   432  		t.Errorf("unexpected error: %v", err)
   433  		return
   434  	}
   435  
   436  	pubKey, err := extKey.Neuter()
   437  	if err != nil {
   438  		t.Errorf("Neuter: unexpected error: %v", err)
   439  		return
   440  	}
   441  
   442  	// Deriving a hardened child extended key should fail from a public key.
   443  	_, err = pubKey.Child(HardenedKeyStart)
   444  	if err != ErrDerivingHardenedFromPublic {
   445  		t.Errorf("Child: mismatched error -- got: %v, want: %v", err, ErrDerivingHardenedFromPublic)
   446  	}
   447  
   448  	_, err = pubKey.BIP44Child(CoinTypeETH, 0)
   449  	if err != ErrInvalidMasterKey {
   450  		t.Errorf("BIP44Child: mistmatched error -- got: %v, want: %v", err, ErrInvalidMasterKey)
   451  	}
   452  
   453  	childKey, _ := extKey.Child(HardenedKeyStart + 1)
   454  	_, err = childKey.BIP44Child(CoinTypeETH, 0) // this should be called from master only
   455  	if err != ErrInvalidMasterKey {
   456  		t.Errorf("BIP44Child: mistmatched error -- got: %v, want: %v", err, ErrInvalidMasterKey)
   457  	}
   458  
   459  	// NewKeyFromString failure tests.
   460  	tests := []struct {
   461  		name      string
   462  		key       string
   463  		err       error
   464  		neuter    bool
   465  		neuterErr error
   466  		extKey    *ExtendedKey
   467  	}{
   468  		{
   469  			name: "invalid key length",
   470  			key:  "xpub1234",
   471  			err:  ErrInvalidKeyLen,
   472  		},
   473  		{
   474  			name: "bad checksum",
   475  			key:  "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EBygr15",
   476  			err:  ErrBadChecksum,
   477  		},
   478  		{
   479  			name: "pubkey not on curve",
   480  			key:  "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ1hr9Rwbk95YadvBkQXxzHBSngB8ndpW6QH7zhhsXZ2jHyZqPjk",
   481  			err:  errors.New("invalid square root"),
   482  		},
   483  		{
   484  			name:      "unsupported version",
   485  			key:       "xbad4LfUL9eKmA66w2GJdVMqhvDmYGJpTGjWRAtjHqoUY17sGaymoMV9Cm3ocn9Ud6Hh2vLFVC7KSKCRVVrqc6dsEdsTjRV1WUmkK85YEUujAPX",
   486  			err:       nil,
   487  			neuter:    true,
   488  			neuterErr: chaincfg.ErrUnknownHDKeyID,
   489  		},
   490  		{
   491  			name:      "zeroed extended key",
   492  			key:       EmptyExtendedKeyString,
   493  			err:       nil,
   494  			neuter:    false,
   495  			neuterErr: nil,
   496  			extKey:    &ExtendedKey{},
   497  		},
   498  		{
   499  			name:      "empty string",
   500  			key:       "",
   501  			err:       nil,
   502  			neuter:    false,
   503  			neuterErr: nil,
   504  			extKey:    &ExtendedKey{},
   505  		},
   506  	}
   507  
   508  	for i, test := range tests {
   509  		t.Run(test.name, func(t *testing.T) {
   510  			extKey, err := NewKeyFromString(test.key)
   511  			if !reflect.DeepEqual(err, test.err) {
   512  				t.Errorf("NewKeyFromString #%d (%s): mismatched error -- got: %v, want: %v", i, test.name, err, test.err)
   513  				return
   514  			}
   515  
   516  			if test.neuter {
   517  				_, err := extKey.Neuter()
   518  				if !reflect.DeepEqual(err, test.neuterErr) {
   519  					t.Errorf("Neuter #%d (%s): mismatched error -- got: %v, want: %v", i, test.name, err, test.neuterErr)
   520  					return
   521  				}
   522  			}
   523  
   524  			if test.extKey != nil {
   525  				if !reflect.DeepEqual(extKey, test.extKey) {
   526  					t.Errorf("ExtKey #%d (%s): mismatched extended key -- got: %+v, want: %+v", i, test.name, extKey, test.extKey)
   527  					return
   528  				}
   529  			}
   530  		})
   531  	}
   532  }
   533  
   534  func TestMaxDepth(t *testing.T) {
   535  	mnemonic := NewMnemonic()
   536  	phrase, err := mnemonic.MnemonicPhrase(128, EnglishLanguage)
   537  	if err != nil {
   538  		t.Errorf("Test failed: could not create mnemonic phrase: %v", err)
   539  	}
   540  
   541  	lastParentKey, err := NewMaster(mnemonic.MnemonicSeed(phrase, "test-password"))
   542  	if err != nil {
   543  		t.Errorf("couldn't create master extended key: %v", err)
   544  	}
   545  
   546  	lastParentKey.Depth = 255
   547  
   548  	_, err = lastParentKey.Child(0)
   549  	if err != ErrMaxDepthExceeded {
   550  		t.Errorf("Expected ErrMaxDepthExceeded, got %+v", err)
   551  	}
   552  }
   553  
   554  func TestBIP44ChildDerivation(t *testing.T) {
   555  	keyString := masterPrivKey1
   556  	derivedKey1String := "xprvA38t8tFW4vbuB7WJXEqMFmZqRrcZUKWqqMcGjjKjr2hbfvPhRtLLJGL4ayWG8shF1VkuUikVGodGshLiKRS7WrdsrGSVDQCY33qoPBxG2Kp"
   557  	derivedKey2String := "xprvA38t8tFW4vbuDgBNpekPnuMSfpWziDLdF7W9Zd3mPy6eDEkM5F17vk59RtVoFbNdBBq84EJf5CqdZhhEoBkAM4DXHQsDqvUxVnncfnDQEFg"
   558  
   559  	extKey, err := NewKeyFromString(keyString)
   560  	if err != nil {
   561  		t.Error("NewKeyFromString: cannot create extended key")
   562  	}
   563  
   564  	accounKey1, err := extKey.BIP44Child(CoinTypeETH, 0)
   565  	if err != nil {
   566  		t.Error("Error dering BIP44-compliant key")
   567  	}
   568  	if accounKey1.String() != derivedKey1String {
   569  		t.Errorf("BIP44Child: key mismatch -- got: %v, want: %v", accounKey1.String(), derivedKey1String)
   570  	}
   571  	t.Logf("Account 1 key: %s", accounKey1.String())
   572  
   573  	accounKey2, err := extKey.BIP44Child(CoinTypeETH, 1)
   574  	if err != nil {
   575  		t.Error("Error dering BIP44-compliant key")
   576  	}
   577  	if accounKey2.String() != derivedKey2String {
   578  		t.Errorf("BIP44Child: key mismatch -- got: %v, want: %v", accounKey2.String(), derivedKey2String)
   579  	}
   580  	t.Logf("Account 1 key: %s", accounKey2.String())
   581  }
   582  
   583  func TestChildForPurpose(t *testing.T) {
   584  	masterKey, err := NewKeyFromString(masterPrivKey1)
   585  	if err != nil {
   586  		t.Error("NewKeyFromString: cannot create master extended key")
   587  	}
   588  
   589  	bip44Child, err := masterKey.EthBIP44Child(0)
   590  	if err != nil {
   591  		t.Error("Error deriving BIP44-compliant key")
   592  	}
   593  
   594  	eip1581Child, err := masterKey.EthEIP1581ChatChild(0)
   595  	if err != nil {
   596  		t.Error("Error deriving EIP1581-compliant key")
   597  	}
   598  
   599  	walletChild, err := masterKey.ChildForPurpose(KeyPurposeWallet, 0)
   600  	if err != nil {
   601  		t.Error("Error deriving BIP44-compliant key")
   602  	}
   603  
   604  	chatChild, err := masterKey.ChildForPurpose(KeyPurposeChat, 0)
   605  	if err != nil {
   606  		t.Error("Error deriving EIP1581-compliant key")
   607  	}
   608  
   609  	// Check that ChildForPurpose with KeyPurposeWallet generates a BIP44 key
   610  	if walletChild.String() != bip44Child.String() {
   611  		t.Errorf("wrong wallet key. expected to be equal to bip44Child")
   612  	}
   613  
   614  	// Check that ChildForPurpose with KeyPurposeChat generates a EIP1581 key
   615  	if chatChild.String() != eip1581Child.String() {
   616  		t.Errorf("wrong chat key. expected to be equal to eip1581Child")
   617  	}
   618  
   619  	// Check that the key generated by ChildForPurpose with KeyPurposeChat is different from the BIP44
   620  	if walletChild.String() == chatChild.String() {
   621  		t.Errorf("wrong chat key. expected to be different from the wallet key")
   622  	}
   623  }
   624  
   625  func TestHDWalletCompatibility(t *testing.T) {
   626  	password := "TREZOR"
   627  	mnemonic := NewMnemonic()
   628  	mnemonicPhrase := "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
   629  	seed := mnemonic.MnemonicSeed(mnemonicPhrase, password)
   630  	rootKey, err := NewMaster(seed)
   631  	if err != nil {
   632  		t.Errorf("couldn't create master extended key: %v", err)
   633  	}
   634  
   635  	expectedAddresses := []struct {
   636  		address string
   637  		pubKey  string
   638  		privKey string
   639  	}{
   640  		{
   641  			address: "0x9c32F71D4DB8Fb9e1A58B0a80dF79935e7256FA6",
   642  			pubKey:  "0x03986dee3b8afe24cb8ccb2ac23dac3f8c43d22850d14b809b26d6b8aa5a1f4778",
   643  			privKey: "0x62f1d86b246c81bdd8f6c166d56896a4a5e1eddbcaebe06480e5c0bc74c28224",
   644  		},
   645  		{
   646  			address: "0x7AF7283bd1462C3b957e8FAc28Dc19cBbF2FAdfe",
   647  			pubKey:  "0x03462e7b95dab24fe8a57ac897d9026545ec4327c9c5e4a772e5d14cc5422f9489",
   648  			privKey: "0x49ee230b1605382ac1c40079191bca937fc30e8c2fa845b7de27a96ffcc4ddbf",
   649  		},
   650  		{
   651  			address: "0x05f48E30fCb69ADcd2A591Ebc7123be8BE72D7a1",
   652  			pubKey:  "0x036650e4b2b8e731a0ef12cda892b70cb95e78ea6e576ba995019b5e9aa7d9c0f5",
   653  			privKey: "0xeef2c0702151930b84cffcaa642af58e692956314519114e78f3211a6465f28b",
   654  		},
   655  		{
   656  			address: "0xbfE91Bc05cE66013660D7Eb742F74BD324DA5F92",
   657  			pubKey:  "0x0201d1c12e8fcea03a68ad5fd0d02fd0a4bfe0339618f949e2e30cf311e8b83c46",
   658  			privKey: "0xbca51d1d3529a0e0787933a2293cf46d9b973ea3ea00e28d3bd33590bc7f7156",
   659  		},
   660  	}
   661  
   662  	for i := 0; i < len(expectedAddresses); i++ {
   663  		key, err := rootKey.BIP44Child(CoinTypeETH, uint32(i))
   664  		if err != nil {
   665  			t.Errorf("Error deriving BIP44-compliant key: %s", err)
   666  		}
   667  
   668  		privateKeyECDSA := key.ToECDSA()
   669  		address := crypto.PubkeyToAddress(privateKeyECDSA.PublicKey).Hex()
   670  
   671  		if address != expectedAddresses[i].address {
   672  			t.Errorf("wrong address generated. expected %s, got %s", expectedAddresses[i].address, address)
   673  		}
   674  
   675  		pubKey := fmt.Sprintf("0x%x", (crypto.CompressPubkey(&privateKeyECDSA.PublicKey)))
   676  		if pubKey != expectedAddresses[i].pubKey {
   677  			t.Errorf("wrong public key generated. expected %s, got %s", expectedAddresses[i].pubKey, pubKey)
   678  		}
   679  
   680  		privKey := fmt.Sprintf("0x%x", crypto.FromECDSA(privateKeyECDSA))
   681  		if privKey != expectedAddresses[i].privKey {
   682  			t.Errorf("wrong private key generated. expected %s, got %s", expectedAddresses[i].privKey, privKey)
   683  		}
   684  	}
   685  }
   686  
   687  // TestPrivateKeyDataWithLeadingZeros is a regression test that checks
   688  // we don't re-introduce a bug we had in the past.
   689  // For a specific mnemonic phrase, we were deriving a wrong key/address
   690  // at path m/44'/60'/0'/0/0 compared to other wallets.
   691  // In this specific case, the second child key is represented in 31 bytes.
   692  // The problem raises when deriving its child key.
   693  // One of the step to derive the child key is calling our splitHMAC
   694  // that returns a secretKey and a chainCode.
   695  // Inside this function we make a sha512 of a seed that is a 37 bytes with:
   696  // 1 byte with 0x00
   697  // 32 bytes for the key data
   698  // 4 bytes for the child key index
   699  // In our case, if the key was less then 32 bytes, it was shifted to the left of that 32 bytes space,
   700  // resulting in a different seed, and a different data returned from the sha512 call.
   701  // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846#.86inuifuq
   702  // https://github.com/iancoleman/bip39/issues/58
   703  func TestPrivateKeyDataWithLeadingZeros(t *testing.T) {
   704  	mn := NewMnemonic()
   705  	words := "radar blur cabbage chef fix engine embark joy scheme fiction master release"
   706  	key, _ := NewMaster(mn.MnemonicSeed(words, ""))
   707  
   708  	path := []uint32{
   709  		HardenedKeyStart + 44, // purpose
   710  		HardenedKeyStart + 60, // cointype
   711  		HardenedKeyStart + 0,  // account
   712  		0,                     // change
   713  		0,                     // index
   714  	}
   715  
   716  	for _, part := range path {
   717  		key, _ = key.Child(part)
   718  		if length := len(key.KeyData); length != 32 {
   719  			t.Errorf("expected key length to be 32, got: %d", length)
   720  		}
   721  	}
   722  
   723  	expectedAddress := "0xaC39b311DCEb2A4b2f5d8461c1cdaF756F4F7Ae9"
   724  	address := crypto.PubkeyToAddress(key.ToECDSA().PublicKey).Hex()
   725  
   726  	if address != expectedAddress {
   727  		t.Errorf("expected address %s, got: %s", expectedAddress, address)
   728  	}
   729  }
   730  
   731  //func TestNewKey(t *testing.T) {
   732  //	mnemonic := NewMnemonic()
   733  //
   734  //	phrase, err := mnemonic.MnemonicPhrase(128, EnglishLanguage)
   735  //	if err != nil {
   736  //		t.Errorf("Test failed: could not create seed: %s", err)
   737  //	}
   738  //
   739  //	password := "badpassword"
   740  //	mnemonic.salt = "Bitcoin seed"
   741  //	key, err := NewMaster(mnemonic.MnemonicSeed(phrase, password))
   742  //	if err != nil {
   743  //		t.Error(err)
   744  //	}
   745  //	t.Logf("%x", key.KeyData)
   746  //}