@@ -2,9 +2,11 @@ package hdwallet
22
33import (
44 "encoding/hex"
5+ "fmt"
56 "testing"
67
78 "github.com/stretchr/testify/assert"
9+ "github.com/stretchr/testify/require"
810)
911
1012// https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
@@ -345,3 +347,159 @@ func TestPaddedPublicKey(t *testing.T) {
345347 t .Errorf ("Unexpected address. Expected 0x2CDfa87C022744CceABC525FaA8e85Df6984A60d and Got %s" , key .Addresses [1 ].ETHAddress )
346348 }
347349}
350+
351+ func TestDerivationPath (t * testing.T ) {
352+ type testCase struct {
353+ derivationPathInput string
354+ nAddresses int
355+ expectedSetPathError * error
356+ expectedAddresses map [string ]string
357+ }
358+
359+ const mnemonic = "test test test test test test test test test test test junk"
360+ const password = ""
361+ doesntMakeSenseErrFunc := func (path string ) * error {
362+ err := fmt .Errorf ("the path %s doesn't seem to make sense" , path )
363+ return & err
364+ }
365+
366+ testCases := []testCase {
367+ // no path derivation
368+ {"" , 1 , nil , map [string ]string {
369+ "m/44'/60'/0'" : "0x340d8879778d3D3Fec643D1736ebFd2bC5824662" ,
370+ }},
371+ {"" , 3 , nil , map [string ]string {
372+ "m/44'/60'/0'/0/0" : "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" ,
373+ "m/44'/60'/0'/0/1" : "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" ,
374+ "m/44'/60'/0'/0/2" : "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" ,
375+ }},
376+
377+ // path derivation input with 1 part
378+ {"m" , 1 , doesntMakeSenseErrFunc ("m" ), nil },
379+ {"m" , 3 , doesntMakeSenseErrFunc ("m" ), nil },
380+
381+ // path derivation input with 2 parts
382+ {"m/44'" , 1 , nil , map [string ]string {
383+ "m/44'" : "0xBe0B49bD63bea56C4c18733ad9C8A41B7161318F" ,
384+ }},
385+ {"m/44'" , 3 , nil , map [string ]string {
386+ "m/44'/60'/0'/0/0" : "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" ,
387+ "m/44'/60'/0'/0/1" : "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" ,
388+ "m/44'/60'/0'/0/2" : "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" ,
389+ }},
390+
391+ // path derivation input with 3 parts
392+ {"m/44'/60'" , 1 , nil , map [string ]string {
393+ "m/44'/60'" : "0x27439E87140CF69e87c89bB4C9776eAaD35BeFb3" ,
394+ }},
395+ {"m/44'/60'" , 3 , nil , map [string ]string {
396+ "m/44'/60'/0'/0/0" : "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" ,
397+ "m/44'/60'/0'/0/1" : "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" ,
398+ "m/44'/60'/0'/0/2" : "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" ,
399+ }},
400+
401+ // path derivation input with 4 parts
402+ {"m/44'/60'/0'" , 1 , nil , map [string ]string {
403+ "m/44'/60'/0'" : "0x340d8879778d3D3Fec643D1736ebFd2bC5824662" ,
404+ }},
405+ {"m/44'/60'/0" , 3 , nil , map [string ]string {
406+ "m/44'/60'/0'/0/0" : "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" ,
407+ "m/44'/60'/0'/0/1" : "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" ,
408+ "m/44'/60'/0'/0/2" : "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" ,
409+ }},
410+
411+ {"m/44'/60'/1" , 1 , nil , map [string ]string {
412+ "m/44'/60'/1" : "0x5600C4Cda24214FAFB227703437a3C98751C3f4F" ,
413+ }},
414+ {"m/44'/60'/1" , 3 , nil , map [string ]string {
415+ "m/44'/60'/1'/0/0" : "0x8C8d35429F74ec245F8Ef2f4Fd1e551cFF97d650" ,
416+ "m/44'/60'/1'/0/1" : "0x40FBBE484b8Ee6139Af08446950B088e10b2306A" ,
417+ "m/44'/60'/1'/0/2" : "0x2b382887D362cCae885a421C978c7e998D3c95a6" ,
418+ }},
419+
420+ // path derivation input with 5 parts
421+ {"m/44'/60'/0'/0" , 1 , nil , map [string ]string {
422+ "m/44'/60'/0'/0" : "0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C" ,
423+ }},
424+ {"m/44'/60'/0'/0" , 3 , nil , map [string ]string {
425+ "m/44'/60'/0'/0/0" : "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" ,
426+ "m/44'/60'/0'/0/1" : "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" ,
427+ "m/44'/60'/0'/0/2" : "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" ,
428+ }},
429+
430+ {"m/44'/60'/1'/2" , 1 , nil , map [string ]string {
431+ "m/44'/60'/1'/2" : "0xDd74C01e87759Ca5787C0A166103Df20a9493836" ,
432+ }},
433+ {"m/44'/60'/1'/2" , 3 , nil , map [string ]string {
434+ "m/44'/60'/1'/2/0" : "0x481Ea61d7635E00e32fd5BbA05E8eFe3855b0146" ,
435+ "m/44'/60'/1'/2/1" : "0x14954c8606365f18013BCE3Af14ff4431766B3Aa" ,
436+ "m/44'/60'/1'/2/2" : "0xC4094cD7436447541Fe5Dfe72023BBFE86799571" ,
437+ }},
438+
439+ // path derivation input with 6 parts
440+ {"m/44'/60'/0'/0/0" , 1 , nil , map [string ]string {
441+ "m/44'/60'/0'/0/0" : "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" ,
442+ }},
443+ {"m/44'/60'/0'/0/0" , 3 , nil , map [string ]string {
444+ "m/44'/60'/0'/0/0" : "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" ,
445+ "m/44'/60'/0'/0/1" : "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" ,
446+ "m/44'/60'/0'/0/2" : "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" ,
447+ }},
448+
449+ {"m/44'/60'/1'/2/3" , 1 , nil , map [string ]string {
450+ "m/44'/60'/1'/2/3" : "0xC0698b4Bf4Bf25219BFF2ef077e9979DE5263b60" ,
451+ }},
452+ {"m/44'/60'/1'/2/3" , 3 , nil , map [string ]string {
453+ "m/44'/60'/1'/2/3" : "0xC0698b4Bf4Bf25219BFF2ef077e9979DE5263b60" ,
454+ "m/44'/60'/1'/2/4" : "0x6B41E428F4C5a582666533B02af618291d4de347" ,
455+ "m/44'/60'/1'/2/5" : "0x68331EB6CE792DF5c6cE927366Cb6dE41CFff51b" ,
456+ }},
457+
458+ // custom derivation
459+ {"m/44'/60'/1'/2/3/4/5/6/7/8/9/0" , 1 , nil , map [string ]string {
460+ "m/44'/60'/1'/2/3/4/5/6/7/8/9/0" : "0x510F2Da3BAc8Bfbf5D1b07852f48FbA3d89aFf8a" ,
461+ }},
462+ {"m/44'/60'/1'/2/3/4/5/6/7/8/9/0" , 3 , nil , map [string ]string {
463+ "m/44'/60'/1'/2/3/4/5/6/7/8/9/0" : "0x510F2Da3BAc8Bfbf5D1b07852f48FbA3d89aFf8a" ,
464+ "m/44'/60'/1'/2/3/4/5/6/7/8/9/1" : "0xB3ce368159A8a60d0a71CA7161aBCa9b40fa68f4" ,
465+ "m/44'/60'/1'/2/3/4/5/6/7/8/9/2" : "0x70DB395c0e92F3f32B6698174bBE355451Bde07A" ,
466+ }},
467+
468+ // op
469+ {"m/44'/60'/2'/470/10" , 1 , nil , map [string ]string {
470+ "m/44'/60'/2'/470/10" : "0x86487B98fB4BeC557dEa441C06A3c4a7feCe152F" ,
471+ }},
472+ {"m/44'/60'/2'/470/10" , 3 , nil , map [string ]string {
473+ "m/44'/60'/2'/470/10" : "0x86487B98fB4BeC557dEa441C06A3c4a7feCe152F" ,
474+ "m/44'/60'/2'/470/11" : "0x2E29b5BD52b1D1D387c8dB9721Db93E8C210654E" ,
475+ "m/44'/60'/2'/470/12" : "0x824FBFCb5F4B5dC2D01533f03C0c815a2F8Bcb03" ,
476+ }},
477+ }
478+
479+ for _ , tc := range testCases {
480+ tcName := fmt .Sprintf ("Input: \" %s\" nAddresses: %d" , tc .derivationPathInput , tc .nAddresses )
481+ t .Run (tcName , func (t * testing.T ) {
482+ pw , err := NewPolyWallet (mnemonic , password )
483+ require .NoError (t , err )
484+
485+ if len (tc .derivationPathInput ) > 0 {
486+ err = pw .SetPath (tc .derivationPathInput )
487+ if tc .expectedSetPathError != nil {
488+ require .Error (t , err )
489+ assert .Equal (t , * tc .expectedSetPathError , err )
490+ return
491+ }
492+ require .NoError (t , err )
493+ }
494+
495+ hdAddresses , err := pw .ExportHDAddresses (tc .nAddresses )
496+ require .NoError (t , err )
497+ assert .Len (t , hdAddresses .Addresses , tc .nAddresses )
498+
499+ for _ , addr := range hdAddresses .Addresses {
500+ assert .Contains (t , tc .expectedAddresses , addr .Path )
501+ assert .Equal (t , tc .expectedAddresses [addr .Path ], addr .ETHAddress )
502+ }
503+ })
504+ }
505+ }
0 commit comments