Skip to content

Commit 74b748e

Browse files
authored
Merge pull request #179 from starius/xpub-derive
derivekey: support xpubs in addition to xprvs
2 parents b352ed9 + 6e4a076 commit 74b748e

File tree

3 files changed

+69
-2
lines changed

3 files changed

+69
-2
lines changed

cmd/chantools/derivekey.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ func deriveKey(extendedKey *hdkeychain.ExtendedKey, path string,
107107
}
108108

109109
privKey, xPriv := na, na
110-
if !neuter {
110+
if !neuter && wif != nil {
111111
privKey, xPriv = wif.String(), child.String()
112112
}
113113

cmd/chantools/derivekey_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,64 @@ func TestDeriveKeySeedBip39(t *testing.T) {
8282

8383
h.assertLogContains(keyContentBIP39)
8484
}
85+
86+
func TestDeriveKeyXprv(t *testing.T) {
87+
h := newHarness(t)
88+
89+
// Derive a specific key from xprv.
90+
derive := &deriveKeyCommand{
91+
Path: testPath,
92+
rootKey: &rootKey{
93+
RootKey: "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nR" +
94+
"k4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejM" +
95+
"RNNU3TGtRBeJgk33yuGBxrMPHi",
96+
},
97+
}
98+
99+
err := derive.Execute(nil, nil)
100+
require.NoError(t, err)
101+
102+
h.assertLogContains("cQcdieZy2d1TAdCsa5MjmHJs2gdHcD7x22nDbhJyVTUa3Ax" +
103+
"5KB3w")
104+
}
105+
106+
func TestDeriveKeyXpub(t *testing.T) {
107+
h := newHarness(t)
108+
109+
// Derive a specific key from xpub.
110+
derive := &deriveKeyCommand{
111+
Path: "m/5/6",
112+
rootKey: &rootKey{
113+
RootKey: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap" +
114+
"9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqse" +
115+
"fD265TMg7usUDFdp6W1EGMcet8",
116+
},
117+
Neuter: true,
118+
}
119+
120+
err := derive.Execute(nil, nil)
121+
require.NoError(t, err)
122+
123+
h.assertLogContains("03dc8655d58bd4fd4326863fe34bd5cdddbefaa3b042571" +
124+
"05eb1ab99aa05e01c2a")
125+
}
126+
127+
func TestDeriveKeyXpubNoNeuter(t *testing.T) {
128+
h := newHarness(t)
129+
130+
// Derive a specific key from xpub.
131+
derive := &deriveKeyCommand{
132+
Path: "m/5/6",
133+
rootKey: &rootKey{
134+
RootKey: "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap" +
135+
"9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqse" +
136+
"fD265TMg7usUDFdp6W1EGMcet8",
137+
},
138+
}
139+
140+
err := derive.Execute(nil, nil)
141+
require.NoError(t, err)
142+
143+
h.assertLogContains("03dc8655d58bd4fd4326863fe34bd5cdddbefaa3b042571" +
144+
"05eb1ab99aa05e01c2a")
145+
}

lnd/hdkeychain.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ func HardenedKey(key uint32) uint32 {
110110
}
111111

112112
// DeriveKey derives the public key and private key in the WIF format for a
113-
// given key path of the extended key.
113+
// given key path from the extended key. If the extendedKey is an xpub, then
114+
// private key is not generated and the returned WIF will be nil.
114115
func DeriveKey(extendedKey *hdkeychain.ExtendedKey, path string,
115116
params *chaincfg.Params) (*hdkeychain.ExtendedKey, *btcec.PublicKey,
116117
*btcutil.WIF, error) {
@@ -131,6 +132,11 @@ func DeriveKey(extendedKey *hdkeychain.ExtendedKey, path string,
131132
"key: %w", err)
132133
}
133134

135+
// If the extended key is xpub, we can't generate the private key.
136+
if !derivedKey.IsPrivate() {
137+
return derivedKey, pubKey, nil, nil
138+
}
139+
134140
privKey, err := derivedKey.ECPrivKey()
135141
if err != nil {
136142
return nil, nil, nil, fmt.Errorf("could not derive private "+

0 commit comments

Comments
 (0)