From 69d0244b21df92098f8df80e91b69af74a2b8e2c Mon Sep 17 00:00:00 2001 From: Otto Allmendinger Date: Wed, 8 Jan 2020 11:46:18 +0100 Subject: [PATCH] fix(networks): BIP32 constants for litecoin Litecoin in fact has the same BIP32 prefixes (xpub/xprv, tpub/tprv) as all other utxo coins. This error was introduced in bitcoinjs-lib and propagated to `cryptocoinjs/coininfo`, where a similar fix is pending: https://github.com/cryptocoinjs/coininfo/pull/76/files It is unclear what the source for the incorrect values was originally. Litecoin Mainnet: https://github.com/litecoin-project/litecoin/blob/1b6c480/src/chainparams.cpp#L142-L143 Litecoin Testnet: https://github.com/litecoin-project/litecoin/blob/1b6c480/src/chainparams.cpp#L249-L250 * Add `getDefaultBip32Mainnet()` and `getDefaultBip32Testnet()` to make shared values more obvious. * Modify tests fixtures and tests which previously assumed bip32-exported coins have different prefixes. BREAKING CHANGE: While this is a breaking change, I don't think these values were actually used anywhere. Issue: BG-16466 --- src/hdnode.js | 4 +- src/networks.js | 103 +++++++++++++++----------------------- test/fixtures/hdnode.json | 8 +-- test/hdnode.js | 12 ++--- test/networks.js | 19 ++----- 5 files changed, 57 insertions(+), 89 deletions(-) diff --git a/src/hdnode.js b/src/hdnode.js index 9cda8a6d..c0891801 100644 --- a/src/hdnode.js +++ b/src/hdnode.js @@ -56,7 +56,6 @@ HDNode.fromSeedHex = function (hex, network) { } HDNode.fromBase58 = function (string, networks) { - // FixMe: Issue #38, this method just pops the latest network object from the list instead of being more discerning. var buffer = base58check.decode(string) if (buffer.length !== 78) throw new Error('Invalid buffer length') @@ -66,6 +65,9 @@ HDNode.fromBase58 = function (string, networks) { // list of networks? if (Array.isArray(networks)) { + // FIXME(BG-16845): + // This is only useful when you know the coin but you are not sure if it is mainnet or testnet. + // All mainnets have xpub/xprv and all testnets have tpub/tprv as version. network = networks.filter(function (x) { return version === x.bip32.private || version === x.bip32.public diff --git a/src/networks.js b/src/networks.js index 1524e019..c9a835b3 100644 --- a/src/networks.js +++ b/src/networks.js @@ -2,15 +2,15 @@ The values for the various fork coins can be found in these files: -property filename varname ------------------------------------------------------------------- -messagePrefix: src/validation.cpp strMessageMagic -bech32_hrp: src/chainparams.cpp bech32_hrp -bip32.public: src/chainparams.cpp base58Prefixes[EXT_PUBLIC_KEY] -bip32.private src/chainparams.cpp base58Prefixes[EXT_SECRET_KEY] -pubKeyHash: src/chainparams.cpp base58Prefixes[PUBKEY_ADDRESS] -scriptHash: src/chainparams.cpp base58Prefixes[SCRIPT_ADDRESS] -wif: src/chainparams.cpp base58Prefixes[SECRET_KEY] +property filename varname notes +------------------------------------------------------------------------------------------------------------------------ +messagePrefix src/validation.cpp strMessageMagic Format `${CoinName} Signed Message` +bech32_hrp src/chainparams.cpp bech32_hrp Only for some networks +bip32.public src/chainparams.cpp base58Prefixes[EXT_PUBLIC_KEY] Mainnets have same value, testnets have same value +bip32.private src/chainparams.cpp base58Prefixes[EXT_SECRET_KEY] Mainnets have same value, testnets have same value +pubKeyHash src/chainparams.cpp base58Prefixes[PUBKEY_ADDRESS] +scriptHash src/chainparams.cpp base58Prefixes[SCRIPT_ADDRESS] +wif src/chainparams.cpp base58Prefixes[SECRET_KEY] Testnets have same value */ @@ -24,6 +24,24 @@ const coins = { DASH: 'dash' } +function getDefaultBip32Mainnet () { + return { + // base58 'xpub' + public: 0x0488b21e, + // base58 'xprv' + private: 0x0488ade4 + } +} + +function getDefaultBip32Testnet () { + return { + // base58 'tpub' + public: 0x043587cf, + // base58 'tprv' + private: 0x04358394 + } +} + module.exports = { // https://github.com/bitcoin/bitcoin/blob/master/src/validation.cpp @@ -31,10 +49,7 @@ module.exports = { bitcoin: { messagePrefix: '\x18Bitcoin Signed Message:\n', bech32: 'bc', - bip32: { - public: 0x0488b21e, - private: 0x0488ade4 - }, + bip32: getDefaultBip32Mainnet(), pubKeyHash: 0x00, scriptHash: 0x05, wif: 0x80, @@ -43,10 +58,7 @@ module.exports = { testnet: { messagePrefix: '\x18Bitcoin Signed Message:\n', bech32: 'tb', - bip32: { - public: 0x043587cf, - private: 0x04358394 - }, + bip32: getDefaultBip32Testnet(), pubKeyHash: 0x6f, scriptHash: 0xc4, wif: 0xef, @@ -57,10 +69,7 @@ module.exports = { // https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/src/chainparams.cpp bitcoincash: { messagePrefix: '\x18Bitcoin Signed Message:\n', - bip32: { - public: 0x0488b21e, - private: 0x0488ade4 - }, + bip32: getDefaultBip32Mainnet(), pubKeyHash: 0x00, scriptHash: 0x05, wif: 0x80, @@ -69,10 +78,7 @@ module.exports = { }, bitcoincashTestnet: { messagePrefix: '\x18Bitcoin Signed Message:\n', - bip32: { - public: 0x043587cf, - private: 0x04358394 - }, + bip32: getDefaultBip32Testnet(), pubKeyHash: 0x6f, scriptHash: 0xc4, wif: 0xef, @@ -84,10 +90,7 @@ module.exports = { bitcoingold: { messagePrefix: '\x18Bitcoin Gold Signed Message:\n', bech32: 'btg', - bip32: { - public: 0x0488b21e, - private: 0x0488ade4 - }, + bip32: getDefaultBip32Mainnet(), pubKeyHash: 0x26, scriptHash: 0x17, wif: 0x80, @@ -100,10 +103,7 @@ module.exports = { // https://github.com/bitcoin-sv/bitcoin-sv/blob/master/src/chainparams.cpp bitcoinsv: { messagePrefix: '\x18Bitcoin Signed Message:\n', - bip32: { - public: 0x0488b21e, - private: 0x0488ade4 - }, + bip32: getDefaultBip32Mainnet(), pubKeyHash: 0x00, scriptHash: 0x05, wif: 0x80, @@ -112,10 +112,7 @@ module.exports = { }, bitcoinsvTestnet: { messagePrefix: '\x18Bitcoin Signed Message:\n', - bip32: { - public: 0x043587cf, - private: 0x04358394 - }, + bip32: getDefaultBip32Testnet(), pubKeyHash: 0x6f, scriptHash: 0xc4, wif: 0xef, @@ -126,10 +123,7 @@ module.exports = { // https://github.com/dashpay/dash/blob/master/src/chainparams.cpp dash: { messagePrefix: '\x19DarkCoin Signed Message:\n', - bip32: { - public: 0x0488b21e, - private: 0x0488ade4 - }, + bip32: getDefaultBip32Mainnet(), pubKeyHash: 0x4c, scriptHash: 0x10, wif: 0xcc, @@ -137,10 +131,7 @@ module.exports = { }, dashTest: { messagePrefix: '\x19DarkCoin Signed Message:\n', - bip32: { - public: 0x043587cf, - private: 0x04358394 - }, + bip32: getDefaultBip32Testnet(), pubKeyHash: 0x8c, scriptHash: 0x13, wif: 0xef, @@ -152,11 +143,7 @@ module.exports = { litecoin: { messagePrefix: '\x19Litecoin Signed Message:\n', bech32: 'ltc', - bip32: { - // FIXME(BG-16466): these are incorrect - public: 0x019da462, - private: 0x019d9cfe - }, + bip32: getDefaultBip32Mainnet(), pubKeyHash: 0x30, scriptHash: 0x32, wif: 0xb0, @@ -165,11 +152,7 @@ module.exports = { litecoinTest: { messagePrefix: '\x19Litecoin Signed Message:\n', bech32: 'tltc', - bip32: { - // FIXME(BG-16466): these are incorrect - public: 0x0488b21e, - private: 0x0488ade4 - }, + bip32: getDefaultBip32Testnet(), pubKeyHash: 0x6f, scriptHash: 0x3a, wif: 0xef, @@ -180,10 +163,7 @@ module.exports = { // https://github.com/zcash/zcash/blob/master/src/chainparams.cpp zcash: { messagePrefix: '\x18ZCash Signed Message:\n', - bip32: { - public: 0x0488b21e, - private: 0x0488ade4 - }, + bip32: getDefaultBip32Mainnet(), pubKeyHash: 0x1cb8, scriptHash: 0x1cbd, wif: 0x80, @@ -200,10 +180,7 @@ module.exports = { }, zcashTest: { messagePrefix: '\x18ZCash Signed Message:\n', - bip32: { - public: 0x043587cf, - private: 0x04358394 - }, + bip32: getDefaultBip32Testnet(), pubKeyHash: 0x1d25, scriptHash: 0x1cba, wif: 0xef, diff --git a/test/fixtures/hdnode.json b/test/fixtures/hdnode.json index 56e97d6c..33e68d7d 100644 --- a/test/fixtures/hdnode.json +++ b/test/fixtures/hdnode.json @@ -213,8 +213,8 @@ "wif": "TAroS5Knm8GZcnpPycBgzjwwDLWMyQjDrcuGPPoArgrbW7Ln22qp", "pubKey": "0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2", "chainCode": "873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508", - "base58": "Ltub2SSUS19CirucWFod2ZsYA2J4v4U76YiCXHdcQttnoiy5aGanFHCPDBX7utfG6f95u1cUbZJNafmvzNCzZZJTw1EmyFoL8u1gJbGM8ipu491", - "base58Priv": "Ltpv71G8qDifUiNetP6nmxPA5STrUVmv2J9YSmXajv8VsYBUyuPhvN9xCaQrfX2wo5xxJNtEazYCFRUu5FmokYMM79pcqz8pcdo4rNXAFPgyB4k", + "base58": "xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8", + "base58Priv": "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi", "identifier": "3442193e1bb70916e914552172cd4e2dbc9df811", "fingerprint": "3442193e", "address": "LPzGaoLUtXFkmNo3u1chDxGxDnSaBQTTxm" @@ -227,8 +227,8 @@ "wif": "TB22qU2V9EJCVKJ8cdYaTfvDhnYcCzthcWgFm1k6hbvbKM1NLxoL", "pubKey": "035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56", "chainCode": "47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141", - "base58": "Ltub2UhtRiSfp82berwLEKkB34QBEt2TUdCDCu4WNzGumvAMwYsxfWjULKsXhADxqy3cuDu3TnqoKJr1xmB8Wb2qzthWAtbb4CutpXPuSU1YMgG", - "base58Priv": "Ltpv73XYpw28ZyVe2zEVyiFnxUZxoKLGQNdZ8NxUi1WcqjNmMBgtLbh3KimGSnPHCoLv1RmvxHs4dnKmo1oXQ8dXuDu8uroxrbVxZPA1gXboYvx", + "base58": "xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", + "base58Priv": "xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", "identifier": "5c1bd648ed23aa5fd50ba52b2457c11e9e80a6a7", "fingerprint": "5c1bd648", "address": "LTcyn1jun6g9hvxtsT7cqMRSyix7AULC76", diff --git a/test/hdnode.js b/test/hdnode.js index 45782b9e..eb38321b 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -13,10 +13,10 @@ var fixtures = require('./fixtures/hdnode.json') var curve = ecdsa.__curve var NETWORKS = require('../src/networks') -var NETWORKS_LIST = [] // Object.values(NETWORKS) -for (var networkName in NETWORKS) { - NETWORKS_LIST.push(NETWORKS[networkName]) -} +var NETWORKS_LIST = [ + NETWORKS.bitcoin, + NETWORKS.testnet +] var validAll = [] fixtures.valid.forEach(function (f) { @@ -176,7 +176,7 @@ describe('HDNode', function () { describe('fromBase58 / toBase58', function () { validAll.forEach(function (f) { it('exports ' + f.base58 + ' (public) correctly', function () { - var hd = HDNode.fromBase58(f.base58, NETWORKS_LIST) + var hd = HDNode.fromBase58(f.base58, f.network ? NETWORKS[f.network] : undefined) assert.strictEqual(hd.toBase58(), f.base58) assert.throws(function () { hd.keyPair.toWIF() }, /Missing private key/) @@ -185,7 +185,7 @@ describe('HDNode', function () { validAll.forEach(function (f) { it('exports ' + f.base58Priv + ' (private) correctly', function () { - var hd = HDNode.fromBase58(f.base58Priv, NETWORKS_LIST) + var hd = HDNode.fromBase58(f.base58Priv, f.network ? NETWORKS[f.network] : undefined) assert.strictEqual(hd.toBase58(), f.base58Priv) assert.strictEqual(hd.keyPair.toWIF(), f.wif) diff --git a/test/networks.js b/test/networks.js index ef699a89..fa85be63 100644 --- a/test/networks.js +++ b/test/networks.js @@ -87,23 +87,12 @@ describe('networks', function () { assert.strictEqual(typeof network.wif, 'number') assert.strictEqual(typeof network.coin, 'string') - // FIXME(BG-16466): litecoin should not be a special case here -- all forks have the same bip32 values - const isLitecoin = coins.getMainnet(network) === networks.litecoin - if (coins.isMainnet(network)) { - assert.strictEqual( - (network.bip32.public === networks.bitcoin.bip32.public), !isLitecoin - ) - assert.strictEqual( - (network.bip32.private === networks.bitcoin.bip32.private), !isLitecoin - ) + assert.strictEqual(network.bip32.public, networks.bitcoin.bip32.public) + assert.strictEqual(network.bip32.private, networks.bitcoin.bip32.private) } else { - assert.strictEqual( - (network.bip32.public === networks.testnet.bip32.public), !isLitecoin - ) - assert.strictEqual( - (network.bip32.private === networks.testnet.bip32.private), !isLitecoin - ) + assert.strictEqual(network.bip32.public, networks.testnet.bip32.public) + assert.strictEqual(network.bip32.private, networks.testnet.bip32.private) } })