Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion src/hdnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ function HDNode(K, chainCode, network) {
network = network || networks.bitcoin

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

this.chainCode = chainCode
Expand Down Expand Up @@ -142,12 +143,27 @@ HDNode.prototype.getAddress = function() {
return this.pubKey.getAddress(this.network)
}

HDNode.prototype.neutered = function() {
var neutered = new HDNode(this.pubKey.Q, this.chainCode, this.network)
neutered.depth = this.depth
neutered.index = this.index
neutered.parentFingerprint = this.parentFingerprint

return neutered
}

HDNode.prototype.toBase58 = function(isPrivate) {
return base58check.encode(this.toBuffer(isPrivate))
}

HDNode.prototype.toBuffer = function(isPrivate) {
if (isPrivate == undefined) isPrivate = !!this.privKey
if (isPrivate == undefined) {
isPrivate = !!this.privKey

// FIXME: remove in 2.x.y
} else {
console.warn('isPrivate flag is deprecated, please use the .neutered() method instead')
}

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

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

// 0x00 + k for private keys
Expand Down
61 changes: 40 additions & 21 deletions test/hdnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ describe('HDNode', function() {
assert.equal(hd.network, networks.testnet)
})

it('throws an exception when an unknown network is given', function() {
it('throws when an invalid length chain code is given', function() {
assert.throws(function() {
new HDNode(d, chainCode.slice(0, 20), networks.testnet)
}, /Expected chainCode length of 32, got 20/)
})

it('throws when an unknown network is given', function() {
assert.throws(function() {
new HDNode(d, chainCode, {})
}, /Unknown BIP32 constants for network/)
Expand Down Expand Up @@ -82,20 +88,21 @@ describe('HDNode', function() {
describe('toBase58', function() {
fixtures.valid.forEach(function(f) {
it('exports ' + f.master.base58 + ' (public) correctly', function() {
var hd = HDNode.fromSeedHex(f.master.seed)
var hd = HDNode.fromSeedHex(f.master.seed).neutered()

assert.equal(hd.toBase58(false), f.master.base58)
assert.equal(hd.toBase58(), f.master.base58)
})
})

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

assert.equal(hd.toBase58(true), f.master.base58Priv)
assert.equal(hd.toBase58(), f.master.base58Priv)
})
})

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

Expand Down Expand Up @@ -160,20 +167,21 @@ describe('HDNode', function() {
describe('toBuffer/toHex', function() {
fixtures.valid.forEach(function(f) {
it('exports ' + f.master.hex + ' (public) correctly', function() {
var hd = HDNode.fromSeedHex(f.master.seed)
var hd = HDNode.fromSeedHex(f.master.seed).neutered()

assert.equal(hd.toHex(false), f.master.hex)
assert.equal(hd.toHex(), f.master.hex)
})
})

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

assert.equal(hd.toHex(true), f.master.hexPriv)
assert.equal(hd.toHex(), f.master.hexPriv)
})
})

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

Expand Down Expand Up @@ -220,6 +228,21 @@ describe('HDNode', function() {
})
})

describe('neutered', function() {
var f = fixtures.valid[0]

it('strips all private information', function() {
var hd = HDNode.fromBase58(f.master.base58)
var hdn = hd.neutered()

assert.equal(hdn.privKey, undefined)
assert.equal(hdn.pubKey.toHex(), hd.pubKey.toHex())
assert.equal(hdn.chainCode, hd.chainCode)
assert.equal(hdn.depth, hd.depth)
assert.equal(hdn.index, hd.index)
})
})

describe('derive', function() {
function verifyVector(hd, v, depth) {
assert.equal(hd.privKey.toWIF(), v.wif)
Expand Down Expand Up @@ -256,32 +279,28 @@ describe('HDNode', function() {
var f = fixtures.valid[1]
var c = f.children[0]

var parentNode = HDNode.fromBase58(f.master.base58Priv)
var child = parentNode.derive(c.m)
var master = HDNode.fromBase58(f.master.base58Priv)
var child = master.derive(c.m).neutered()

// FIXME: N(CKDpriv((kpar, cpar), i)), could be done better...
var childNeutered = HDNode.fromBase58(child.toBase58(false)) // neuter
assert.equal(childNeutered.toBase58(), c.base58)
assert.equal(child.toBase58(), c.base58)
})

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

var parentNode = HDNode.fromBase58(f.master.base58Priv)
var child = parentNode.deriveHardened(c.m)
var master = HDNode.fromBase58(f.master.base58Priv)
var child = master.deriveHardened(c.m).neutered()

// FIXME: N(CKDpriv((kpar, cpar), i)), could be done better...
var childNeutered = HDNode.fromBase58(child.toBase58(false)) // neuter
assert.equal(childNeutered.toBase58(), c.base58)
assert.equal(child.toBase58(), c.base58)
})

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

var parentNode = HDNode.fromBase58(f.master.base58)
var child = parentNode.derive(c.m)
var master = HDNode.fromBase58(f.master.base58)
var child = master.derive(c.m)

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

var parentNode = HDNode.fromBase58(f.master.base58)
var master = HDNode.fromBase58(f.master.base58)

assert.throws(function() {
parentNode.deriveHardened(c.m)
master.deriveHardened(c.m)
}, /Could not derive hardened child key/)
})
})
Expand Down