network: add more helper functions.

This commit is contained in:
Christopher Jeffrey 2017-05-13 21:35:58 -07:00
parent 2997333122
commit 11a660aeab
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
6 changed files with 366 additions and 366 deletions

View File

@ -82,25 +82,14 @@ HD.fromJSON = function fromJSON(json, network) {
/** /**
* Instantiate an HD key from serialized data. * Instantiate an HD key from serialized data.
* @param {Buffer} data * @param {Buffer} data
* @param {Network?} network
* @returns {HDPrivateKey|HDPublicKey} * @returns {HDPrivateKey|HDPublicKey}
*/ */
HD.fromRaw = function fromRaw(data) { HD.fromRaw = function fromRaw(data, network) {
if (HDPrivateKey.isRaw(data)) if (HDPrivateKey.isRaw(data, network))
return HDPrivateKey.fromRaw(data); return HDPrivateKey.fromRaw(data, network);
return HDPublicKey.fromRaw(data); return HDPublicKey.fromRaw(data, network);
};
/**
* 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);
}; };
/** /**
@ -117,11 +106,11 @@ HD.from = function from(options, network) {
if (HD.isHD(options)) if (HD.isHD(options))
return options; return options;
if (HD.isBase58(options)) if (HD.isBase58(options, network))
return HD.fromBase58(options, network); return HD.fromBase58(options, network);
if (HD.isRaw(options)) if (HD.isRaw(options, network))
return HD.fromRaw(options); return HD.fromRaw(options, network);
if (options && typeof options === 'object') if (options && typeof options === 'object')
return HD.fromMnemonic(options, network); 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. * Test whether an object is in the form of a base58 hd key.
* @param {String} data * @param {String} data
* @param {Network?} network
* @returns {Boolean} * @returns {Boolean}
*/ */
HD.isBase58 = function isBase58(data) { HD.isBase58 = function isBase58(data, network) {
return HDPrivateKey.isBase58(data) return HDPrivateKey.isBase58(data, network)
|| HDPublicKey.isBase58(data); || HDPublicKey.isBase58(data, network);
}; };
/** /**
* Test whether an object is in the form of a serialized hd key. * Test whether an object is in the form of a serialized hd key.
* @param {Buffer} data * @param {Buffer} data
* @param {Network?} network
* @returns {NetworkType} * @returns {NetworkType}
*/ */
HD.isRaw = function isRaw(data) { HD.isRaw = function isRaw(data, network) {
return HDPrivateKey.isRaw(data) return HDPrivateKey.isRaw(data, network)
|| HDPublicKey.isRaw(data); || HDPublicKey.isRaw(data, network);
}; };
/** /**

View File

@ -10,7 +10,6 @@ var assert = require('assert');
var util = require('../utils/util'); var util = require('../utils/util');
var crypto = require('../crypto/crypto'); var crypto = require('../crypto/crypto');
var ec = require('../crypto/ec'); var ec = require('../crypto/ec');
var networks = require('../protocol/networks');
var Network = require('../protocol/network'); var Network = require('../protocol/network');
var StaticWriter = require('../utils/staticwriter'); var StaticWriter = require('../utils/staticwriter');
var BufferReader = require('../utils/reader'); var BufferReader = require('../utils/reader');
@ -142,18 +141,6 @@ HDPrivateKey.prototype.xpubkey = function xpubkey() {
return this.toPublic().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). * Destroy the key (zeroes chain code, privkey, and pubkey).
* @param {Boolean} pub - Destroy hd public key as well. * @param {Boolean} pub - Destroy hd public key as well.
@ -189,19 +176,11 @@ HDPrivateKey.prototype.destroy = function destroy(pub) {
* @returns {HDPrivateKey} * @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; 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') if (typeof index === 'string')
return this.derivePath(index, cache); return this.derivePath(index);
hardened = index >= common.HARDENED ? true : hardened; hardened = index >= common.HARDENED ? true : hardened;
@ -214,12 +193,11 @@ HDPrivateKey.prototype.derive = function derive(index, hardened, cache) {
if (this.depth >= 0xff) if (this.depth >= 0xff)
throw new Error('Depth too high.'); throw new Error('Depth too high.');
if (cache) { id = this.getID(index);
id = this.getID(index); child = common.cache.get(id);
child = cache.get(id);
if (child) if (child)
return child; return child;
}
bw = new StaticWriter(37); bw = new StaticWriter(37);
@ -241,7 +219,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened, cache) {
try { try {
key = ec.privateKeyTweakAdd(this.privateKey, left); key = ec.privateKeyTweakAdd(this.privateKey, left);
} catch (e) { } catch (e) {
return this.derive(index + 1, cache); return this.derive(index + 1);
} }
if (!this.fingerPrint) if (!this.fingerPrint)
@ -256,8 +234,7 @@ HDPrivateKey.prototype.derive = function derive(index, hardened, cache) {
child.privateKey = key; child.privateKey = key;
child.publicKey = ec.publicKeyCreate(key, true); child.publicKey = ec.publicKeyCreate(key, true);
if (cache) common.cache.set(id, child);
cache.set(id, child);
return child; return child;
}; };
@ -282,13 +259,13 @@ HDPrivateKey.prototype.getID = function getID(index) {
* @throws Error if key is not a master key. * @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(util.isNumber(accountIndex), 'Account index must be a number.');
assert(this.isMaster(), 'Cannot derive account index.'); assert(this.isMaster(), 'Cannot derive account index.');
return this return this
.derive(44, true, cache) .derive(44, true)
.derive(this.network.keyPrefix.coinType, true, cache) .derive(this.network.keyPrefix.coinType, true)
.derive(accountIndex, true, cache); .derive(accountIndex, true);
}; };
/** /**
@ -296,9 +273,9 @@ HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(accountIndex,
* @returns {HDPrivateKey} * @returns {HDPrivateKey}
*/ */
HDPrivateKey.prototype.derivePurpose45 = function derivePurpose45(cache) { HDPrivateKey.prototype.derivePurpose45 = function derivePurpose45() {
assert(this.isMaster(), 'Cannot derive purpose 45.'); 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. * Test whether an object is in the form of a base58 xprivkey.
* @param {String} data * @param {String} data
* @param {Network?} network
* @returns {Boolean} * @returns {Boolean}
*/ */
HDPrivateKey.isBase58 = function isBase58(data) { HDPrivateKey.isBase58 = function isBase58(data, network) {
var i, type, prefix; var prefix;
if (typeof data !== 'string') if (typeof data !== 'string')
return false; return false;
for (i = 0; i < networks.types.length; i++) { if (data.length < 4)
type = networks.types[i]; return false;
prefix = networks[type].keyPrefix.xprivkey58;
if (data.indexOf(prefix) === 0)
return true;
}
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. * Test whether a buffer has a valid network prefix.
* @param {Buffer} data * @param {Buffer} data
* @returns {NetworkType} * @param {Network?} network
* @returns {Boolean}
*/ */
HDPrivateKey.isRaw = function isRaw(data) { HDPrivateKey.isRaw = function isRaw(data, network) {
var i, version, prefix, type; var version;
if (!Buffer.isBuffer(data)) if (!Buffer.isBuffer(data))
return false; return false;
@ -368,14 +350,12 @@ HDPrivateKey.isRaw = function isRaw(data) {
version = data.readUInt32BE(0, true); version = data.readUInt32BE(0, true);
for (i = 0; i < networks.types.length; i++) { try {
type = networks.types[i]; Network.fromPrivate(version, network);
prefix = networks[type].keyPrefix.xprivkey; return true;
if (version === prefix) } catch (e) {
return type; return false;
} }
return false;
}; };
/** /**
@ -404,13 +384,13 @@ HDPrivateKey.isValidPath = function isValidPath(path) {
* @throws Error if `path` is not a valid 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 indexes = common.parsePath(path, common.MAX_INDEX);
var key = this; var key = this;
var i; var i;
for (i = 0; i < indexes.length; i++) for (i = 0; i < indexes.length; i++)
key = key.derive(indexes[i], cache); key = key.derive(indexes[i]);
return key; return key;
}; };
@ -530,8 +510,7 @@ HDPrivateKey.fromSeed = function fromSeed(seed, network) {
HDPrivateKey.prototype.fromMnemonic = function fromMnemonic(mnemonic, network) { HDPrivateKey.prototype.fromMnemonic = function fromMnemonic(mnemonic, network) {
assert(mnemonic instanceof Mnemonic); assert(mnemonic instanceof Mnemonic);
this.fromSeed(mnemonic.toSeed(), network); return this.fromSeed(mnemonic.toSeed(), network);
return this;
}; };
/** /**
@ -622,42 +601,31 @@ HDPrivateKey.generate = function generate(network) {
*/ */
HDPrivateKey.prototype.fromBase58 = function fromBase58(xkey, network) { HDPrivateKey.prototype.fromBase58 = function fromBase58(xkey, network) {
this.fromRaw(base58.decode(xkey)); assert(typeof xkey === 'string');
this._xprivkey = xkey; this._xprivkey = xkey;
if (network && !this.verifyNetwork(network)) return this.fromRaw(base58.decode(xkey), network);
throw new Error('Network mismatch for HD private key.');
return this;
}; };
/** /**
* Inject properties from serialized data. * Inject properties from serialized data.
* @private * @private
* @param {Buffer} raw * @param {Buffer} raw
* @param {(Network|NetworkType)?} network
*/ */
HDPrivateKey.prototype.fromReader = function fromReader(br) { HDPrivateKey.prototype.fromReader = function fromReader(br, network) {
var i, version, type, prefix; var version = br.readU32BE();
version = br.readU32BE(); this.network = Network.fromPrivate(version, network);
this.depth = br.readU8(); this.depth = br.readU8();
this.parentFingerPrint = br.readBytes(4); this.parentFingerPrint = br.readBytes(4);
this.childIndex = br.readU32BE(); this.childIndex = br.readU32BE();
this.chainCode = br.readBytes(32); this.chainCode = br.readBytes(32);
br.readU8(); br.readU8();
this.privateKey = br.readBytes(32); 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.publicKey = ec.publicKeyCreate(this.privateKey, true);
this.network = Network.get(type);
br.verifyChecksum();
return this; return this;
}; };
@ -666,10 +634,11 @@ HDPrivateKey.prototype.fromReader = function fromReader(br) {
* Inject properties from serialized data. * Inject properties from serialized data.
* @private * @private
* @param {Buffer} raw * @param {Buffer} raw
* @param {(Network|NetworkType)?} network
*/ */
HDPrivateKey.prototype.fromRaw = function fromRaw(raw) { HDPrivateKey.prototype.fromRaw = function fromRaw(raw, network) {
return this.fromReader(new BufferReader(raw)); return this.fromReader(new BufferReader(raw), network);
}; };
/** /**
@ -739,21 +708,23 @@ HDPrivateKey.fromBase58 = function fromBase58(xkey, network) {
/** /**
* Instantiate key from buffer reader. * Instantiate key from buffer reader.
* @param {BufferReader} br * @param {BufferReader} br
* @param {(Network|NetworkType)?} network
* @returns {HDPrivateKey} * @returns {HDPrivateKey}
*/ */
HDPrivateKey.fromReader = function fromReader(br) { HDPrivateKey.fromReader = function fromReader(br, network) {
return new HDPrivateKey().fromReader(br); return new HDPrivateKey().fromReader(br, network);
}; };
/** /**
* Instantiate key from serialized data. * Instantiate key from serialized data.
* @param {Buffer} raw * @param {Buffer} raw
* @param {(Network|NetworkType)?} network
* @returns {HDPrivateKey} * @returns {HDPrivateKey}
*/ */
HDPrivateKey.fromRaw = function fromRaw(raw) { HDPrivateKey.fromRaw = function fromRaw(raw, network) {
return new HDPrivateKey().fromRaw(raw); return new HDPrivateKey().fromRaw(raw, network);
}; };
/** /**
@ -803,7 +774,7 @@ HDPrivateKey.isHDPrivateKey = function isHDPrivateKey(obj) {
return obj return obj
&& typeof obj.derive === 'function' && typeof obj.derive === 'function'
&& typeof obj.fromMnemonic === 'function' && typeof obj.fromMnemonic === 'function'
&& obj.chainCode !== undefined; && Buffer.isBuffer(obj.chainCode);
}; };
/* /*

View File

@ -10,7 +10,6 @@ var assert = require('assert');
var util = require('../utils/util'); var util = require('../utils/util');
var crypto = require('../crypto/crypto'); var crypto = require('../crypto/crypto');
var ec = require('../crypto/ec'); var ec = require('../crypto/ec');
var networks = require('../protocol/networks');
var Network = require('../protocol/network'); var Network = require('../protocol/network');
var StaticWriter = require('../utils/staticwriter'); var StaticWriter = require('../utils/staticwriter');
var BufferReader = require('../utils/reader'); var BufferReader = require('../utils/reader');
@ -121,18 +120,6 @@ HDPublicKey.prototype.xpubkey = function() {
return this._xpubkey; 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). * Destroy the key (zeroes chain code and pubkey).
*/ */
@ -162,19 +149,11 @@ HDPublicKey.prototype.destroy = function destroy() {
* @throws on `hardened` * @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; 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') if (typeof index === 'string')
return this.derivePath(index, cache); return this.derivePath(index);
if (index >= common.HARDENED || hardened) if (index >= common.HARDENED || hardened)
throw new Error('Index out of range.'); throw new Error('Index out of range.');
@ -185,12 +164,11 @@ HDPublicKey.prototype.derive = function derive(index, hardened, cache) {
if (this.depth >= 0xff) if (this.depth >= 0xff)
throw new Error('Depth too high.'); throw new Error('Depth too high.');
if (cache) { id = this.getID(index);
id = this.getID(index); child = common.cache.get(id);
child = cache.get(id);
if (child) if (child)
return child; return child;
}
bw = new StaticWriter(37); bw = new StaticWriter(37);
bw.writeBytes(this.publicKey); bw.writeBytes(this.publicKey);
@ -204,7 +182,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened, cache) {
try { try {
key = ec.publicKeyTweakAdd(this.publicKey, left, true); key = ec.publicKeyTweakAdd(this.publicKey, left, true);
} catch (e) { } catch (e) {
return this.derive(index + 1, cache); return this.derive(index + 1);
} }
if (!this.fingerPrint) if (!this.fingerPrint)
@ -218,8 +196,7 @@ HDPublicKey.prototype.derive = function derive(index, hardened, cache) {
child.chainCode = right; child.chainCode = right;
child.publicKey = key; child.publicKey = key;
if (cache) common.cache.set(id, child);
cache.set(id, child);
return child; return child;
}; };
@ -320,13 +297,13 @@ HDPublicKey.isValidPath = function isValidPath(path) {
* @throws Error if hardened. * @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 indexes = common.parsePath(path, common.HARDENED);
var key = this; var key = this;
var i; var i;
for (i = 0; i < indexes.length; i++) for (i = 0; i < indexes.length; i++)
key = key.derive(indexes[i], cache); key = key.derive(indexes[i]);
return key; return key;
}; };
@ -427,47 +404,53 @@ HDPublicKey.fromJSON = function fromJSON(json, network) {
/** /**
* Test whether an object is in the form of a base58 xpubkey. * Test whether an object is in the form of a base58 xpubkey.
* @param {String} data * @param {String} data
* @param {(Network|NetworkType)?} network
* @returns {Boolean} * @returns {Boolean}
*/ */
HDPublicKey.isBase58 = function isBase58(data) { HDPublicKey.isBase58 = function isBase58(data, network) {
var i, type, prefix; var prefix;
if (typeof data !== 'string') if (typeof data !== 'string')
return false; return false;
for (i = 0; i < networks.types.length; i++) { if (data.length < 4)
type = networks.types[i]; return false;
prefix = networks[type].keyPrefix.xpubkey58;
if (data.indexOf(prefix) === 0)
return true;
}
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. * Test whether a buffer has a valid network prefix.
* @param {Buffer} data * @param {Buffer} data
* @param {(Network|NetworkType)?} network
* @returns {NetworkType} * @returns {NetworkType}
*/ */
HDPublicKey.isRaw = function isRaw(data) { HDPublicKey.isRaw = function isRaw(data, network) {
var i, version, prefix, type; var version;
if (!Buffer.isBuffer(data)) if (!Buffer.isBuffer(data))
return false; return false;
if (data.length < 4)
return false;
version = data.readUInt32BE(0, true); version = data.readUInt32BE(0, true);
for (i = 0; i < networks.types.length; i++) { try {
type = networks.types[i]; Network.fromPublic(version, network);
prefix = networks[type].keyPrefix.xpubkey; return true;
if (version === prefix) } catch (e) {
return type; return false;
} }
return false;
}; };
/** /**
@ -478,41 +461,30 @@ HDPublicKey.isRaw = function isRaw(data) {
*/ */
HDPublicKey.prototype.fromBase58 = function fromBase58(xkey, network) { HDPublicKey.prototype.fromBase58 = function fromBase58(xkey, network) {
this.fromRaw(base58.decode(xkey)); assert(typeof xkey === 'string');
this._xpubkey = xkey; this._xpubkey = xkey;
if (network && !this.verifyNetwork(network)) return this.fromRaw(base58.decode(xkey), network);
throw new Error('Network mismatch for HD public key.');
return this;
}; };
/** /**
* Inject properties from serialized data. * Inject properties from serialized data.
* @private * @private
* @param {Buffer} raw * @param {Buffer} raw
* @param {(Network|NetworkType)?} network
*/ */
HDPublicKey.prototype.fromReader = function fromReader(br) { HDPublicKey.prototype.fromReader = function fromReader(br, network) {
var i, version, type, prefix; var version = br.readU32BE();
version = br.readU32BE(); this.network = Network.fromPublic(version, network);
this.depth = br.readU8(); this.depth = br.readU8();
this.parentFingerPrint = br.readBytes(4); this.parentFingerPrint = br.readBytes(4);
this.childIndex = br.readU32BE(); this.childIndex = br.readU32BE();
this.chainCode = br.readBytes(32); this.chainCode = br.readBytes(32);
this.publicKey = br.readBytes(33); this.publicKey = br.readBytes(33);
br.verifyChecksum(); 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; return this;
}; };
@ -520,15 +492,16 @@ HDPublicKey.prototype.fromReader = function fromReader(br) {
* Inject properties from serialized data. * Inject properties from serialized data.
* @private * @private
* @param {Buffer} raw * @param {Buffer} raw
* @param {(Network|NetworkType)?} network
*/ */
HDPublicKey.prototype.fromRaw = function fromRaw(raw) { HDPublicKey.prototype.fromRaw = function fromRaw(raw, network) {
return this.fromReader(new BufferReader(raw)); return this.fromReader(new BufferReader(raw), network);
}; };
/** /**
* Serialize key data to base58 extended key. * Serialize key data to base58 extended key.
* @param {Network|String} network * @param {(Network|NetworkType)?} network
* @returns {Base58String} * @returns {Base58String}
*/ */
@ -539,7 +512,7 @@ HDPublicKey.prototype.toBase58 = function toBase58(network) {
/** /**
* Write the key to a buffer writer. * Write the key to a buffer writer.
* @param {BufferWriter} bw * @param {BufferWriter} bw
* @param {Network|NetworkType} network * @param {(Network|NetworkType)?} network
*/ */
HDPublicKey.prototype.toWriter = function toWriter(bw, network) { HDPublicKey.prototype.toWriter = function toWriter(bw, network) {
@ -570,7 +543,7 @@ HDPublicKey.prototype.getSize = function getSize() {
/** /**
* Serialize the key. * Serialize the key.
* @param {Network|NetworkType} network * @param {(Network|NetworkType)?} network
* @returns {Buffer} * @returns {Buffer}
*/ */
@ -592,21 +565,23 @@ HDPublicKey.fromBase58 = function fromBase58(xkey, network) {
/** /**
* Instantiate key from serialized data. * Instantiate key from serialized data.
* @param {BufferReader} br * @param {BufferReader} br
* @param {(Network|NetworkType)?} network
* @returns {HDPublicKey} * @returns {HDPublicKey}
*/ */
HDPublicKey.fromReader = function fromReader(br) { HDPublicKey.fromReader = function fromReader(br, network) {
return new HDPublicKey().fromReader(br); return new HDPublicKey().fromReader(br, network);
}; };
/** /**
* Instantiate key from serialized data. * Instantiate key from serialized data.
* @param {Buffer} raw * @param {Buffer} raw
* @param {(Network|NetworkType)?} network
* @returns {HDPublicKey} * @returns {HDPublicKey}
*/ */
HDPublicKey.fromRaw = function fromRaw(data) { HDPublicKey.fromRaw = function fromRaw(data, network) {
return new HDPublicKey().fromRaw(data); return new HDPublicKey().fromRaw(data, network);
}; };
/** /**
@ -618,8 +593,8 @@ HDPublicKey.fromRaw = function fromRaw(data) {
HDPublicKey.isHDPublicKey = function isHDPublicKey(obj) { HDPublicKey.isHDPublicKey = function isHDPublicKey(obj) {
return obj return obj
&& typeof obj.derive === 'function' && typeof obj.derive === 'function'
&& typeof obj.toExtended !== 'function' && obj.fromMnemonic === undefined
&& obj.chainCode !== undefined; && Buffer.isBuffer(obj.chainCode);
}; };
/* /*

View File

@ -9,7 +9,6 @@
var assert = require('assert'); var assert = require('assert');
var Network = require('../protocol/network'); var Network = require('../protocol/network');
var networks = require('../protocol/networks');
var encoding = require('../utils/encoding'); var encoding = require('../utils/encoding');
var util = require('../utils/util'); var util = require('../utils/util');
var crypto = require('../crypto/crypto'); var crypto = require('../crypto/crypto');
@ -22,12 +21,7 @@ var bech32 = require('../utils/bech32');
* Represents an address. * Represents an address.
* @alias module:primitives.Address * @alias module:primitives.Address
* @constructor * @constructor
* @param {Object} options * @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.
* @property {Buffer} hash * @property {Buffer} hash
* @property {AddressPrefix} type * @property {AddressPrefix} type
* @property {Number} version * @property {Number} version
@ -105,30 +99,6 @@ Address.prototype.getHash = function getHash(enc) {
return this.hash; 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. * Test whether the address is null.
* @returns {Boolean} * @returns {Boolean}
@ -153,13 +123,46 @@ Address.prototype.isNull = function isNull() {
/** /**
* Get the address type as a string. * Get the address type as a string.
* @returns {AddressPrefix} * @returns {String}
*/ */
Address.prototype.getType = function getType() { Address.prototype.getType = function getType() {
return Address.typesByVal[this.type].toLowerCase(); 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. * Calculate size of serialized address.
* @returns {Number} * @returns {Number}
@ -225,7 +228,7 @@ Address.prototype.toBech32 = function toBech32(network) {
var hrp; var hrp;
assert(version !== -1, assert(version !== -1,
'Cannot convert non-segwit address to bech32.'); 'Cannot convert non-program address to bech32.');
if (!network) if (!network)
network = this.network; network = this.network;
@ -245,28 +248,18 @@ Address.prototype.toBech32 = function toBech32(network) {
*/ */
Address.prototype.fromString = function fromString(addr, network) { Address.prototype.fromString = function fromString(addr, network) {
var i, type, hrp, expect; var hrp;
assert(typeof addr === 'string'); assert(typeof addr === 'string');
assert(addr.length >= 2);
hrp = addr.substring(0, 2).toLowerCase(); try {
hrp = addr.substring(0, 2).toLowerCase();
if (network) { network = Network.fromBech32(hrp, network);
network = Network.get(network); } catch (e) {
expect = network.addressPrefix.bech32; return this.fromBase58(addr, network);
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);
}
} }
return this.fromBase58(addr, network); return this.fromBech32(addr, network);
}; };
/** /**
@ -312,23 +305,16 @@ Address.prototype.inspect = function inspect() {
* @throws Parse error * @throws Parse error
*/ */
Address.prototype.fromRaw = function fromRaw(data) { Address.prototype.fromRaw = function fromRaw(data, network) {
var br = new BufferReader(data, true); var br = new BufferReader(data, true);
var i, prefix, network, type, version, hash; var prefix, type, version, hash;
if (data.length > 40) if (data.length > 40)
throw new Error('Address is too long.'); throw new Error('Address is too long.');
prefix = br.readU8(); prefix = br.readU8();
network = Network.fromAddress(prefix, network);
for (i = 0; i < networks.types.length; i++) { type = Address.getType(prefix, network);
network = networks[networks.types[i]];
type = Address.getType(prefix, network);
if (type !== -1)
break;
}
assert(i < networks.types.length, 'Unknown address prefix.');
if (data.length > 25) { if (data.length > 25) {
version = br.readU8(); version = br.readU8();
@ -351,87 +337,69 @@ Address.prototype.fromRaw = function fromRaw(data) {
* @throws Parse error. * @throws Parse error.
*/ */
Address.fromRaw = function fromRaw(data) { Address.fromRaw = function fromRaw(data, network) {
return new Address().fromRaw(data); return new Address().fromRaw(data, network);
}; };
/** /**
* Inject properties from base58 address. * Inject properties from base58 address.
* @private * @private
* @param {Base58Address} str * @param {Base58Address} data
* @param {Network?} network * @param {Network?} network
* @throws Parse error * @throws Parse error
*/ */
Address.prototype.fromBase58 = function fromBase58(str, network) { Address.prototype.fromBase58 = function fromBase58(data, network) {
assert(typeof str === 'string'); assert(typeof data === 'string');
if (str.length > 55) if (data.length > 55)
throw new Error('Address is too long.'); throw new Error('Address is too long.');
this.fromRaw(base58.decode(str)); return this.fromRaw(base58.decode(data), network);
if (network && !this.verifyNetwork(network))
throw new Error('Network mismatch for address.');
return this;
}; };
/** /**
* Create an address object from a base58 address. * Create an address object from a base58 address.
* @param {Base58Address} str * @param {Base58Address} data
* @param {Network?} network * @param {Network?} network
* @returns {Address} * @returns {Address}
* @throws Parse error. * @throws Parse error.
*/ */
Address.fromBase58 = function fromBase58(str, network) { Address.fromBase58 = function fromBase58(data, network) {
return new Address().fromBase58(str, network); return new Address().fromBase58(data, network);
}; };
/** /**
* Inject properties from bech32 address. * Inject properties from bech32 address.
* @private * @private
* @param {String} str * @param {String} data
* @param {Network?} network * @param {Network?} network
* @throws Parse error * @throws Parse error
*/ */
Address.prototype.fromBech32 = function fromBech32(str, network) { Address.prototype.fromBech32 = function fromBech32(data, network) {
var type = Address.types.WITNESS; 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) { return this.fromHash(addr.hash, type, addr.version, 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);
}; };
/** /**
* Create an address object from a bech32 address. * Create an address object from a bech32 address.
* @param {String} str * @param {String} data
* @param {Network?} network * @param {Network?} network
* @returns {Address} * @returns {Address}
* @throws Parse error. * @throws Parse error.
*/ */
Address.fromBech32 = function fromBech32(str, network) { Address.fromBech32 = function fromBech32(data, network) {
return new Address().fromBech32(str, network); return new Address().fromBech32(data, network);
}; };
/** /**
@ -877,37 +845,11 @@ Address.getHash = function getHash(data, enc) {
: hash; : 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. * Get an address type for a specified network address prefix.
* @param {Number} prefix * @param {Number} prefix
* @param {Network} network * @param {Network} network
* @returns {AddressPrefix} * @returns {AddressType}
*/ */
Address.getType = function getType(prefix, network) { Address.getType = function getType(prefix, network) {
@ -922,7 +864,7 @@ Address.getType = function getType(prefix, network) {
case prefixes.witnessscripthash: case prefixes.witnessscripthash:
return Address.types.WITNESS; return Address.types.WITNESS;
default: default:
return -1; throw new Error('Unknown address prefix.');
} }
}; };

View File

@ -11,7 +11,6 @@ var assert = require('assert');
var util = require('../utils/util'); var util = require('../utils/util');
var encoding = require('../utils/encoding'); var encoding = require('../utils/encoding');
var crypto = require('../crypto/crypto'); var crypto = require('../crypto/crypto');
var networks = require('../protocol/networks');
var Network = require('../protocol/network'); var Network = require('../protocol/network');
var BufferReader = require('../utils/reader'); var BufferReader = require('../utils/reader');
var StaticWriter = require('../utils/staticwriter'); var StaticWriter = require('../utils/staticwriter');
@ -124,7 +123,7 @@ KeyRing.prototype.refresh = function refresh() {
* @private * @private
* @param {Buffer} key * @param {Buffer} key
* @param {Boolean?} compressed * @param {Boolean?} compressed
* @param {(NetworkType|Network)} network * @param {(NetworkType|Network)?} network
*/ */
KeyRing.prototype.fromPrivate = function fromPrivate(key, compressed, 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. * Instantiate keyring from a private key.
* @param {Buffer} key * @param {Buffer} key
* @param {Boolean?} compressed * @param {Boolean?} compressed
* @param {(NetworkType|Network)} network * @param {(NetworkType|Network)?} network
* @returns {KeyRing} * @returns {KeyRing}
*/ */
@ -159,7 +158,7 @@ KeyRing.fromPrivate = function fromPrivate(key, compressed, network) {
* Inject data from public key. * Inject data from public key.
* @private * @private
* @param {Buffer} key * @param {Buffer} key
* @param {(NetworkType|Network)} network * @param {(NetworkType|Network)?} network
*/ */
KeyRing.prototype.fromPublic = function fromPublic(key, network) { KeyRing.prototype.fromPublic = function fromPublic(key, network) {
@ -203,7 +202,7 @@ KeyRing.generate = function(compressed, network) {
/** /**
* Instantiate keyring from a public key. * Instantiate keyring from a public key.
* @param {Buffer} publicKey * @param {Buffer} publicKey
* @param {(NetworkType|Network)} network * @param {(NetworkType|Network)?} network
* @returns {KeyRing} * @returns {KeyRing}
*/ */
@ -215,7 +214,7 @@ KeyRing.fromPublic = function fromPublic(key, network) {
* Inject data from public key. * Inject data from public key.
* @private * @private
* @param {Buffer} privateKey * @param {Buffer} privateKey
* @param {(NetworkType|Network)} network * @param {(NetworkType|Network)?} network
*/ */
KeyRing.prototype.fromKey = function fromKey(key, compressed, 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. * Instantiate keyring from a public key.
* @param {Buffer} publicKey * @param {Buffer} publicKey
* @param {(NetworkType|Network)} network * @param {(NetworkType|Network)?} network
* @returns {KeyRing} * @returns {KeyRing}
*/ */
@ -248,7 +247,7 @@ KeyRing.fromKey = function fromKey(key, compressed, network) {
* @private * @private
* @param {Buffer} key * @param {Buffer} key
* @param {Script} script * @param {Script} script
* @param {(NetworkType|Network)} network * @param {(NetworkType|Network)?} network
*/ */
KeyRing.prototype.fromScript = function fromScript(key, script, compressed, 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. * Instantiate keyring from script.
* @param {Buffer} key * @param {Buffer} key
* @param {Script} script * @param {Script} script
* @param {(NetworkType|Network)} network * @param {(NetworkType|Network)?} network
* @returns {KeyRing} * @returns {KeyRing}
*/ */
@ -302,13 +301,18 @@ KeyRing.prototype.getSecretSize = function getSecretSize() {
* @returns {Base58String} * @returns {Base58String}
*/ */
KeyRing.prototype.toSecret = function toSecret() { KeyRing.prototype.toSecret = function toSecret(network) {
var size = this.getSecretSize(); var size = this.getSecretSize();
var bw = new StaticWriter(size); var bw = new StaticWriter(size);
assert(this.privateKey, 'Cannot serialize without private key.'); 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); bw.writeBytes(this.privateKey);
if (this.publicKey.length === 33) if (this.publicKey.length === 33)
@ -323,23 +327,15 @@ KeyRing.prototype.toSecret = function toSecret() {
* Inject properties from serialized CBitcoinSecret. * Inject properties from serialized CBitcoinSecret.
* @private * @private
* @param {Base58String} secret * @param {Base58String} secret
* @param {Network?} network * @param {(Network|NetworkType)?} network
*/ */
KeyRing.prototype.fromSecret = function fromSecret(data, network) { KeyRing.prototype.fromSecret = function fromSecret(data, network) {
var br = new BufferReader(base58.decode(data), true); var br = new BufferReader(base58.decode(data), true);
var i, prefix, version, type, key, compressed; var version, key, compressed;
version = br.readU8(); version = br.readU8();
network = Network.fromWIF(version, network);
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.');
key = br.readBytes(32); key = br.readBytes(32);
@ -352,18 +348,13 @@ KeyRing.prototype.fromSecret = function fromSecret(data, network) {
br.verifyChecksum(); br.verifyChecksum();
this.fromPrivate(key, compressed, type); return this.fromPrivate(key, compressed, network);
if (network && !this.verifyNetwork(network))
throw new Error('Network mismatch for WIF.');
return this;
}; };
/** /**
* Instantiate a keyring from a serialized CBitcoinSecret. * Instantiate a keyring from a serialized CBitcoinSecret.
* @param {Base58String} secret * @param {Base58String} secret
* @param {Network?} network * @param {(Network|NetworkType)?} network
* @returns {KeyRing} * @returns {KeyRing}
*/ */
@ -371,17 +362,6 @@ KeyRing.fromSecret = function fromSecret(data, network) {
return new KeyRing().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. * Get private key.
* @param {String?} enc - Can be `"hex"`, `"base58"`, or `null`. * @param {String?} enc - Can be `"hex"`, `"base58"`, or `null`.

View File

@ -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} * @returns {Network}
*/ */
Network.fromMagic = function fromMagic(magic) { Network.by = function by(value, compare, network, name) {
var i, type; 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++) { for (i = 0; i < networks.types.length; i++) {
type = networks.types[i]; type = networks.types[i];
if (magic === networks[type].magic) network = networks[type];
break; 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; 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 * Expose
*/ */