diff --git a/tests/make-tests/make-hdnode.js b/tests/make-tests/make-hdnode.js index 28fbdc2c..fdde04c3 100644 --- a/tests/make-tests/make-hdnode.js +++ b/tests/make-tests/make-hdnode.js @@ -32,6 +32,9 @@ function getHD(seed) { path: 'm', privateKey: '0x' + privateKey.toString('hex'), address: '0x' + ethereumUtil.privateToAddress(privateKey).toString('hex'), + parentFingerprint: rootNode.parentFingerprint, + xpriv: rootNode.toBase58(), + xpub: rootNode.neutered().toBase58(), }]; for (var j = 0; j < 5; j++) { @@ -42,6 +45,9 @@ function getHD(seed) { path: path, privateKey: '0x' + privateKey.toString('hex'), address: '0x' + ethereumUtil.privateToAddress(privateKey).toString('hex'), + parentFingerprint: node.parentFingerprint, + xpriv: node.toBase58(), + xpub: node.neutered().toBase58(), }); } @@ -96,6 +102,8 @@ Testcases['axic'] = { path: "m/44'/60'/0'/0/0", address: '0xac39b311dceb2a4b2f5d8461c1cdaf756f4f7ae9', privateKey: '0xb96e9ccb774cc33213cbcb2c69d3cdae17b0fe4888a1ccd343cbd1a17fd98b18', + xpriv: "xprvA2xEQ2iTe9QB22rvf5cbfpUxEBmMdvc7stEFxLhiMXmdLrwLbqugPCHRZiRfEq2puC5vTgwyFneV38hppF8oTf9aoaUv7M8u2XvnACTe6r4", + xpub: "xpub6FwaoYFMUWxUEWwPm79c2xRgnDbr3PKyF79rkj7KusJcDfGV9PDvvzbuQz32JYu3y2EpqY7xUag5Zw89YXokCKVtWLrfJ1RDUAYLLzTR8En" } ] } diff --git a/tests/test-hdnode.js b/tests/test-hdnode.js index cdd8f871..ab9d66e8 100644 --- a/tests/test-hdnode.js +++ b/tests/test-hdnode.js @@ -9,21 +9,76 @@ describe('Test HD Node Derivation', function(test) { var tests = utils.loadTests('hdnode'); tests.forEach(function(test) { - it('Derives the HD nodes - ' + test.name, function() { + it('Derives the HD nodes - ' + test.name, function() { this.timeout(10000); - var rootNode = new ethers.utils.HDNode.fromSeed(test.seed); - test.hdnodes.forEach(function(nodeTest) { + //var rootNode = new ethers.utils.HDNode.fromSeed(test.seed); + var rootNode = new ethers.utils.HDNode.fromMnemonic(test.mnemonic, null, test.password || null); + test.hdnodes.forEach(function(nodeTest) { var node = rootNode.derivePath(nodeTest.path); assert.equal(node.privateKey, nodeTest.privateKey, 'Generates privateKey - ' + nodeTest.privateKey); + assert.equal(node.extendedKey, nodeTest.xpriv, + "Child Extended privateKey - " + nodeTest.privateKey); + assert.equal(node.neuter().extendedKey, nodeTest.xpub, + "Child Extended privateKey - " + nodeTest.privateKey); + var wallet = new ethers.Wallet(node.privateKey); assert.equal(wallet.address.toLowerCase(), nodeTest.address, 'Generates address - ' + nodeTest.privateKey); - assert.equal(node.address, (new ethers.Wallet(node)).address, 'HDNode address matches - ' + nodeTest.privateKey); + assert.equal(node.address, (new ethers.Wallet(node)).address, + 'HDNode address matches - ' + nodeTest.privateKey); + + // Test public extended key derivation + let lastHardened = nodeTest.path.match(/^(.*)'([^']*)$/); + if (lastHardened && lastHardened[2].trim() !== "") { + + // Derive as far as we can for hardened, then derive the remaining from neutered + var hardNode = rootNode.derivePath(lastHardened[1] + "'"); + let neutered = hardNode.neuter(); + let nodeXpriv = ethers.utils.HDNode.fromExtendedKey(hardNode.extendedKey); + nodeXpriv = nodeXpriv.derivePath(lastHardened[2].substring(1)); + let nodeXpub = ethers.utils.HDNode.fromExtendedKey(neutered.extendedKey); + nodeXpub = nodeXpub.derivePath(lastHardened[2].substring(1)); + + assert.equal(neutered.privateKey, null, + 'Neutered HDNode privateKey null - ' + nodeTest.privateKey); + assert.equal(neutered.xpriv, null, + 'Neutered HDNode xpriv null - ' + nodeTest.privateKey); + + neutered = neutered.derivePath(lastHardened[2].substring(1)); + + assert.equal(neutered.address.toLowerCase(), nodeTest.address, + 'Derived Neutered HDNode address matches - ' + nodeTest.privateKey); + + assert.equal(neutered.xpub, node.xpub, + 'Derived Neutered HDNode xpub matches - ' + nodeTest.privateKey); + + assert.equal(neutered.privateKey, null, + 'Derived Neutered HDNode privateKey null - ' + nodeTest.privateKey); + assert.equal(neutered.xpriv, null, + 'Neutered HDNode xpriv null - ' + nodeTest.privateKey); + + // Test extended key derivation + assert.equal(nodeXpub.xpriv, null, + 'Serialized Neutered HDNode xpriv null - ' + nodeTest.privateKey); + assert.equal(nodeXpriv.extendedKey, node.extendedKey, + 'Serialized HDNode xpriv matches - ' + nodeTest.privateKey); + assert.equal(nodeXpub.extendedKey, neutered.extendedKey, + 'Serialized Neutered HDNode xpub matches - ' + nodeTest.privateKey); + } + + // Test serialization + var deserializedNode = ethers.utils.HDNode.fromExtendedKey(nodeTest.xpriv); + + assert.equal(deserializedNode.extendedKey, nodeTest.xpriv, + 'Neutered HDNode xpriv null - ' + nodeTest.privateKey); + assert.equal(deserializedNode.neuter().extendedKey, nodeTest.xpub, + 'Neutered HDNode xpriv null - ' + nodeTest.privateKey); + }); }); }); diff --git a/tests/tests/hdnode.json.gz b/tests/tests/hdnode.json.gz index 0ac67837..ed03a862 100644 Binary files a/tests/tests/hdnode.json.gz and b/tests/tests/hdnode.json.gz differ