diff --git a/go.mod b/go.mod index fb5741a..0d0c675 100644 --- a/go.mod +++ b/go.mod @@ -1,12 +1,12 @@ -module github.com/miguelmota/go-ethereum-hdwallet +module github.com/witoff/go-ethereum-hdwallet go 1.12 require ( github.com/allegro/bigcache v1.2.1 // indirect github.com/aristanetworks/goarista v0.0.0-20190912214011-b54698eaaca6 // indirect - github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 - github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d + github.com/btcsuite/btcd v0.21.0-beta + github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce github.com/davecgh/go-spew v1.1.1 github.com/edsrzf/mmap-go v1.0.0 // indirect github.com/elastic/gosigar v0.10.5 // indirect @@ -23,9 +23,8 @@ require ( github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect github.com/syndtr/goleveldb v0.0.0-20180621010148-0d5a0ceb10cf // indirect github.com/tyler-smith/go-bip39 v0.0.0-20180618194314-52158e4697b8 - golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 // indirect + golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 // indirect golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72 // indirect - golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 // indirect gopkg.in/fatih/set.v0 v0.2.1 // indirect gopkg.in/karalabe/cookiejar.v2 v2.0.0-20150724131613-8dcd6a7f4951 // indirect ) diff --git a/go.sum b/go.sum index 0d64abb..546ce90 100644 --- a/go.sum +++ b/go.sum @@ -22,14 +22,23 @@ github.com/btcsuite/btcd v0.0.0-20180531025944-86fed781132a h1:fuEu3WaOzatbWFGlC github.com/btcsuite/btcd v0.0.0-20180531025944-86fed781132a/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3 h1:A/EVblehb75cUgXA5njHPn0kLAsykn6mJGz7rnmW5W0= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M= +github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016 h1:BsZAJgCuMsoFZMZNyj7Lyt6sS8anDhedVrAMCOyPMIo= github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d h1:yJzD/yFppdVCf6ApMkVy8cUxV0XrxdP9rVf6D87/Mng= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= +github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -38,6 +47,7 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= @@ -85,6 +95,7 @@ github.com/jackpal/go-nat-pmp v1.0.1 h1:i0LektDkO1QlrTm/cSuP+PyBCDnYvjPLGl4LdWEM github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jcmturner/gofork v0.0.0-20190328161633-dc7c13fece03/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -108,6 +119,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc= @@ -167,7 +179,12 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= +golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -194,6 +211,9 @@ golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190912141932-bc967efca4b8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69 h1:rOhMmluY6kLMhdnrivzec6lLgaVbMHMn2ISQXJeJ5EM= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/hdwallet.go b/hdwallet.go index 9568677..0e73384 100644 --- a/hdwallet.go +++ b/hdwallet.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "math/big" + "os" "sync" "github.com/btcsuite/btcd/chaincfg" @@ -29,15 +30,18 @@ var DefaultRootDerivationPath = accounts.DefaultRootDerivationPath // at m/44'/60'/0'/1, etc var DefaultBaseDerivationPath = accounts.DefaultBaseDerivationPath +const issue179FixEnvar = "GO_ETHEREUM_HDWALLET_FIX_ISSUE_179" + // Wallet is the underlying wallet struct. type Wallet struct { - mnemonic string - masterKey *hdkeychain.ExtendedKey - seed []byte - url accounts.URL - paths map[common.Address]accounts.DerivationPath - accounts []accounts.Account - stateLock sync.RWMutex + mnemonic string + masterKey *hdkeychain.ExtendedKey + seed []byte + url accounts.URL + paths map[common.Address]accounts.DerivationPath + accounts []accounts.Account + stateLock sync.RWMutex + fixIssue172 bool } func newWallet(seed []byte) (*Wallet, error) { @@ -47,10 +51,11 @@ func newWallet(seed []byte) (*Wallet, error) { } return &Wallet{ - masterKey: masterKey, - seed: seed, - accounts: []accounts.Account{}, - paths: map[common.Address]accounts.DerivationPath{}, + masterKey: masterKey, + seed: seed, + accounts: []accounts.Account{}, + paths: map[common.Address]accounts.DerivationPath{}, + fixIssue172: false || len(os.Getenv(issue179FixEnvar)) > 0, }, nil } @@ -152,6 +157,14 @@ func (w *Wallet) Unpin(account accounts.Account) error { return errors.New("account not found") } +// SetFixIssue172 determines whether the standard (correct) bip39 +// derivation path was used, or if derivation should be affected by +// Issue172 [0] which was how this library was originally implemented. +// [0] https://github.com/btcsuite/btcutil/pull/182/files +func (w *Wallet) SetFixIssue172(fixIssue172 bool) { + w.fixIssue172 = fixIssue172 +} + // Derive implements accounts.Wallet, deriving a new account at the specific // derivation path. If pin is set to true, the account will be added to the list // of tracked accounts. @@ -491,7 +504,11 @@ func (w *Wallet) derivePrivateKey(path accounts.DerivationPath) (*ecdsa.PrivateK var err error key := w.masterKey for _, n := range path { - key, err = key.Child(n) + if w.fixIssue172 && key.IsAffectedByIssue172() { + key, err = key.Derive(n) + } else { + key, err = key.DeriveNonStandard(n) + } if err != nil { return nil, err } diff --git a/hdwallet_test.go b/hdwallet_test.go index b46129d..b98de59 100644 --- a/hdwallet_test.go +++ b/hdwallet_test.go @@ -2,6 +2,7 @@ package hdwallet import ( "math/big" + "os" "strings" "testing" @@ -13,6 +14,50 @@ import ( // TODO: table test +func TestIssue172(t *testing.T) { + mnemonic := "sound practice disease erupt basket pumpkin truck file gorilla behave find exchange napkin boy congress address city net prosper crop chair marine chase seven" + + getWallet := func() *Wallet { + wallet, err := NewFromMnemonic(mnemonic) + if err != nil { + t.Error(err) + } + return wallet + } + + path, err := ParseDerivationPath("m/44'/60'/0'/0/0") + if err != nil { + t.Error(err) + } + + // Reset Envars + os.Setenv(issue179FixEnvar, "") + + // Derive the old (wrong way) + account, err := getWallet().Derive(path, false) + + if account.Address.Hex() != "0x3943412CBEEEd4b68d73382b136F36b0CB82F481" { + t.Error("wrong address") + } + + // Set envar to non-zero length to derive correctly + os.Setenv(issue179FixEnvar, "1") + account, err = getWallet().Derive(path, false) + if account.Address.Hex() != "0x98e440675eFF3041D20bECb7fE7e81746A431b6d" { + t.Error("wrong address") + } + + // Reset Envars + os.Setenv(issue179FixEnvar, "") + wallet := getWallet() + wallet.SetFixIssue172(true) + account, err = wallet.Derive(path, false) + + if account.Address.Hex() != "0x98e440675eFF3041D20bECb7fE7e81746A431b6d" { + t.Error("wrong address") + } +} + func TestWallet(t *testing.T) { mnemonic := "tag volcano eight thank tide danger coast health above argue embrace heavy" wallet, err := NewFromMnemonic(mnemonic)