Skip to content

Commit

Permalink
Merge pull request #3 from galt-tr/hdkeyHelpers
Browse files Browse the repository at this point in the history
Add basic helper functions for hd_key.go
  • Loading branch information
mrz1836 authored Oct 6, 2020
2 parents d562c4d + fa889df commit 4b5120f
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 2 deletions.
40 changes: 40 additions & 0 deletions hd_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/bitcoinsv/bsvd/bsvec"
"github.com/bitcoinsv/bsvd/chaincfg"
"github.com/bitcoinsv/bsvutil"
"github.com/bitcoinsv/bsvutil/hdkeychain"
)

Expand Down Expand Up @@ -34,6 +35,12 @@ func GenerateHDKey(seedLength uint8) (hdKey *hdkeychain.ExtendedKey, err error)
return hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)
}

// GenerateHDKeyFromString will create a new master node for use in creating a
// hierarchical deterministic key chain from an xPrivKey string
func GenerateHDKeyFromString(xpriv string) (hdKey *hdkeychain.ExtendedKey, err error) {
return hdkeychain.NewKeyFromString(xpriv)
}

// GenerateHDKeyPair will generate a new xPub HD master node (xPrivateKey & xPublicKey)
func GenerateHDKeyPair(seedLength uint8) (xPrivateKey, xPublicKey string, err error) {

Expand Down Expand Up @@ -77,6 +84,17 @@ func GetHDKeyByPath(hdKey *hdkeychain.ExtendedKey, chain, num uint32) (*hdkeycha
return childKeyChain.Child(num)
}

// GetHDKeyChild gets the child hd key for a given num
// Note: For a hardened child, start at 0x80000000. (For reference, 0x8000000 = 0').
func GetHDKeyChild(hdKey *hdkeychain.ExtendedKey, num uint32) (*hdkeychain.ExtendedKey, error) {
// Make sure we have a valid key
if hdKey == nil {
return nil, errors.New("hdKey is nil")
}
// Get child key from the num path
return hdKey.Child(num)
}

// GetPrivateKeyByPath gets the key for a given derivation path (chain/num)
func GetPrivateKeyByPath(hdKey *hdkeychain.ExtendedKey, chain, num uint32) (*bsvec.PrivateKey, error) {

Expand All @@ -89,3 +107,25 @@ func GetPrivateKeyByPath(hdKey *hdkeychain.ExtendedKey, chain, num uint32) (*bsv
// Get the private key
return childKeyNum.ECPrivKey()
}

// GetPrivateKeyFromHDKey - helper function to get the Private Key associated
// with a given hdKey
func GetPrivateKeyFromHDKey(hdKey *hdkeychain.ExtendedKey) (*bsvec.PrivateKey, error) {
return hdKey.ECPrivKey()
}

// GetPublicKeyFromHDKey - helper function to get the Public Key associated
// with a given hdKey
func GetPublicKeyFromHDKey(hdKey *hdkeychain.ExtendedKey) (*bsvec.PublicKey, error) {
return hdKey.ECPubKey()
}

// GetAddressFromHDKey - helper function to get the Public Key associated with
// a given hdKey
func GetAddressFromHDKey(hdKey *hdkeychain.ExtendedKey) (*bsvutil.LegacyAddressPubKeyHash, error) {
pubKey, err := GetPublicKeyFromHDKey(hdKey)
if err != nil {
return nil, err
}
return AddressFromPubKey(pubKey)
}
102 changes: 100 additions & 2 deletions hd_key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ func TestGetHDKeyByPath(t *testing.T) {
}
}

// ExampleGetPrivateKeyByPath example using GetHDKeyByPath()
// ExampleGetHDKeyByPath example using GetHDKeyByPath()
func ExampleGetHDKeyByPath() {

hdKey, err := GenerateHDKey(SecureSeedLength)
Expand All @@ -290,10 +290,108 @@ func ExampleGetHDKeyByPath() {
// Output:hd key (111) found at path 0/1
}

// BenchmarkGetPrivateKeyByPath benchmarks the method GetHDKeyByPath()
// BenchmarkGetHDKeyByPath benchmarks the method GetHDKeyByPath()
func BenchmarkGetHDKeyByPath(b *testing.B) {
hdKey, _ := GenerateHDKey(SecureSeedLength)
for i := 0; i < b.N; i++ {
_, _ = GetHDKeyByPath(hdKey, 0, 1)
}
}

// TestGetHDKeyByPath will test the method GetHDKeyByPath()
func TestGetHDChild(t *testing.T) {

t.Parallel()

// Generate a valid key
validKey, err := GenerateHDKey(RecommendedSeedLength)
if err != nil {
t.Fatalf("error occurred: %s", err.Error())
}

// Max depth key
var maxKey *hdkeychain.ExtendedKey
maxKey, err = GetHDKeyByPath(validKey, 1<<9, 1<<9)
if err != nil {
t.Fatalf("error occurred: %s", err.Error())
}

// Test depth limit
// todo: make a better test (after 126 maxKey is now nil)
for i := 0; i < 1<<8-1; i++ {
maxKey, err = GetHDKeyChild(maxKey, uint32(i))
if i < 126 && err != nil {
t.Fatalf("error occurred: %s", err.Error())
}
// TODO: make this better rather than grabbing the child twice. This is
// basically a copy of the GetHDKeyByPath test
maxKey, err = GetHDKeyChild(maxKey, uint32(i))
if i >= 126 && err == nil {
t.Fatalf("expected to hit depth limit on HD key index: %d", i)
}
}

// Create the list of tests
var tests = []struct {
inputHDKey *hdkeychain.ExtendedKey
inputNum uint32
expectedNil bool
expectedError bool
}{
{nil, 0, true, true},
{validKey, 0, false, false},
{validKey, 10, false, false},
{validKey, 100, false, false},
{validKey, 2 ^ 31 + 1, false, false},
{validKey, 1 << 8, false, false},
{validKey, 1 << 9, false, false},
{validKey, 1 << 10, false, false},
{validKey, 1 << 11, false, false},
{validKey, 1 << 12, false, false},
{validKey, 1 << 16, false, false},
{validKey, 1<<32 - 1, false, false},
}

// Run tests
for _, test := range tests {
if hdKey, err := GetHDKeyChild(test.inputHDKey, test.inputNum); err != nil && !test.expectedError {
t.Errorf("%s Failed: [%v] [%d] inputted and error not expected but got: %s", t.Name(), test.inputHDKey, test.inputNum, err.Error())
} else if err == nil && test.expectedError {
t.Errorf("%s Failed: [%v] [%d] inputted and error was expected", t.Name(), test.inputHDKey, test.inputNum)
} else if hdKey == nil && !test.expectedNil {
t.Errorf("%s Failed: [%v] [%d] inputted and was nil but not expected", t.Name(), test.inputHDKey, test.inputNum)
} else if hdKey != nil && test.expectedNil {
t.Errorf("%s Failed: [%v] [%d] inputted and was NOT nil but expected to be nil", t.Name(), test.inputHDKey, test.inputNum)
} else if hdKey != nil && len(hdKey.String()) == 0 {
t.Errorf("%s Failed: [%v] [%d] inputted and should not be empty", t.Name(), test.inputHDKey, test.inputNum)
}
}
}

// ExampleGetHDKeyChild example using GetHDKeyChild()
func ExampleGetHDKeyChild() {

hdKey, err := GenerateHDKey(SecureSeedLength)
if err != nil {
fmt.Printf("error occurred: %s", err.Error())
return
}

// Get a child key
var childKey *hdkeychain.ExtendedKey
childKey, err = GetHDKeyChild(hdKey, 0)
if err != nil {
fmt.Printf("error occurred: %s", err.Error())
return
}
fmt.Printf("hd key (%d) found at path %d", len(childKey.String()), 0)
// Output:hd key (111) found at path 0
}

// BenchmarkGetHDKeyChild benchmarks the method GetHDKeyChild()
func BenchmarkGetHDKeyChild(b *testing.B) {
hdKey, _ := GenerateHDKey(SecureSeedLength)
for i := 0; i < b.N; i++ {
_, _ = GetHDKeyChild(hdKey, 0)
}
}

0 comments on commit 4b5120f

Please sign in to comment.