diff --git a/lib/hd/hd.js b/lib/hd/hd.js index ce200a51..aeca5a2c 100644 --- a/lib/hd/hd.js +++ b/lib/hd/hd.js @@ -82,25 +82,14 @@ HD.fromJSON = function fromJSON(json, network) { /** * Instantiate an HD key from serialized data. * @param {Buffer} data + * @param {Network?} network * @returns {HDPrivateKey|HDPublicKey} */ -HD.fromRaw = function fromRaw(data) { - if (HDPrivateKey.isRaw(data)) - return HDPrivateKey.fromRaw(data); - return HDPublicKey.fromRaw(data); -}; - -/** - * Instantiate HD key from extended serialized data. - * @param {Buffer} data - * @returns {HDPrivateKey|HDPublicKey} - */ - -HD.fromExtended = function fromExtended(data) { - if (HDPrivateKey.isRaw(data)) - return HDPrivateKey.fromExtended(data); - return HDPublicKey.fromRaw(data); +HD.fromRaw = function fromRaw(data, network) { + if (HDPrivateKey.isRaw(data, network)) + return HDPrivateKey.fromRaw(data, network); + return HDPublicKey.fromRaw(data, network); }; /** @@ -117,11 +106,11 @@ HD.from = function from(options, network) { if (HD.isHD(options)) return options; - if (HD.isBase58(options)) + if (HD.isBase58(options, network)) return HD.fromBase58(options, network); - if (HD.isRaw(options)) - return HD.fromRaw(options); + if (HD.isRaw(options, network)) + return HD.fromRaw(options, network); if (options && typeof options === 'object') return HD.fromMnemonic(options, network); @@ -132,23 +121,25 @@ HD.from = function from(options, network) { /** * Test whether an object is in the form of a base58 hd key. * @param {String} data + * @param {Network?} network * @returns {Boolean} */ -HD.isBase58 = function isBase58(data) { - return HDPrivateKey.isBase58(data) - || HDPublicKey.isBase58(data); +HD.isBase58 = function isBase58(data, network) { + return HDPrivateKey.isBase58(data, network) + || HDPublicKey.isBase58(data, network); }; /** * Test whether an object is in the form of a serialized hd key. * @param {Buffer} data + * @param {Network?} network * @returns {NetworkType} */ -HD.isRaw = function isRaw(data) { - return HDPrivateKey.isRaw(data) - || HDPublicKey.isRaw(data); +HD.isRaw = function isRaw(data, network) { + return HDPrivateKey.isRaw(data, network) + || HDPublicKey.isRaw(data, network); }; /** diff --git a/lib/hd/private.js b/lib/hd/private.js index df4bc3d2..40164ff1 100644 --- a/lib/hd/private.js +++ b/lib/hd/private.js @@ -10,7 +10,6 @@ var assert = require('assert'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); var ec = require('../crypto/ec'); -var networks = require('../protocol/networks'); var Network = require('../protocol/network'); var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); @@ -142,18 +141,6 @@ HDPrivateKey.prototype.xpubkey = function xpubkey() { return this.toPublic().xpubkey(); }; -/** - * Verify network. - * @param {(NetworkType|Network)} network - * @returns {Boolean} - */ - -HDPrivateKey.prototype.verifyNetwork = function verifyNetwork(network) { - network = Network.get(network); - return this.network.keyPrefix.xprivkey === network.keyPrefix.xprivkey - && this.network.keyPrefix.coinType === network.keyPrefix.coinType; -}; - /** * Destroy the key (zeroes chain code, privkey, and pubkey). * @param {Boolean} pub - Destroy hd public key as well. @@ -189,19 +176,11 @@ HDPrivateKey.prototype.destroy = function destroy(pub) { * @returns {HDPrivateKey} */ -HDPrivateKey.prototype.derive = function derive(index, hardened, cache) { +HDPrivateKey.prototype.derive = function derive(index, hardened) { var bw, id, data, hash, left, right, key, child; - if (typeof hardened !== 'boolean') { - cache = hardened; - hardened = false; - } - - if (!cache) - cache = common.cache; - if (typeof index === 'string') - return this.derivePath(index, cache); + return this.derivePath(index); hardened = index >= common.HARDENED ? true : hardened; @@ -214,12 +193,11 @@ HDPrivateKey.prototype.derive = function derive(index, hardened, cache) { if (this.depth >= 0xff) throw new Error('Depth too high.'); - if (cache) { - id = this.getID(index); - child = cache.get(id); - if (child) - return child; - } + id = this.getID(index); + child = common.cache.get(id); + + if (child) + return child; bw = new StaticWriter(37); @@ -241,7 +219,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened, cache) { try { key = ec.privateKeyTweakAdd(this.privateKey, left); } catch (e) { - return this.derive(index + 1, cache); + return this.derive(index + 1); } if (!this.fingerPrint) @@ -256,8 +234,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened, cache) { child.privateKey = key; child.publicKey = ec.publicKeyCreate(key, true); - if (cache) - cache.set(id, child); + common.cache.set(id, child); return child; }; @@ -282,13 +259,13 @@ HDPrivateKey.prototype.getID = function getID(index) { * @throws Error if key is not a master key. */ -HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(accountIndex, cache) { +HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(accountIndex) { assert(util.isNumber(accountIndex), 'Account index must be a number.'); assert(this.isMaster(), 'Cannot derive account index.'); return this - .derive(44, true, cache) - .derive(this.network.keyPrefix.coinType, true, cache) - .derive(accountIndex, true, cache); + .derive(44, true) + .derive(this.network.keyPrefix.coinType, true) + .derive(accountIndex, true); }; /** @@ -296,9 +273,9 @@ HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(accountIndex, * @returns {HDPrivateKey} */ -HDPrivateKey.prototype.derivePurpose45 = function derivePurpose45(cache) { +HDPrivateKey.prototype.derivePurpose45 = function derivePurpose45() { assert(this.isMaster(), 'Cannot derive purpose 45.'); - return this.derive(45, true, cache); + return this.derive(45, true); }; /** @@ -332,33 +309,38 @@ HDPrivateKey.prototype.isPurpose45 = function isPurpose45() { /** * Test whether an object is in the form of a base58 xprivkey. * @param {String} data + * @param {Network?} network * @returns {Boolean} */ -HDPrivateKey.isBase58 = function isBase58(data) { - var i, type, prefix; +HDPrivateKey.isBase58 = function isBase58(data, network) { + var prefix; if (typeof data !== 'string') return false; - for (i = 0; i < networks.types.length; i++) { - type = networks.types[i]; - prefix = networks[type].keyPrefix.xprivkey58; - if (data.indexOf(prefix) === 0) - return true; - } + if (data.length < 4) + return false; - return false; + prefix = data.substring(0, 4); + + try { + Network.fromPrivate58(prefix, network); + return true; + } catch (e) { + return false; + } }; /** * Test whether a buffer has a valid network prefix. * @param {Buffer} data - * @returns {NetworkType} + * @param {Network?} network + * @returns {Boolean} */ -HDPrivateKey.isRaw = function isRaw(data) { - var i, version, prefix, type; +HDPrivateKey.isRaw = function isRaw(data, network) { + var version; if (!Buffer.isBuffer(data)) return false; @@ -368,14 +350,12 @@ HDPrivateKey.isRaw = function isRaw(data) { version = data.readUInt32BE(0, true); - for (i = 0; i < networks.types.length; i++) { - type = networks.types[i]; - prefix = networks[type].keyPrefix.xprivkey; - if (version === prefix) - return type; + try { + Network.fromPrivate(version, network); + return true; + } catch (e) { + return false; } - - return false; }; /** @@ -404,13 +384,13 @@ HDPrivateKey.isValidPath = function isValidPath(path) { * @throws Error if `path` is not a valid path. */ -HDPrivateKey.prototype.derivePath = function derivePath(path, cache) { +HDPrivateKey.prototype.derivePath = function derivePath(path) { var indexes = common.parsePath(path, common.MAX_INDEX); var key = this; var i; for (i = 0; i < indexes.length; i++) - key = key.derive(indexes[i], cache); + key = key.derive(indexes[i]); return key; }; @@ -530,8 +510,7 @@ HDPrivateKey.fromSeed = function fromSeed(seed, network) { HDPrivateKey.prototype.fromMnemonic = function fromMnemonic(mnemonic, network) { assert(mnemonic instanceof Mnemonic); - this.fromSeed(mnemonic.toSeed(), network); - return this; + return this.fromSeed(mnemonic.toSeed(), network); }; /** @@ -622,42 +601,31 @@ HDPrivateKey.generate = function generate(network) { */ HDPrivateKey.prototype.fromBase58 = function fromBase58(xkey, network) { - this.fromRaw(base58.decode(xkey)); + assert(typeof xkey === 'string'); this._xprivkey = xkey; - if (network && !this.verifyNetwork(network)) - throw new Error('Network mismatch for HD private key.'); - return this; + return this.fromRaw(base58.decode(xkey), network); }; /** * Inject properties from serialized data. * @private * @param {Buffer} raw + * @param {(Network|NetworkType)?} network */ -HDPrivateKey.prototype.fromReader = function fromReader(br) { - var i, version, type, prefix; +HDPrivateKey.prototype.fromReader = function fromReader(br, network) { + var version = br.readU32BE(); - version = br.readU32BE(); + this.network = Network.fromPrivate(version, network); this.depth = br.readU8(); this.parentFingerPrint = br.readBytes(4); this.childIndex = br.readU32BE(); this.chainCode = br.readBytes(32); br.readU8(); this.privateKey = br.readBytes(32); - br.verifyChecksum(); - - for (i = 0; i < networks.types.length; i++) { - type = networks.types[i]; - prefix = networks[type].keyPrefix.xprivkey; - if (version === prefix) - break; - } - - assert(i < networks.types.length, 'Network not found.'); - this.publicKey = ec.publicKeyCreate(this.privateKey, true); - this.network = Network.get(type); + + br.verifyChecksum(); return this; }; @@ -666,10 +634,11 @@ HDPrivateKey.prototype.fromReader = function fromReader(br) { * Inject properties from serialized data. * @private * @param {Buffer} raw + * @param {(Network|NetworkType)?} network */ -HDPrivateKey.prototype.fromRaw = function fromRaw(raw) { - return this.fromReader(new BufferReader(raw)); +HDPrivateKey.prototype.fromRaw = function fromRaw(raw, network) { + return this.fromReader(new BufferReader(raw), network); }; /** @@ -739,21 +708,23 @@ HDPrivateKey.fromBase58 = function fromBase58(xkey, network) { /** * Instantiate key from buffer reader. * @param {BufferReader} br + * @param {(Network|NetworkType)?} network * @returns {HDPrivateKey} */ -HDPrivateKey.fromReader = function fromReader(br) { - return new HDPrivateKey().fromReader(br); +HDPrivateKey.fromReader = function fromReader(br, network) { + return new HDPrivateKey().fromReader(br, network); }; /** * Instantiate key from serialized data. * @param {Buffer} raw + * @param {(Network|NetworkType)?} network * @returns {HDPrivateKey} */ -HDPrivateKey.fromRaw = function fromRaw(raw) { - return new HDPrivateKey().fromRaw(raw); +HDPrivateKey.fromRaw = function fromRaw(raw, network) { + return new HDPrivateKey().fromRaw(raw, network); }; /** @@ -803,7 +774,7 @@ HDPrivateKey.isHDPrivateKey = function isHDPrivateKey(obj) { return obj && typeof obj.derive === 'function' && typeof obj.fromMnemonic === 'function' - && obj.chainCode !== undefined; + && Buffer.isBuffer(obj.chainCode); }; /* diff --git a/lib/hd/public.js b/lib/hd/public.js index a14c1889..9b782ca1 100644 --- a/lib/hd/public.js +++ b/lib/hd/public.js @@ -10,7 +10,6 @@ var assert = require('assert'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); var ec = require('../crypto/ec'); -var networks = require('../protocol/networks'); var Network = require('../protocol/network'); var StaticWriter = require('../utils/staticwriter'); var BufferReader = require('../utils/reader'); @@ -121,18 +120,6 @@ HDPublicKey.prototype.xpubkey = function() { return this._xpubkey; }; -/** - * Verify network. - * @param {(NetworkType|Network)} network - * @returns {Boolean} - */ - -HDPublicKey.prototype.verifyNetwork = function verifyNetwork(network) { - network = Network.get(network); - return this.network.keyPrefix.xpubkey === network.keyPrefix.xpubkey - && this.network.keyPrefix.coinType === network.keyPrefix.coinType; -}; - /** * Destroy the key (zeroes chain code and pubkey). */ @@ -162,19 +149,11 @@ HDPublicKey.prototype.destroy = function destroy() { * @throws on `hardened` */ -HDPublicKey.prototype.derive = function derive(index, hardened, cache) { +HDPublicKey.prototype.derive = function derive(index, hardened) { var bw, id, data, hash, left, right, key, child; - if (typeof hardened !== 'boolean') { - cache = hardened; - hardened = false; - } - - if (!cache) - cache = common.cache; - if (typeof index === 'string') - return this.derivePath(index, cache); + return this.derivePath(index); if (index >= common.HARDENED || hardened) throw new Error('Index out of range.'); @@ -185,12 +164,11 @@ HDPublicKey.prototype.derive = function derive(index, hardened, cache) { if (this.depth >= 0xff) throw new Error('Depth too high.'); - if (cache) { - id = this.getID(index); - child = cache.get(id); - if (child) - return child; - } + id = this.getID(index); + child = common.cache.get(id); + + if (child) + return child; bw = new StaticWriter(37); bw.writeBytes(this.publicKey); @@ -204,7 +182,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened, cache) { try { key = ec.publicKeyTweakAdd(this.publicKey, left, true); } catch (e) { - return this.derive(index + 1, cache); + return this.derive(index + 1); } if (!this.fingerPrint) @@ -218,8 +196,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened, cache) { child.chainCode = right; child.publicKey = key; - if (cache) - cache.set(id, child); + common.cache.set(id, child); return child; }; @@ -320,13 +297,13 @@ HDPublicKey.isValidPath = function isValidPath(path) { * @throws Error if hardened. */ -HDPublicKey.prototype.derivePath = function derivePath(path, cache) { +HDPublicKey.prototype.derivePath = function derivePath(path) { var indexes = common.parsePath(path, common.HARDENED); var key = this; var i; for (i = 0; i < indexes.length; i++) - key = key.derive(indexes[i], cache); + key = key.derive(indexes[i]); return key; }; @@ -427,47 +404,53 @@ HDPublicKey.fromJSON = function fromJSON(json, network) { /** * Test whether an object is in the form of a base58 xpubkey. * @param {String} data + * @param {(Network|NetworkType)?} network * @returns {Boolean} */ -HDPublicKey.isBase58 = function isBase58(data) { - var i, type, prefix; +HDPublicKey.isBase58 = function isBase58(data, network) { + var prefix; if (typeof data !== 'string') return false; - for (i = 0; i < networks.types.length; i++) { - type = networks.types[i]; - prefix = networks[type].keyPrefix.xpubkey58; - if (data.indexOf(prefix) === 0) - return true; - } + if (data.length < 4) + return false; - return false; + prefix = data.substring(0, 4); + + try { + Network.fromPublic58(prefix, network); + return true; + } catch (e) { + return false; + } }; /** * Test whether a buffer has a valid network prefix. * @param {Buffer} data + * @param {(Network|NetworkType)?} network * @returns {NetworkType} */ -HDPublicKey.isRaw = function isRaw(data) { - var i, version, prefix, type; +HDPublicKey.isRaw = function isRaw(data, network) { + var version; if (!Buffer.isBuffer(data)) return false; + if (data.length < 4) + return false; + version = data.readUInt32BE(0, true); - for (i = 0; i < networks.types.length; i++) { - type = networks.types[i]; - prefix = networks[type].keyPrefix.xpubkey; - if (version === prefix) - return type; + try { + Network.fromPublic(version, network); + return true; + } catch (e) { + return false; } - - return false; }; /** @@ -478,41 +461,30 @@ HDPublicKey.isRaw = function isRaw(data) { */ HDPublicKey.prototype.fromBase58 = function fromBase58(xkey, network) { - this.fromRaw(base58.decode(xkey)); + assert(typeof xkey === 'string'); this._xpubkey = xkey; - if (network && !this.verifyNetwork(network)) - throw new Error('Network mismatch for HD public key.'); - return this; + return this.fromRaw(base58.decode(xkey), network); }; /** * Inject properties from serialized data. * @private * @param {Buffer} raw + * @param {(Network|NetworkType)?} network */ -HDPublicKey.prototype.fromReader = function fromReader(br) { - var i, version, type, prefix; +HDPublicKey.prototype.fromReader = function fromReader(br, network) { + var version = br.readU32BE(); - version = br.readU32BE(); + this.network = Network.fromPublic(version, network); this.depth = br.readU8(); this.parentFingerPrint = br.readBytes(4); this.childIndex = br.readU32BE(); this.chainCode = br.readBytes(32); this.publicKey = br.readBytes(33); + br.verifyChecksum(); - for (i = 0; i < networks.types.length; i++) { - type = networks.types[i]; - prefix = networks[type].keyPrefix.xpubkey; - if (version === prefix) - break; - } - - assert(i < networks.types.length, 'Network not found.'); - - this.network = Network.get(type); - return this; }; @@ -520,15 +492,16 @@ HDPublicKey.prototype.fromReader = function fromReader(br) { * Inject properties from serialized data. * @private * @param {Buffer} raw + * @param {(Network|NetworkType)?} network */ -HDPublicKey.prototype.fromRaw = function fromRaw(raw) { - return this.fromReader(new BufferReader(raw)); +HDPublicKey.prototype.fromRaw = function fromRaw(raw, network) { + return this.fromReader(new BufferReader(raw), network); }; /** * Serialize key data to base58 extended key. - * @param {Network|String} network + * @param {(Network|NetworkType)?} network * @returns {Base58String} */ @@ -539,7 +512,7 @@ HDPublicKey.prototype.toBase58 = function toBase58(network) { /** * Write the key to a buffer writer. * @param {BufferWriter} bw - * @param {Network|NetworkType} network + * @param {(Network|NetworkType)?} network */ HDPublicKey.prototype.toWriter = function toWriter(bw, network) { @@ -570,7 +543,7 @@ HDPublicKey.prototype.getSize = function getSize() { /** * Serialize the key. - * @param {Network|NetworkType} network + * @param {(Network|NetworkType)?} network * @returns {Buffer} */ @@ -592,21 +565,23 @@ HDPublicKey.fromBase58 = function fromBase58(xkey, network) { /** * Instantiate key from serialized data. * @param {BufferReader} br + * @param {(Network|NetworkType)?} network * @returns {HDPublicKey} */ -HDPublicKey.fromReader = function fromReader(br) { - return new HDPublicKey().fromReader(br); +HDPublicKey.fromReader = function fromReader(br, network) { + return new HDPublicKey().fromReader(br, network); }; /** * Instantiate key from serialized data. * @param {Buffer} raw + * @param {(Network|NetworkType)?} network * @returns {HDPublicKey} */ -HDPublicKey.fromRaw = function fromRaw(data) { - return new HDPublicKey().fromRaw(data); +HDPublicKey.fromRaw = function fromRaw(data, network) { + return new HDPublicKey().fromRaw(data, network); }; /** @@ -618,8 +593,8 @@ HDPublicKey.fromRaw = function fromRaw(data) { HDPublicKey.isHDPublicKey = function isHDPublicKey(obj) { return obj && typeof obj.derive === 'function' - && typeof obj.toExtended !== 'function' - && obj.chainCode !== undefined; + && obj.fromMnemonic === undefined + && Buffer.isBuffer(obj.chainCode); }; /* diff --git a/lib/primitives/address.js b/lib/primitives/address.js index c29a44b2..58782091 100644 --- a/lib/primitives/address.js +++ b/lib/primitives/address.js @@ -9,7 +9,6 @@ var assert = require('assert'); var Network = require('../protocol/network'); -var networks = require('../protocol/networks'); var encoding = require('../utils/encoding'); var util = require('../utils/util'); var crypto = require('../crypto/crypto'); @@ -22,12 +21,7 @@ var bech32 = require('../utils/bech32'); * Represents an address. * @alias module:primitives.Address * @constructor - * @param {Object} options - * @param {Buffer|Hash} options.hash - Address hash. - * @param {AddressPrefix} options.type - Address type - * `{witness,}{pubkeyhash,scripthash}`. - * @param {Number} [options.version=-1] - Witness program version. - * @param {(Network|NetworkType)?} options.network - Network name. + * @param {Object?} options * @property {Buffer} hash * @property {AddressPrefix} type * @property {Number} version @@ -105,30 +99,6 @@ Address.prototype.getHash = function getHash(enc) { return this.hash; }; -/** - * Get a network address prefix for the address. - * @param {Network?} network - * @returns {Number} - */ - -Address.prototype.getPrefix = function getPrefix(network) { - if (!network) - network = this.network; - network = Network.get(network); - return Address.getPrefix(this.type, this.hash, network); -}; - -/** - * Verify an address network (compares prefixes). - * @param {Network} network - * @returns {Boolean} - */ - -Address.prototype.verifyNetwork = function verifyNetwork(network) { - assert(network); - return this.getPrefix() === this.getPrefix(network); -}; - /** * Test whether the address is null. * @returns {Boolean} @@ -153,13 +123,46 @@ Address.prototype.isNull = function isNull() { /** * Get the address type as a string. - * @returns {AddressPrefix} + * @returns {String} */ Address.prototype.getType = function getType() { return Address.typesByVal[this.type].toLowerCase(); }; +/** + * Get a network address prefix for the address. + * @param {Network?} network + * @returns {Number} + */ + +Address.prototype.getPrefix = function getPrefix(network) { + var prefixes; + + if (!network) + network = this.network; + + network = Network.get(network); + prefixes = network.addressPrefix; + + switch (this.type) { + case Address.types.PUBKEYHASH: + return prefixes.pubkeyhash; + case Address.types.SCRIPTHASH: + return prefixes.scripthash; + case Address.types.WITNESS: + if (this.hash.length === 20) + return prefixes.witnesspubkeyhash; + + if (this.hash.length === 32) + return prefixes.witnessscripthash; + + break; + } + + return -1; +}; + /** * Calculate size of serialized address. * @returns {Number} @@ -225,7 +228,7 @@ Address.prototype.toBech32 = function toBech32(network) { var hrp; assert(version !== -1, - 'Cannot convert non-segwit address to bech32.'); + 'Cannot convert non-program address to bech32.'); if (!network) network = this.network; @@ -245,28 +248,18 @@ Address.prototype.toBech32 = function toBech32(network) { */ Address.prototype.fromString = function fromString(addr, network) { - var i, type, hrp, expect; + var hrp; assert(typeof addr === 'string'); - assert(addr.length >= 2); - hrp = addr.substring(0, 2).toLowerCase(); - - if (network) { - network = Network.get(network); - expect = network.addressPrefix.bech32; - if (hrp === expect) - return this.fromBech32(addr, network); - } else { - for (i = 0; i < networks.types.length; i++) { - type = networks.types[i]; - expect = networks[type].addressPrefix.bech32; - if (hrp === expect) - return this.fromBech32(addr, type); - } + try { + hrp = addr.substring(0, 2).toLowerCase(); + network = Network.fromBech32(hrp, network); + } catch (e) { + return this.fromBase58(addr, network); } - return this.fromBase58(addr, network); + return this.fromBech32(addr, network); }; /** @@ -312,23 +305,16 @@ Address.prototype.inspect = function inspect() { * @throws Parse error */ -Address.prototype.fromRaw = function fromRaw(data) { +Address.prototype.fromRaw = function fromRaw(data, network) { var br = new BufferReader(data, true); - var i, prefix, network, type, version, hash; + var prefix, type, version, hash; if (data.length > 40) throw new Error('Address is too long.'); prefix = br.readU8(); - - for (i = 0; i < networks.types.length; i++) { - network = networks[networks.types[i]]; - type = Address.getType(prefix, network); - if (type !== -1) - break; - } - - assert(i < networks.types.length, 'Unknown address prefix.'); + network = Network.fromAddress(prefix, network); + type = Address.getType(prefix, network); if (data.length > 25) { version = br.readU8(); @@ -351,87 +337,69 @@ Address.prototype.fromRaw = function fromRaw(data) { * @throws Parse error. */ -Address.fromRaw = function fromRaw(data) { - return new Address().fromRaw(data); +Address.fromRaw = function fromRaw(data, network) { + return new Address().fromRaw(data, network); }; /** * Inject properties from base58 address. * @private - * @param {Base58Address} str + * @param {Base58Address} data * @param {Network?} network * @throws Parse error */ -Address.prototype.fromBase58 = function fromBase58(str, network) { - assert(typeof str === 'string'); +Address.prototype.fromBase58 = function fromBase58(data, network) { + assert(typeof data === 'string'); - if (str.length > 55) + if (data.length > 55) throw new Error('Address is too long.'); - this.fromRaw(base58.decode(str)); - - if (network && !this.verifyNetwork(network)) - throw new Error('Network mismatch for address.'); - - return this; + return this.fromRaw(base58.decode(data), network); }; /** * Create an address object from a base58 address. - * @param {Base58Address} str + * @param {Base58Address} data * @param {Network?} network * @returns {Address} * @throws Parse error. */ -Address.fromBase58 = function fromBase58(str, network) { - return new Address().fromBase58(str, network); +Address.fromBase58 = function fromBase58(data, network) { + return new Address().fromBase58(data, network); }; /** * Inject properties from bech32 address. * @private - * @param {String} str + * @param {String} data * @param {Network?} network * @throws Parse error */ -Address.prototype.fromBech32 = function fromBech32(str, network) { +Address.prototype.fromBech32 = function fromBech32(data, network) { var type = Address.types.WITNESS; - var i, addr; + var addr; - assert(typeof str === 'string'); + assert(typeof data === 'string'); - addr = bech32.decode(str); + addr = bech32.decode(data); + network = Network.fromBech32(addr.hrp, network); - if (network) { - network = Network.get(network); - if (addr.hrp !== network.addressPrefix.bech32) - throw new Error('Network mismatch for bech32 address.'); - } else { - for (i = 0; i < networks.types.length; i++) { - network = networks[networks.types[i]]; - if (addr.hrp === network.addressPrefix.bech32) - break; - } - - assert(i < networks.types.length, 'Unknown bech32 address prefix.'); - } - - return this.fromHash(addr.hash, type, addr.version, network.type); + return this.fromHash(addr.hash, type, addr.version, network); }; /** * Create an address object from a bech32 address. - * @param {String} str + * @param {String} data * @param {Network?} network * @returns {Address} * @throws Parse error. */ -Address.fromBech32 = function fromBech32(str, network) { - return new Address().fromBech32(str, network); +Address.fromBech32 = function fromBech32(data, network) { + return new Address().fromBech32(data, network); }; /** @@ -877,37 +845,11 @@ Address.getHash = function getHash(data, enc) { : hash; }; -/** - * Get a network address prefix for a specified address type. - * @param {AddressPrefix} type - * @param {Buffer} hash - * @param {Network} network - * @returns {Number} - */ - -Address.getPrefix = function getPrefix(type, hash, network) { - var prefixes = network.addressPrefix; - switch (type) { - case Address.types.PUBKEYHASH: - return prefixes.pubkeyhash; - case Address.types.SCRIPTHASH: - return prefixes.scripthash; - case Address.types.WITNESS: - if (hash.length === 20) - return prefixes.witnesspubkeyhash; - if (hash.length === 32) - return prefixes.witnessscripthash; - assert(false, 'No witness prefix defined.'); - default: - return -1; - } -}; - /** * Get an address type for a specified network address prefix. * @param {Number} prefix * @param {Network} network - * @returns {AddressPrefix} + * @returns {AddressType} */ Address.getType = function getType(prefix, network) { @@ -922,7 +864,7 @@ Address.getType = function getType(prefix, network) { case prefixes.witnessscripthash: return Address.types.WITNESS; default: - return -1; + throw new Error('Unknown address prefix.'); } }; diff --git a/lib/primitives/keyring.js b/lib/primitives/keyring.js index f96f8fb8..a66b72a9 100644 --- a/lib/primitives/keyring.js +++ b/lib/primitives/keyring.js @@ -11,7 +11,6 @@ var assert = require('assert'); var util = require('../utils/util'); var encoding = require('../utils/encoding'); var crypto = require('../crypto/crypto'); -var networks = require('../protocol/networks'); var Network = require('../protocol/network'); var BufferReader = require('../utils/reader'); var StaticWriter = require('../utils/staticwriter'); @@ -124,7 +123,7 @@ KeyRing.prototype.refresh = function refresh() { * @private * @param {Buffer} key * @param {Boolean?} compressed - * @param {(NetworkType|Network)} network + * @param {(NetworkType|Network)?} network */ KeyRing.prototype.fromPrivate = function fromPrivate(key, compressed, network) { @@ -147,7 +146,7 @@ KeyRing.prototype.fromPrivate = function fromPrivate(key, compressed, network) { * Instantiate keyring from a private key. * @param {Buffer} key * @param {Boolean?} compressed - * @param {(NetworkType|Network)} network + * @param {(NetworkType|Network)?} network * @returns {KeyRing} */ @@ -159,7 +158,7 @@ KeyRing.fromPrivate = function fromPrivate(key, compressed, network) { * Inject data from public key. * @private * @param {Buffer} key - * @param {(NetworkType|Network)} network + * @param {(NetworkType|Network)?} network */ KeyRing.prototype.fromPublic = function fromPublic(key, network) { @@ -203,7 +202,7 @@ KeyRing.generate = function(compressed, network) { /** * Instantiate keyring from a public key. * @param {Buffer} publicKey - * @param {(NetworkType|Network)} network + * @param {(NetworkType|Network)?} network * @returns {KeyRing} */ @@ -215,7 +214,7 @@ KeyRing.fromPublic = function fromPublic(key, network) { * Inject data from public key. * @private * @param {Buffer} privateKey - * @param {(NetworkType|Network)} network + * @param {(NetworkType|Network)?} network */ KeyRing.prototype.fromKey = function fromKey(key, compressed, network) { @@ -235,7 +234,7 @@ KeyRing.prototype.fromKey = function fromKey(key, compressed, network) { /** * Instantiate keyring from a public key. * @param {Buffer} publicKey - * @param {(NetworkType|Network)} network + * @param {(NetworkType|Network)?} network * @returns {KeyRing} */ @@ -248,7 +247,7 @@ KeyRing.fromKey = function fromKey(key, compressed, network) { * @private * @param {Buffer} key * @param {Script} script - * @param {(NetworkType|Network)} network + * @param {(NetworkType|Network)?} network */ KeyRing.prototype.fromScript = function fromScript(key, script, compressed, network) { @@ -269,7 +268,7 @@ KeyRing.prototype.fromScript = function fromScript(key, script, compressed, netw * Instantiate keyring from script. * @param {Buffer} key * @param {Script} script - * @param {(NetworkType|Network)} network + * @param {(NetworkType|Network)?} network * @returns {KeyRing} */ @@ -302,13 +301,18 @@ KeyRing.prototype.getSecretSize = function getSecretSize() { * @returns {Base58String} */ -KeyRing.prototype.toSecret = function toSecret() { +KeyRing.prototype.toSecret = function toSecret(network) { var size = this.getSecretSize(); var bw = new StaticWriter(size); assert(this.privateKey, 'Cannot serialize without private key.'); - bw.writeU8(this.network.keyPrefix.privkey); + if (!network) + network = this.network; + + network = Network.get(network); + + bw.writeU8(network.keyPrefix.privkey); bw.writeBytes(this.privateKey); if (this.publicKey.length === 33) @@ -323,23 +327,15 @@ KeyRing.prototype.toSecret = function toSecret() { * Inject properties from serialized CBitcoinSecret. * @private * @param {Base58String} secret - * @param {Network?} network + * @param {(Network|NetworkType)?} network */ KeyRing.prototype.fromSecret = function fromSecret(data, network) { var br = new BufferReader(base58.decode(data), true); - var i, prefix, version, type, key, compressed; + var version, key, compressed; version = br.readU8(); - - for (i = 0; i < networks.types.length; i++) { - type = networks.types[i]; - prefix = networks[type].keyPrefix.privkey; - if (version === prefix) - break; - } - - assert(i < networks.types.length, 'Network not found.'); + network = Network.fromWIF(version, network); key = br.readBytes(32); @@ -352,18 +348,13 @@ KeyRing.prototype.fromSecret = function fromSecret(data, network) { br.verifyChecksum(); - this.fromPrivate(key, compressed, type); - - if (network && !this.verifyNetwork(network)) - throw new Error('Network mismatch for WIF.'); - - return this; + return this.fromPrivate(key, compressed, network); }; /** * Instantiate a keyring from a serialized CBitcoinSecret. * @param {Base58String} secret - * @param {Network?} network + * @param {(Network|NetworkType)?} network * @returns {KeyRing} */ @@ -371,17 +362,6 @@ KeyRing.fromSecret = function fromSecret(data, network) { return new KeyRing().fromSecret(data, network); }; -/** - * Verify network. - * @param {(NetworkType|Network)} network - * @returns {Boolean} - */ - -KeyRing.prototype.verifyNetwork = function verifyNetwork(network) { - network = Network.get(network); - return this.network.keyPrefix.privkey === network.keyPrefix.privkey; -}; - /** * Get private key. * @param {String?} enc - Can be `"hex"`, `"base58"`, or `null`. diff --git a/lib/protocol/network.js b/lib/protocol/network.js index c25beaf1..d4d35787 100644 --- a/lib/protocol/network.js +++ b/lib/protocol/network.js @@ -235,22 +235,121 @@ Network.ensure = function ensure(type) { }; /** - * Get a network by its magic number. + * Get a network by an associated comparator. + * @private + * @param {Object} value + * @param {Function} compare + * @param {Network|null} network + * @param {String} name * @returns {Network} */ -Network.fromMagic = function fromMagic(magic) { +Network.by = function by(value, compare, network, name) { var i, type; + if (network) { + network = Network.get(network); + if (compare(network, value)) + return network; + throw new Error('Network mismatch for ' + name + '.'); + } + for (i = 0; i < networks.types.length; i++) { type = networks.types[i]; - if (magic === networks[type].magic) - break; + network = networks[type]; + if (compare(network, value)) + return Network.get(type); } - assert(i < networks.types.length, 'Network not found.'); + throw new Error('Network not found for ' + name + '.'); +}; - return Network.get(type); +/** + * Get a network by its magic number. + * @param {Number} value + * @param {Network?} network + * @returns {Network} + */ + +Network.fromMagic = function fromMagic(value, network) { + return Network.by(value, cmpMagic, network, 'magic number'); +}; + +/** + * Get a network by its WIF prefix. + * @param {Number} value + * @param {Network?} network + * @returns {Network} + */ + +Network.fromWIF = function fromWIF(prefix, network) { + return Network.by(prefix, cmpWIF, network, 'WIF'); +}; + +/** + * Get a network by its xpubkey prefix. + * @param {Number} value + * @param {Network?} network + * @returns {Network} + */ + +Network.fromPublic = function fromPublic(prefix, network) { + return Network.by(prefix, cmpPub, network, 'xpubkey'); +}; + +/** + * Get a network by its xprivkey prefix. + * @param {Number} value + * @param {Network?} network + * @returns {Network} + */ + +Network.fromPrivate = function fromPrivate(prefix, network) { + return Network.by(prefix, cmpPriv, network, 'xprivkey'); +}; + +/** + * Get a network by its xpubkey base58 prefix. + * @param {String} prefix + * @param {Network?} network + * @returns {Network} + */ + +Network.fromPublic58 = function fromPublic58(prefix, network) { + return Network.by(prefix, cmpPub58, network, 'xpubkey'); +}; + +/** + * Get a network by its xprivkey base58 prefix. + * @param {String} prefix + * @param {Network?} network + * @returns {Network} + */ + +Network.fromPrivate58 = function fromPrivate58(prefix, network) { + return Network.by(prefix, cmpPriv58, network, 'xprivkey'); +}; + +/** + * Get a network by its base58 address prefix. + * @param {Number} value + * @param {Network?} network + * @returns {Network} + */ + +Network.fromAddress = function fromAddress(prefix, network) { + return Network.by(prefix, cmpAddress, network, 'base58 address'); +}; + +/** + * Get a network by its bech32 address prefix. + * @param {String} hrp + * @param {Network?} network + * @returns {Network} + */ + +Network.fromBech32 = function fromBech32(hrp, network) { + return Network.by(hrp, cmpBech32, network, 'bech32 address'); }; /** @@ -302,6 +401,48 @@ function cmpNode(a, b) { return a.height - b.height; } +function cmpMagic(network, magic) { + return network.magic === magic; +} + +function cmpWIF(network, prefix) { + return network.keyPrefix.privkey === prefix; +} + +function cmpPub(network, prefix) { + return network.keyPrefix.xpubkey === prefix; +} + +function cmpPriv(network, prefix) { + return network.keyPrefix.xprivkey === prefix; +} + +function cmpPub58(network, prefix) { + return network.keyPrefix.xpubkey58 === prefix; +} + +function cmpPriv58(network, prefix) { + return network.keyPrefix.xprivkey58 === prefix; +} + +function cmpAddress(network, prefix) { + var prefixes = network.addressPrefix; + + switch (prefix) { + case prefixes.pubkeyhash: + case prefixes.scripthash: + case prefixes.witnesspubkeyhash: + case prefixes.witnessscripthash: + return true; + } + + return false; +} + +function cmpBech32(network, hrp) { + return network.addressPrefix.bech32 === hrp; +} + /* * Expose */