Skip to content

Commit 09455a6

Browse files
committed
Merge pull request #248 from dcousens/hdnode
HDNode / BIP32 Neutering
2 parents 5888ca5 + 6b42949 commit 09455a6

File tree

2 files changed

+58
-22
lines changed

2 files changed

+58
-22
lines changed

src/hdnode.js

+18-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ function HDNode(K, chainCode, network) {
3131
network = network || networks.bitcoin
3232

3333
assert(Buffer.isBuffer(chainCode), 'Expected Buffer, got ' + chainCode)
34+
assert.equal(chainCode.length, 32, 'Expected chainCode length of 32, got ' + chainCode.length)
3435
assert(network.bip32, 'Unknown BIP32 constants for network')
3536

3637
this.chainCode = chainCode
@@ -142,12 +143,27 @@ HDNode.prototype.getAddress = function() {
142143
return this.pubKey.getAddress(this.network)
143144
}
144145

146+
HDNode.prototype.neutered = function() {
147+
var neutered = new HDNode(this.pubKey.Q, this.chainCode, this.network)
148+
neutered.depth = this.depth
149+
neutered.index = this.index
150+
neutered.parentFingerprint = this.parentFingerprint
151+
152+
return neutered
153+
}
154+
145155
HDNode.prototype.toBase58 = function(isPrivate) {
146156
return base58check.encode(this.toBuffer(isPrivate))
147157
}
148158

149159
HDNode.prototype.toBuffer = function(isPrivate) {
150-
if (isPrivate == undefined) isPrivate = !!this.privKey
160+
if (isPrivate == undefined) {
161+
isPrivate = !!this.privKey
162+
163+
// FIXME: remove in 2.x.y
164+
} else {
165+
console.warn('isPrivate flag is deprecated, please use the .neutered() method instead')
166+
}
151167

152168
// Version
153169
var version = isPrivate ? this.network.bip32.private : this.network.bip32.public
@@ -173,6 +189,7 @@ HDNode.prototype.toBuffer = function(isPrivate) {
173189

174190
// 33 bytes: the public key or private key data
175191
if (isPrivate) {
192+
// FIXME: remove in 2.x.y
176193
assert(this.privKey, 'Missing private key')
177194

178195
// 0x00 + k for private keys

test/hdnode.js

+40-21
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,13 @@ describe('HDNode', function() {
4949
assert.equal(hd.network, networks.testnet)
5050
})
5151

52-
it('throws an exception when an unknown network is given', function() {
52+
it('throws when an invalid length chain code is given', function() {
53+
assert.throws(function() {
54+
new HDNode(d, chainCode.slice(0, 20), networks.testnet)
55+
}, /Expected chainCode length of 32, got 20/)
56+
})
57+
58+
it('throws when an unknown network is given', function() {
5359
assert.throws(function() {
5460
new HDNode(d, chainCode, {})
5561
}, /Unknown BIP32 constants for network/)
@@ -82,20 +88,21 @@ describe('HDNode', function() {
8288
describe('toBase58', function() {
8389
fixtures.valid.forEach(function(f) {
8490
it('exports ' + f.master.base58 + ' (public) correctly', function() {
85-
var hd = HDNode.fromSeedHex(f.master.seed)
91+
var hd = HDNode.fromSeedHex(f.master.seed).neutered()
8692

87-
assert.equal(hd.toBase58(false), f.master.base58)
93+
assert.equal(hd.toBase58(), f.master.base58)
8894
})
8995
})
9096

9197
fixtures.valid.forEach(function(f) {
9298
it('exports ' + f.master.base58Priv + ' (private) correctly', function() {
9399
var hd = HDNode.fromSeedHex(f.master.seed)
94100

95-
assert.equal(hd.toBase58(true), f.master.base58Priv)
101+
assert.equal(hd.toBase58(), f.master.base58Priv)
96102
})
97103
})
98104

105+
// FIXME: remove in 2.x.y
99106
it('fails when there is no private key', function() {
100107
var hd = HDNode.fromBase58(fixtures.valid[0].master.base58)
101108

@@ -160,20 +167,21 @@ describe('HDNode', function() {
160167
describe('toBuffer/toHex', function() {
161168
fixtures.valid.forEach(function(f) {
162169
it('exports ' + f.master.hex + ' (public) correctly', function() {
163-
var hd = HDNode.fromSeedHex(f.master.seed)
170+
var hd = HDNode.fromSeedHex(f.master.seed).neutered()
164171

165-
assert.equal(hd.toHex(false), f.master.hex)
172+
assert.equal(hd.toHex(), f.master.hex)
166173
})
167174
})
168175

169176
fixtures.valid.forEach(function(f) {
170177
it('exports ' + f.master.hexPriv + ' (private) correctly', function() {
171178
var hd = HDNode.fromSeedHex(f.master.seed)
172179

173-
assert.equal(hd.toHex(true), f.master.hexPriv)
180+
assert.equal(hd.toHex(), f.master.hexPriv)
174181
})
175182
})
176183

184+
// FIXME: remove in 2.x.y
177185
it('fails when there is no private key', function() {
178186
var hd = HDNode.fromHex(fixtures.valid[0].master.hex)
179187

@@ -220,6 +228,21 @@ describe('HDNode', function() {
220228
})
221229
})
222230

231+
describe('neutered', function() {
232+
var f = fixtures.valid[0]
233+
234+
it('strips all private information', function() {
235+
var hd = HDNode.fromBase58(f.master.base58)
236+
var hdn = hd.neutered()
237+
238+
assert.equal(hdn.privKey, undefined)
239+
assert.equal(hdn.pubKey.toHex(), hd.pubKey.toHex())
240+
assert.equal(hdn.chainCode, hd.chainCode)
241+
assert.equal(hdn.depth, hd.depth)
242+
assert.equal(hdn.index, hd.index)
243+
})
244+
})
245+
223246
describe('derive', function() {
224247
function verifyVector(hd, v, depth) {
225248
assert.equal(hd.privKey.toWIF(), v.wif)
@@ -256,32 +279,28 @@ describe('HDNode', function() {
256279
var f = fixtures.valid[1]
257280
var c = f.children[0]
258281

259-
var parentNode = HDNode.fromBase58(f.master.base58Priv)
260-
var child = parentNode.derive(c.m)
282+
var master = HDNode.fromBase58(f.master.base58Priv)
283+
var child = master.derive(c.m).neutered()
261284

262-
// FIXME: N(CKDpriv((kpar, cpar), i)), could be done better...
263-
var childNeutered = HDNode.fromBase58(child.toBase58(false)) // neuter
264-
assert.equal(childNeutered.toBase58(), c.base58)
285+
assert.equal(child.toBase58(), c.base58)
265286
})
266287

267288
it('works for Private -> public (neutered, hardened)', function() {
268289
var f = fixtures.valid[0]
269290
var c = f.children[0]
270291

271-
var parentNode = HDNode.fromBase58(f.master.base58Priv)
272-
var child = parentNode.deriveHardened(c.m)
292+
var master = HDNode.fromBase58(f.master.base58Priv)
293+
var child = master.deriveHardened(c.m).neutered()
273294

274-
// FIXME: N(CKDpriv((kpar, cpar), i)), could be done better...
275-
var childNeutered = HDNode.fromBase58(child.toBase58(false)) // neuter
276-
assert.equal(childNeutered.toBase58(), c.base58)
295+
assert.equal(child.toBase58(), c.base58)
277296
})
278297

279298
it('works for Public -> public', function() {
280299
var f = fixtures.valid[1]
281300
var c = f.children[0]
282301

283-
var parentNode = HDNode.fromBase58(f.master.base58)
284-
var child = parentNode.derive(c.m)
302+
var master = HDNode.fromBase58(f.master.base58)
303+
var child = master.derive(c.m)
285304

286305
assert.equal(child.toBase58(), c.base58)
287306
})
@@ -290,10 +309,10 @@ describe('HDNode', function() {
290309
var f = fixtures.valid[0]
291310
var c = f.children[0]
292311

293-
var parentNode = HDNode.fromBase58(f.master.base58)
312+
var master = HDNode.fromBase58(f.master.base58)
294313

295314
assert.throws(function() {
296-
parentNode.deriveHardened(c.m)
315+
master.deriveHardened(c.m)
297316
}, /Could not derive hardened child key/)
298317
})
299318
})

0 commit comments

Comments
 (0)