hd rewrite.
This commit is contained in:
parent
2dede909c3
commit
c9bdad9bdf
806
lib/bcoin/hd.js
806
lib/bcoin/hd.js
@ -59,6 +59,8 @@ var constants = bcoin.protocol.constants;
|
|||||||
var network = bcoin.protocol.network;
|
var network = bcoin.protocol.network;
|
||||||
var KeyPair = require('./keypair');
|
var KeyPair = require('./keypair');
|
||||||
var LRU = require('./lru');
|
var LRU = require('./lru');
|
||||||
|
var BufferWriter = require('./writer');
|
||||||
|
var BufferReader = require('./reader');
|
||||||
|
|
||||||
var english = require('../../etc/english.json');
|
var english = require('../../etc/english.json');
|
||||||
|
|
||||||
@ -91,7 +93,10 @@ HDSeed.prototype.createSeed = function createSeed() {
|
|||||||
if (!this.mnemonic)
|
if (!this.mnemonic)
|
||||||
this.mnemonic = this.createMnemonic(this.entropy);
|
this.mnemonic = this.createMnemonic(this.entropy);
|
||||||
|
|
||||||
this.seed = utils.pbkdf2(this.mnemonic, 'mnemonic' + this.passphrase, 2048, 64);
|
this.seed = utils.pbkdf2(
|
||||||
|
this.mnemonic,
|
||||||
|
'mnemonic' + this.passphrase,
|
||||||
|
2048, 64);
|
||||||
|
|
||||||
return this.seed;
|
return this.seed;
|
||||||
};
|
};
|
||||||
@ -126,22 +131,29 @@ function HD(options) {
|
|||||||
return new HDPrivateKey(options);
|
return new HDPrivateKey(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
HD.generate = function generate(privateKey, entropy) {
|
HD.fromBase58 = function fromBase58(xkey) {
|
||||||
return HDPrivateKey.generate(privateKey, entropy);
|
if (HDPrivateKey.isExtended(xkey))
|
||||||
|
return HDPrivateKey.fromBase58(xkey);
|
||||||
|
return HDPublicKey.fromBase58(xkey);
|
||||||
};
|
};
|
||||||
|
|
||||||
HD.fromSeed = function fromSeed(options) {
|
HD.generate = function generate(options, networkType) {
|
||||||
return HDPrivateKey.fromSeed(options);
|
return HDPrivateKey.generate(options, networkType);
|
||||||
|
};
|
||||||
|
|
||||||
|
HD.fromSeed = function fromSeed(options, networkType) {
|
||||||
|
return HDPrivateKey.fromSeed(options, networkType);
|
||||||
};
|
};
|
||||||
|
|
||||||
HD.cache = new LRU(500);
|
HD.cache = new LRU(500);
|
||||||
|
|
||||||
HD.isHD = function isHD(obj) {
|
HD.isHD = function isHD(obj) {
|
||||||
return HDPrivateKey.isHDPrivateKey(obj) || HDPublicKey.isHDPublicKey(obj);
|
return HDPrivateKey.isHDPrivateKey(obj)
|
||||||
|
|| HDPublicKey.isHDPublicKey(obj);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HD Private Key
|
* HDPrivateKey
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function HDPrivateKey(options) {
|
function HDPrivateKey(options) {
|
||||||
@ -150,11 +162,10 @@ function HDPrivateKey(options) {
|
|||||||
if (!(this instanceof HDPrivateKey))
|
if (!(this instanceof HDPrivateKey))
|
||||||
return new HDPrivateKey(options);
|
return new HDPrivateKey(options);
|
||||||
|
|
||||||
|
assert(options, 'No options for HD private key.');
|
||||||
assert(!(options instanceof HDPrivateKey));
|
assert(!(options instanceof HDPrivateKey));
|
||||||
assert(!(options instanceof HDPublicKey));
|
assert(!(options instanceof HDPublicKey));
|
||||||
|
|
||||||
assert(options);
|
|
||||||
|
|
||||||
if (HDPrivateKey.isExtended(options))
|
if (HDPrivateKey.isExtended(options))
|
||||||
options = { xkey: options };
|
options = { xkey: options };
|
||||||
|
|
||||||
@ -167,39 +178,125 @@ function HDPrivateKey(options) {
|
|||||||
if (HDPublicKey.isExtended(options.xkey))
|
if (HDPublicKey.isExtended(options.xkey))
|
||||||
return new HDPublicKey(options);
|
return new HDPublicKey(options);
|
||||||
|
|
||||||
this.network = options.network || network.type;
|
this.networkType = options.networkType || network.type;
|
||||||
|
this.xprivkey = options.xkey;
|
||||||
this.seed = options.seed;
|
this.seed = options.seed;
|
||||||
|
|
||||||
if (options.xkey) {
|
if (options.data) {
|
||||||
data = this._unbuild(options.xkey);
|
|
||||||
} else if (options.seed) {
|
|
||||||
data = this._seed(options.seed);
|
|
||||||
} else {
|
|
||||||
data = options.data;
|
data = options.data;
|
||||||
|
} else if (options.xkey) {
|
||||||
|
data = HDPrivateKey.parse(options.xkey);
|
||||||
|
this.networkType = data.networkType;
|
||||||
|
data = data.data;
|
||||||
|
} else if (options.seed) {
|
||||||
|
data = HDPrivateKey._fromSeed(options.seed, this.networkType);
|
||||||
|
} else {
|
||||||
|
assert(false, 'No data passed to HD key.');
|
||||||
}
|
}
|
||||||
assert(data);
|
|
||||||
|
|
||||||
data = this._normalize(data);
|
assert(data.depth <= 0xff, 'Depth is too high.');
|
||||||
|
|
||||||
this.data = data;
|
this.version = data.version;
|
||||||
|
this.depth = data.depth;
|
||||||
|
this.parentFingerPrint = data.parentFingerPrint;
|
||||||
|
this.childIndex = data.childIndex;
|
||||||
|
this.chainCode = data.chainCode;
|
||||||
|
this.privateKey = data.privateKey;
|
||||||
|
|
||||||
this._build(data);
|
this.publicKey = ec.publicKeyCreate(data.privateKey, true);
|
||||||
|
this.fingerPrint = null;
|
||||||
|
|
||||||
if (utils.readU32BE(data.parentFingerPrint) === 0)
|
this.hdPrivateKey = this;
|
||||||
this.isMaster = true;
|
|
||||||
else
|
if (!this.xprivkey)
|
||||||
this.isMaster = false;
|
this.xprivkey = HDPrivateKey.render(data);
|
||||||
|
|
||||||
this.isPrivate = true;
|
this.isPrivate = true;
|
||||||
this.isPublic = false;
|
this.isPublic = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
HDPublicKey.isHDPrivateKey = function isHDPrivateKey(obj) {
|
|
||||||
return obj && obj.isPrivate && typeof obj._unbuild === 'function';
|
|
||||||
};
|
|
||||||
|
|
||||||
utils.inherits(HDPrivateKey, HD);
|
utils.inherits(HDPrivateKey, HD);
|
||||||
|
|
||||||
|
HDPrivateKey.prototype.__defineGetter__('hdPublicKey', function() {
|
||||||
|
if (!this._hdPublicKey) {
|
||||||
|
this._hdPublicKey = new HDPublicKey({
|
||||||
|
networkType: this.networkType,
|
||||||
|
data: {
|
||||||
|
version: network[this.networkType].prefixes.xpubkey,
|
||||||
|
depth: this.depth,
|
||||||
|
parentFingerPrint: this.parentFingerPrint,
|
||||||
|
childIndex: this.childIndex,
|
||||||
|
chainCode: this.chainCode,
|
||||||
|
publicKey: this.publicKey
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return this._hdPublicKey;
|
||||||
|
});
|
||||||
|
|
||||||
|
HDPrivateKey.prototype.__defineGetter__('xpubkey', function() {
|
||||||
|
return this.hdPublicKey.xpubkey;
|
||||||
|
});
|
||||||
|
|
||||||
|
HDPrivateKey.prototype.derive = function derive(index, hardened) {
|
||||||
|
var cached, p, data, hash, leftPart, chainCode, privateKey, child;
|
||||||
|
|
||||||
|
if (typeof index === 'string')
|
||||||
|
return this.derivePath(index);
|
||||||
|
|
||||||
|
cached = HD.cache.get(this.xprivkey + '/' + index);
|
||||||
|
|
||||||
|
if (cached)
|
||||||
|
return cached;
|
||||||
|
|
||||||
|
hardened = index >= constants.hd.hardened ? true : hardened;
|
||||||
|
if (index < constants.hd.hardened && hardened)
|
||||||
|
index += constants.hd.hardened;
|
||||||
|
|
||||||
|
p = new BufferWriter();
|
||||||
|
|
||||||
|
if (hardened) {
|
||||||
|
p.writeU8(0);
|
||||||
|
p.writeBytes(this.privateKey);
|
||||||
|
p.writeU32BE(index);
|
||||||
|
} else {
|
||||||
|
p.writeBytes(this.publicKey);
|
||||||
|
p.writeU32BE(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
data = p.render();
|
||||||
|
|
||||||
|
hash = utils.sha512hmac(data, this.chainCode);
|
||||||
|
leftPart = new bn(hash.slice(0, 32));
|
||||||
|
chainCode = hash.slice(32, 64);
|
||||||
|
|
||||||
|
privateKey = leftPart
|
||||||
|
.add(new bn(this.privateKey))
|
||||||
|
.mod(ec.elliptic.curve.n)
|
||||||
|
.toBuffer('be', 32);
|
||||||
|
|
||||||
|
if (!this.fingerPrint) {
|
||||||
|
this.fingerPrint = utils.ripesha(this.publicKey)
|
||||||
|
.slice(0, constants.hd.parentFingerPrintSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
child = new HDPrivateKey({
|
||||||
|
networkType: this.networkType,
|
||||||
|
data: {
|
||||||
|
version: this.version,
|
||||||
|
depth: this.depth + 1,
|
||||||
|
parentFingerPrint: this.fingerPrint,
|
||||||
|
childIndex: index,
|
||||||
|
chainCode: chainCode,
|
||||||
|
privateKey: privateKey
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
HD.cache.set(this.xprivkey + '/' + index, child);
|
||||||
|
|
||||||
|
return child;
|
||||||
|
};
|
||||||
|
|
||||||
HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(options) {
|
HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(options) {
|
||||||
var coinType, accountIndex, child;
|
var coinType, accountIndex, child;
|
||||||
|
|
||||||
@ -215,7 +312,7 @@ HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (coinType == null)
|
if (coinType == null)
|
||||||
coinType = network[this.network].type === 'main' ? 0 : 1;
|
coinType = this.networkType === 'main' ? 0 : 1;
|
||||||
|
|
||||||
assert(utils.isFinite(coinType));
|
assert(utils.isFinite(coinType));
|
||||||
assert(utils.isFinite(accountIndex));
|
assert(utils.isFinite(accountIndex));
|
||||||
@ -246,15 +343,15 @@ HDPrivateKey.prototype.derivePurpose45 = function derivePurpose45() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.prototype.isPurpose45 = function isPurpose45() {
|
HDPrivateKey.prototype.isPurpose45 = function isPurpose45() {
|
||||||
if (utils.readU8(this.depth) !== 1)
|
if (this.depth !== 1)
|
||||||
return false;
|
return false;
|
||||||
return utils.readU32BE(this.childIndex) === constants.hd.hardened + 45;
|
return this.childIndex === constants.hd.hardened + 45;
|
||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.prototype.isAccount44 = function isAccount44() {
|
HDPrivateKey.prototype.isAccount44 = function isAccount44() {
|
||||||
if (utils.readU32BE(this.childIndex) < constants.hd.hardened)
|
if (this.childIndex < constants.hd.hardened)
|
||||||
return false;
|
return false;
|
||||||
return utils.readU8(this.depth) === 3;
|
return this.depth === 3;
|
||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.isExtended = function isExtended(data) {
|
HDPrivateKey.isExtended = function isExtended(data) {
|
||||||
@ -264,258 +361,6 @@ HDPrivateKey.isExtended = function isExtended(data) {
|
|||||||
return network.xprivkeys[data.slice(0, 4)];
|
return network.xprivkeys[data.slice(0, 4)];
|
||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.prototype._normalize = function _normalize(data) {
|
|
||||||
if (!data.version) {
|
|
||||||
data.version = (this instanceof HDPrivateKey)
|
|
||||||
? network[this.network].prefixes.xprivkey
|
|
||||||
: network[this.network].prefixes.xpubkey;
|
|
||||||
}
|
|
||||||
|
|
||||||
// version = uint_32be
|
|
||||||
if (typeof data.version === 'number')
|
|
||||||
data.version = array32(data.version);
|
|
||||||
|
|
||||||
// depth = unsigned char
|
|
||||||
if (typeof data.depth === 'number')
|
|
||||||
data.depth = new Buffer([data.depth]);
|
|
||||||
|
|
||||||
if (data.depth.length > 1)
|
|
||||||
throw new Error('Depth is too high');
|
|
||||||
|
|
||||||
// parent finger print = uint_32be
|
|
||||||
if (typeof data.parentFingerPrint === 'number')
|
|
||||||
data.parentFingerPrint = array32(data.parentFingerPrint);
|
|
||||||
|
|
||||||
// child index = uint_32be
|
|
||||||
if (typeof data.childIndex === 'number')
|
|
||||||
data.childIndex = array32(data.childIndex);
|
|
||||||
|
|
||||||
// chain code = 32 bytes
|
|
||||||
if (typeof data.chainCode === 'string')
|
|
||||||
data.chainCode = new Buffer(data.chainCode, 'hex');
|
|
||||||
|
|
||||||
// checksum = 4 bytes
|
|
||||||
if (typeof data.checksum === 'string')
|
|
||||||
data.checksum = new Buffer(data.checksum, 'hex');
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPrivateKey.prototype._seed = function _seed(seed) {
|
|
||||||
var hash;
|
|
||||||
|
|
||||||
if (seed instanceof HDSeed)
|
|
||||||
seed = seed.createSeed();
|
|
||||||
|
|
||||||
if (utils.isHex(seed))
|
|
||||||
seed = new Buffer(seed, 'hex');
|
|
||||||
|
|
||||||
if (seed.length < constants.hd.minEntropy
|
|
||||||
|| seed.length > constants.hd.maxEntropy) {
|
|
||||||
throw new Error('entropy not in range');
|
|
||||||
}
|
|
||||||
|
|
||||||
hash = utils.sha512hmac(seed, 'Bitcoin seed');
|
|
||||||
|
|
||||||
return {
|
|
||||||
version: array32(network[this.network].prefixes.xprivkey),
|
|
||||||
depth: new Buffer([0]),
|
|
||||||
parentFingerPrint: new Buffer([0, 0, 0, 0]),
|
|
||||||
childIndex: new Buffer([0, 0, 0, 0]),
|
|
||||||
chainCode: hash.slice(32, 64),
|
|
||||||
privateKey: hash.slice(0, 32),
|
|
||||||
checksum: null
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPrivateKey.fromSeed = function fromSeed(options) {
|
|
||||||
var seed = (options instanceof HDSeed) ? options : new HDSeed(options);
|
|
||||||
return new HDPrivateKey({ seed: seed });
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPrivateKey._generate = function _generate(privateKey, entropy) {
|
|
||||||
if (!privateKey)
|
|
||||||
privateKey = ec.generatePrivateKey();
|
|
||||||
|
|
||||||
if (!entropy)
|
|
||||||
entropy = ec.random(32);
|
|
||||||
|
|
||||||
return {
|
|
||||||
version: array32(network.prefixes.xprivkey),
|
|
||||||
depth: new Buffer([0]),
|
|
||||||
parentFingerPrint: new Buffer([0, 0, 0, 0]),
|
|
||||||
childIndex: new Buffer([0, 0, 0, 0]),
|
|
||||||
chainCode: entropy,
|
|
||||||
privateKey: privateKey,
|
|
||||||
checksum: null
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPrivateKey.generate = function generate(privateKey, entropy) {
|
|
||||||
return new HDPrivateKey(HDPrivateKey._generate(privateKey, entropy));
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPrivateKey.prototype._generate = function _generate(privateKey, entropy) {
|
|
||||||
var data = HDPrivateKey._generate(privateKey, entropy);
|
|
||||||
data.version = array32(network[this.network].prefixes.xprivkey);
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPrivateKey.prototype._unbuild = function _unbuild(xkey) {
|
|
||||||
var raw = utils.fromBase58(xkey);
|
|
||||||
var data = {};
|
|
||||||
var off = 0;
|
|
||||||
var hash;
|
|
||||||
|
|
||||||
data.version = raw.slice(off, off + 4);
|
|
||||||
off += 4;
|
|
||||||
data.depth = raw.slice(off, off + 1);
|
|
||||||
off += 1;
|
|
||||||
data.parentFingerPrint = raw.slice(off, off + 4);
|
|
||||||
off += 4;
|
|
||||||
data.childIndex = raw.slice(off, off + 4);
|
|
||||||
off += 4;
|
|
||||||
data.chainCode = raw.slice(off, off + 32);
|
|
||||||
off += 32;
|
|
||||||
off += 1; // nul byte
|
|
||||||
data.privateKey = raw.slice(off, off + 32);
|
|
||||||
off += 32;
|
|
||||||
data.checksum = raw.slice(off, off + 4);
|
|
||||||
off += 4;
|
|
||||||
|
|
||||||
hash = utils.dsha256(raw.slice(0, -4)).slice(0, 4);
|
|
||||||
if (!utils.isEqual(data.checksum, hash))
|
|
||||||
throw new Error('checksum mismatch');
|
|
||||||
|
|
||||||
network.types.some(function(type) {
|
|
||||||
if (utils.readU32BE(data.version) === network[type].prefixes.xprivkey) {
|
|
||||||
this.network = type;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
this.xprivkey = xkey;
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPrivateKey.prototype._build = function _build(data) {
|
|
||||||
var off = 0;
|
|
||||||
var sequence, checksum;
|
|
||||||
|
|
||||||
if (!this.xprivkey) {
|
|
||||||
sequence = new Buffer(82);
|
|
||||||
off += utils.copy(data.version, sequence, off);
|
|
||||||
off += utils.copy(data.depth, sequence, off);
|
|
||||||
off += utils.copy(data.parentFingerPrint, sequence, off);
|
|
||||||
off += utils.copy(data.childIndex, sequence, off);
|
|
||||||
off += utils.copy(data.chainCode, sequence, off);
|
|
||||||
off += utils.writeU8(sequence, 0, off);
|
|
||||||
off += utils.copy(data.privateKey, sequence, off);
|
|
||||||
assert(off === 78, off);
|
|
||||||
checksum = utils.dsha256(sequence.slice(0, off)).slice(0, 4);
|
|
||||||
off += utils.copy(checksum, sequence, off);
|
|
||||||
assert(off === 82, off);
|
|
||||||
|
|
||||||
this.xprivkey = utils.toBase58(sequence);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.version = data.version;
|
|
||||||
this.depth = data.depth;
|
|
||||||
this.parentFingerPrint = data.parentFingerPrint;
|
|
||||||
this.childIndex = data.childIndex;
|
|
||||||
this.chainCode = data.chainCode;
|
|
||||||
this.privateKey = data.privateKey;
|
|
||||||
this.checksum = null;
|
|
||||||
|
|
||||||
this.publicKey = ec.publicKeyCreate(data.privateKey, true);
|
|
||||||
this.fingerPrint = null;
|
|
||||||
|
|
||||||
this.hdPrivateKey = this;
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPrivateKey.prototype.__defineGetter__('hdPublicKey', function() {
|
|
||||||
if (!this._hdPublicKey) {
|
|
||||||
this._hdPublicKey = new HDPublicKey({
|
|
||||||
network: this.network,
|
|
||||||
data: {
|
|
||||||
version: array32(network[this.network].prefixes.xpubkey),
|
|
||||||
depth: this.depth,
|
|
||||||
parentFingerPrint: this.parentFingerPrint,
|
|
||||||
childIndex: this.childIndex,
|
|
||||||
chainCode: this.chainCode,
|
|
||||||
checksum: this.checksum,
|
|
||||||
publicKey: this.publicKey
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this._hdPublicKey;
|
|
||||||
});
|
|
||||||
|
|
||||||
HDPrivateKey.prototype.__defineGetter__('xpubkey', function() {
|
|
||||||
return this.hdPublicKey.xpubkey;
|
|
||||||
});
|
|
||||||
|
|
||||||
HDPrivateKey.prototype.derive = function derive(index, hardened) {
|
|
||||||
var cached, data, hash, leftPart, chainCode, privateKey, child;
|
|
||||||
var off = 0;
|
|
||||||
|
|
||||||
if (typeof index === 'string')
|
|
||||||
return this.derivePath(index);
|
|
||||||
|
|
||||||
cached = HD.cache.get(this.xprivkey + '/' + index);
|
|
||||||
|
|
||||||
if (cached)
|
|
||||||
return cached;
|
|
||||||
|
|
||||||
hardened = index >= constants.hd.hardened ? true : hardened;
|
|
||||||
if (index < constants.hd.hardened && hardened)
|
|
||||||
index += constants.hd.hardened;
|
|
||||||
|
|
||||||
if (hardened) {
|
|
||||||
data = new Buffer(1 + this.privateKey.length + 4);
|
|
||||||
off += utils.writeU8(data, 0, off);
|
|
||||||
off += utils.copy(this.privateKey, data, off);
|
|
||||||
off += utils.writeU32BE(data, index, off);
|
|
||||||
} else {
|
|
||||||
data = new Buffer(this.publicKey.length + 4);
|
|
||||||
off += utils.copy(this.publicKey, data, off);
|
|
||||||
off += utils.writeU32BE(data, index, off);
|
|
||||||
}
|
|
||||||
|
|
||||||
hash = utils.sha512hmac(data, this.chainCode);
|
|
||||||
leftPart = new bn(hash.slice(0, 32));
|
|
||||||
chainCode = hash.slice(32, 64);
|
|
||||||
|
|
||||||
privateKey = leftPart
|
|
||||||
.add(new bn(this.privateKey))
|
|
||||||
.mod(ec.elliptic.curve.n)
|
|
||||||
.toBuffer('be', 32);
|
|
||||||
|
|
||||||
if (!this.fingerPrint) {
|
|
||||||
this.fingerPrint = utils.ripesha(this.publicKey)
|
|
||||||
.slice(0, constants.hd.parentFingerPrintSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
child = new HDPrivateKey({
|
|
||||||
network: this.network,
|
|
||||||
data: {
|
|
||||||
version: this.version,
|
|
||||||
depth: utils.readU8(this.depth) + 1,
|
|
||||||
parentFingerPrint: this.fingerPrint,
|
|
||||||
childIndex: index,
|
|
||||||
chainCode: chainCode,
|
|
||||||
privateKey: privateKey,
|
|
||||||
checksum: null
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
HD.cache.set(this.xprivkey + '/' + index, child);
|
|
||||||
|
|
||||||
return child;
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
// https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
||||||
HDPrivateKey._getIndexes = function _getIndexes(path) {
|
HDPrivateKey._getIndexes = function _getIndexes(path) {
|
||||||
var steps = path.split('/');
|
var steps = path.split('/');
|
||||||
@ -580,6 +425,128 @@ HDPrivateKey.prototype.derivePath = function derivePath(path) {
|
|||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HDPrivateKey._fromSeed = function _fromSeed(seed, networkType) {
|
||||||
|
var data = seed.createSeed();
|
||||||
|
var hash;
|
||||||
|
|
||||||
|
if (data.length < constants.hd.minEntropy
|
||||||
|
|| data.length > constants.hd.maxEntropy) {
|
||||||
|
throw new Error('Entropy not in range.');
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = utils.sha512hmac(data, 'Bitcoin seed');
|
||||||
|
|
||||||
|
return {
|
||||||
|
version: networkType
|
||||||
|
? network[networkType].prefixes.xprivkey
|
||||||
|
: network.prefixes.xprivkey,
|
||||||
|
depth: 0,
|
||||||
|
parentFingerPrint: new Buffer([0, 0, 0, 0]),
|
||||||
|
childIndex: 0,
|
||||||
|
chainCode: hash.slice(32, 64),
|
||||||
|
privateKey: hash.slice(0, 32)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPrivateKey.fromSeed = function fromSeed(options, networkType) {
|
||||||
|
var seed, key;
|
||||||
|
|
||||||
|
if (!options)
|
||||||
|
options = {};
|
||||||
|
|
||||||
|
seed = (options instanceof HDSeed)
|
||||||
|
? options
|
||||||
|
: new HDSeed(options);
|
||||||
|
|
||||||
|
key = new HDPrivateKey({
|
||||||
|
data: HDPrivateKey._fromSeed(seed, networkType)
|
||||||
|
});
|
||||||
|
|
||||||
|
key.seed = seed;
|
||||||
|
|
||||||
|
return key;
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPrivateKey._generate = function _generate(options, networkType) {
|
||||||
|
if (!options)
|
||||||
|
opitons = {};
|
||||||
|
|
||||||
|
if (Buffer.isBuffer(options))
|
||||||
|
options = { privateKey: options };
|
||||||
|
|
||||||
|
if (!options.privateKey)
|
||||||
|
options.privateKey = ec.generatePrivateKey();
|
||||||
|
|
||||||
|
if (!options.entropy)
|
||||||
|
options.entropy = ec.random(32);
|
||||||
|
|
||||||
|
return {
|
||||||
|
version: networkType
|
||||||
|
? network[networkType].prefixes.xprivkey
|
||||||
|
: network.prefixes.xprivkey,
|
||||||
|
depth: 0,
|
||||||
|
parentFingerPrint: new Buffer([0, 0, 0, 0]),
|
||||||
|
childIndex: 0,
|
||||||
|
chainCode: entropy,
|
||||||
|
privateKey: privateKey
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPrivateKey.generate = function generate(options, networkType) {
|
||||||
|
return new HDPrivateKey({
|
||||||
|
data: HDPrivateKey._generate(options, networkType)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPrivateKey.parse = function parse(xkey) {
|
||||||
|
var raw = utils.fromBase58(xkey);
|
||||||
|
var p = new BufferReader(raw, true);
|
||||||
|
var data = {};
|
||||||
|
var i, type, prefix;
|
||||||
|
|
||||||
|
data.version = p.readU32BE();
|
||||||
|
data.depth = p.readU8();
|
||||||
|
data.parentFingerPrint = p.readBytes(4);
|
||||||
|
data.childIndex = p.readU32BE();
|
||||||
|
data.chainCode = p.readBytes(32);
|
||||||
|
p.readU8();
|
||||||
|
data.privateKey = p.readBytes(32);
|
||||||
|
p.verifyChecksum();
|
||||||
|
|
||||||
|
for (i = 0; i < network.types.length; i++) {
|
||||||
|
type = network.types[i];
|
||||||
|
prefix = network[type].prefixes.xprivkey;
|
||||||
|
if (data.version === prefix)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(i < network.types.length, 'Network not found.');
|
||||||
|
|
||||||
|
return {
|
||||||
|
networkType: type,
|
||||||
|
xprivkey: xkey,
|
||||||
|
data: data
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPrivateKey.fromBase58 = function fromBase58(xkey) {
|
||||||
|
var data = HDPrivateKey.parse(xkey);
|
||||||
|
return new HDPrivateKey(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPrivateKey.render = function render(data) {
|
||||||
|
var p = new BufferWriter();
|
||||||
|
p.writeU32BE(data.version);
|
||||||
|
p.writeU8(data.depth);
|
||||||
|
p.writeBytes(data.parentFingerPrint);
|
||||||
|
p.writeU32BE(data.childIndex);
|
||||||
|
p.writeBytes(data.chainCode);
|
||||||
|
p.writeU8(0);
|
||||||
|
p.writeBytes(data.privateKey);
|
||||||
|
p.writeChecksum();
|
||||||
|
return utils.toBase58(p.render());
|
||||||
|
};
|
||||||
|
|
||||||
HDPrivateKey.prototype.toJSON = function toJSON(passphrase) {
|
HDPrivateKey.prototype.toJSON = function toJSON(passphrase) {
|
||||||
var json = {
|
var json = {
|
||||||
v: 1,
|
v: 1,
|
||||||
@ -647,27 +614,31 @@ HDPrivateKey._fromJSON = function _fromJSON(json, passphrase) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
|
HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
|
||||||
|
var key;
|
||||||
|
|
||||||
json = HDPrivateKey._fromJSON(json, passphrase);
|
json = HDPrivateKey._fromJSON(json, passphrase);
|
||||||
|
|
||||||
if (json.xprivkey) {
|
if (json.xprivkey) {
|
||||||
return new HDPrivateKey({
|
key = HDPrivateKey.fromBase58(json.xprivkey);
|
||||||
xkey: json.xprivkey,
|
key.seed = json.seed ? new HDSeed(json.seed) : null;
|
||||||
seed: json.seed ? new HDSeed(json.seed) : null
|
return key;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json.seed)
|
if (json.seed)
|
||||||
return HDPrivateKey.fromSeed(json.seed);
|
return HDPrivateKey.fromSeed(json.seed);
|
||||||
|
|
||||||
if (json.xpubkey) {
|
if (json.xpubkey)
|
||||||
return new HDPublicKey({
|
return HDPublicKey.fromBase58(json.xprivkey);
|
||||||
xkey: json.xpubkey
|
|
||||||
});
|
assert(false, 'Could not handle HD key JSON.');
|
||||||
}
|
};
|
||||||
|
|
||||||
|
HDPublicKey.isHDPrivateKey = function isHDPrivateKey(obj) {
|
||||||
|
return obj && obj.isPrivate && typeof obj.derive === 'function';
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HD Public Key
|
* HDPublicKey
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function HDPublicKey(options) {
|
function HDPublicKey(options) {
|
||||||
@ -676,9 +647,7 @@ function HDPublicKey(options) {
|
|||||||
if (!(this instanceof HDPublicKey))
|
if (!(this instanceof HDPublicKey))
|
||||||
return new HDPublicKey(options);
|
return new HDPublicKey(options);
|
||||||
|
|
||||||
if (!options)
|
assert(options, 'No options for HDPublicKey');
|
||||||
throw new Error('No options for HDPublicKey');
|
|
||||||
|
|
||||||
assert(!(options instanceof HDPrivateKey));
|
assert(!(options instanceof HDPrivateKey));
|
||||||
assert(!(options instanceof HDPublicKey));
|
assert(!(options instanceof HDPublicKey));
|
||||||
|
|
||||||
@ -694,120 +663,27 @@ function HDPublicKey(options) {
|
|||||||
if (HDPrivateKey.isExtended(options.xkey))
|
if (HDPrivateKey.isExtended(options.xkey))
|
||||||
throw new Error('Cannot pass xprivkey into HDPublicKey');
|
throw new Error('Cannot pass xprivkey into HDPublicKey');
|
||||||
|
|
||||||
this.network = options.network || network.type;
|
this.networkType = options.networkType || network.type;
|
||||||
|
this.xpubkey = options.xkey;
|
||||||
|
|
||||||
data = options.xkey
|
if (options.data) {
|
||||||
? this._unbuild(options.xkey)
|
data = options.data;
|
||||||
: options.data;
|
} else if (options.xkey) {
|
||||||
assert(data);
|
data = HDPublicKey.parse(options.xkey);
|
||||||
|
this.networkType = data.networkType;
|
||||||
data = this._normalize(data);
|
data = data.data;
|
||||||
|
} else {
|
||||||
this.data = data;
|
assert(false, 'No data passed to HD key.');
|
||||||
|
|
||||||
this._build(data);
|
|
||||||
|
|
||||||
if (utils.readU32BE(data.parentFingerPrint) === 0)
|
|
||||||
this.isMaster = true;
|
|
||||||
else
|
|
||||||
this.isMaster = false;
|
|
||||||
|
|
||||||
this.isPrivate = false;
|
|
||||||
this.isPublic = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.inherits(HDPublicKey, HD);
|
|
||||||
|
|
||||||
HDPublicKey.isHDPublicKey = function isHDPublicKey(obj) {
|
|
||||||
return obj && obj.isPublic && typeof obj._unbuild === 'function';
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPublicKey.prototype.deriveAccount44 = HDPrivateKey.prototype.deriveAccount44;
|
|
||||||
HDPublicKey.prototype.derivePurpose45 = HDPrivateKey.prototype.derivePurpose45;
|
|
||||||
HDPublicKey.prototype.isPurpose45 = HDPrivateKey.prototype.isPurpose45;
|
|
||||||
HDPublicKey.prototype.isAccount44 = HDPrivateKey.prototype.isAccount44;
|
|
||||||
HDPublicKey.prototype.toJSON = HDPrivateKey.prototype.toJSON;
|
|
||||||
HDPublicKey.fromJSON = HDPrivateKey.fromJSON;
|
|
||||||
|
|
||||||
HDPublicKey.isExtended = function isExtended(data) {
|
|
||||||
if (typeof data !== 'string')
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return network.xpubkeys[data.slice(0, 4)];
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPublicKey.prototype._normalize = HDPrivateKey.prototype._normalize;
|
|
||||||
|
|
||||||
HDPublicKey.prototype._unbuild = function _unbuild(xkey) {
|
|
||||||
var raw = utils.fromBase58(xkey);
|
|
||||||
var data = {};
|
|
||||||
var off = 0;
|
|
||||||
var hash;
|
|
||||||
|
|
||||||
data.version = raw.slice(off, off + 4);
|
|
||||||
off += 4;
|
|
||||||
data.depth = raw.slice(off, off + 1);
|
|
||||||
off += 1;
|
|
||||||
data.parentFingerPrint = raw.slice(off, off + 4);
|
|
||||||
off += 4;
|
|
||||||
data.childIndex = raw.slice(off, off + 4);
|
|
||||||
off += 4;
|
|
||||||
data.chainCode = raw.slice(off, off + 32);
|
|
||||||
off += 32;
|
|
||||||
data.publicKey = raw.slice(off, off + 33);
|
|
||||||
off += 33;
|
|
||||||
data.checksum = raw.slice(off, off + 4);
|
|
||||||
off += 4;
|
|
||||||
|
|
||||||
hash = utils.dsha256(raw.slice(0, -4)).slice(0, 4);
|
|
||||||
if (!utils.isEqual(data.checksum, hash))
|
|
||||||
throw new Error('checksum mismatch');
|
|
||||||
|
|
||||||
network.types.some(function(type) {
|
|
||||||
if (utils.readU32BE(data.version) === network[type].prefixes.xprivkey) {
|
|
||||||
this.network = type;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
this.xpubkey = xkey;
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
HDPublicKey.prototype._build = function _build(data) {
|
|
||||||
var off = 0;
|
|
||||||
var sequence, checksum;
|
|
||||||
|
|
||||||
if (!this.xpubkey) {
|
|
||||||
sequence = new Buffer(82);
|
|
||||||
off += utils.copy(data.version, sequence, off);
|
|
||||||
off += utils.copy(data.depth, sequence, off);
|
|
||||||
off += utils.copy(data.parentFingerPrint, sequence, off);
|
|
||||||
off += utils.copy(data.childIndex, sequence, off);
|
|
||||||
off += utils.copy(data.chainCode, sequence, off);
|
|
||||||
off += utils.copy(data.publicKey, sequence, off);
|
|
||||||
assert(off === 78, off);
|
|
||||||
checksum = utils.dsha256(sequence.slice(0, off)).slice(0, 4);
|
|
||||||
off += utils.copy(checksum, sequence, off);
|
|
||||||
assert(off === 82, off);
|
|
||||||
|
|
||||||
if (!data.checksum || !data.checksum.length)
|
|
||||||
data.checksum = checksum;
|
|
||||||
else if (utils.toHex(checksum) !== utils.toHex(data.checksum))
|
|
||||||
throw new Error('checksum mismatch');
|
|
||||||
|
|
||||||
this.xpubkey = utils.toBase58(sequence);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(data.depth <= 0xff, 'Depth is too high.');
|
||||||
|
|
||||||
this.version = data.version;
|
this.version = data.version;
|
||||||
this.depth = data.depth;
|
this.depth = data.depth;
|
||||||
this.parentFingerPrint = data.parentFingerPrint;
|
this.parentFingerPrint = data.parentFingerPrint;
|
||||||
this.childIndex = data.childIndex;
|
this.childIndex = data.childIndex;
|
||||||
this.chainCode = data.chainCode;
|
this.chainCode = data.chainCode;
|
||||||
this.publicKey = data.publicKey;
|
this.publicKey = data.publicKey;
|
||||||
this.checksum = null;
|
|
||||||
|
|
||||||
this.privateKey = null;
|
this.privateKey = null;
|
||||||
this.fingerPrint = null;
|
this.fingerPrint = null;
|
||||||
@ -816,11 +692,18 @@ HDPublicKey.prototype._build = function _build(data) {
|
|||||||
|
|
||||||
this.hdPrivateKey = null;
|
this.hdPrivateKey = null;
|
||||||
this.xprivkey = null;
|
this.xprivkey = null;
|
||||||
};
|
|
||||||
|
if (!this.xpubkey)
|
||||||
|
this.xpubkey = HDPublicKey.render(data);
|
||||||
|
|
||||||
|
this.isPrivate = false;
|
||||||
|
this.isPublic = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.inherits(HDPublicKey, HD);
|
||||||
|
|
||||||
HDPublicKey.prototype.derive = function derive(index, hardened) {
|
HDPublicKey.prototype.derive = function derive(index, hardened) {
|
||||||
var off = 0;
|
var cached, p, data, hash, leftPart, chainCode;
|
||||||
var cached, data, hash, leftPart, chainCode;
|
|
||||||
var publicPoint, point, publicKey, child;
|
var publicPoint, point, publicKey, child;
|
||||||
|
|
||||||
if (typeof index === 'string')
|
if (typeof index === 'string')
|
||||||
@ -832,14 +715,15 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
|
|||||||
return cached;
|
return cached;
|
||||||
|
|
||||||
if (index >= constants.hd.hardened || hardened)
|
if (index >= constants.hd.hardened || hardened)
|
||||||
throw new Error('invalid index');
|
throw new Error('Invalid index.');
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
throw new Error('invalid path');
|
throw new Error('Invalid path.');
|
||||||
|
|
||||||
data = new Buffer(this.publicKey.length + 4);
|
p = new BufferWriter();
|
||||||
off += utils.copy(this.publicKey, data, off);
|
p.writeBytes(this.publicKey);
|
||||||
off += utils.writeU32BE(data, index, off);
|
p.writeU32BE(index);
|
||||||
|
data = p.render();
|
||||||
|
|
||||||
hash = utils.sha512hmac(data, this.chainCode);
|
hash = utils.sha512hmac(data, this.chainCode);
|
||||||
leftPart = new bn(hash.slice(0, 32));
|
leftPart = new bn(hash.slice(0, 32));
|
||||||
@ -855,15 +739,14 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
child = new HDPublicKey({
|
child = new HDPublicKey({
|
||||||
network: this.network,
|
networkType: this.networkType,
|
||||||
data: {
|
data: {
|
||||||
version: this.version,
|
version: this.version,
|
||||||
depth: utils.readU8(this.depth) + 1,
|
depth: this.depth + 1,
|
||||||
parentFingerPrint: this.fingerPrint,
|
parentFingerPrint: this.fingerPrint,
|
||||||
childIndex: index,
|
childIndex: index,
|
||||||
chainCode: chainCode,
|
chainCode: chainCode,
|
||||||
publicKey: publicKey,
|
publicKey: publicKey
|
||||||
checksum: null
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -872,9 +755,16 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
|
|||||||
return child;
|
return child;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HDPublicKey.prototype.deriveAccount44 = HDPrivateKey.prototype.deriveAccount44;
|
||||||
|
HDPublicKey.prototype.derivePurpose45 = HDPrivateKey.prototype.derivePurpose45;
|
||||||
|
HDPublicKey.prototype.isPurpose45 = HDPrivateKey.prototype.isPurpose45;
|
||||||
|
HDPublicKey.prototype.isAccount44 = HDPrivateKey.prototype.isAccount44;
|
||||||
|
|
||||||
HDPublicKey.isValidPath = function isValidPath(arg) {
|
HDPublicKey.isValidPath = function isValidPath(arg) {
|
||||||
|
var indexes;
|
||||||
|
|
||||||
if (typeof arg === 'string') {
|
if (typeof arg === 'string') {
|
||||||
var indexes = HDPrivateKey._getIndexes(arg);
|
indexes = HDPrivateKey._getIndexes(arg);
|
||||||
return indexes !== null && indexes.every(HDPublicKey.isValidPath);
|
return indexes !== null && indexes.every(HDPublicKey.isValidPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -885,20 +775,84 @@ HDPublicKey.isValidPath = function isValidPath(arg) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HDPublicKey.prototype.derivePath = function derivePath(path) {
|
HDPublicKey.prototype.derivePath = function derivePath(path) {
|
||||||
if (~path.indexOf('\''))
|
var indexes;
|
||||||
throw new Error('cannot derive hardened');
|
|
||||||
else if (!HDPublicKey.isValidPath(path))
|
|
||||||
throw new Error('invalid path');
|
|
||||||
|
|
||||||
var indexes = HDPrivateKey._getIndexes(path);
|
if (path.indexOf('\'') !== -1)
|
||||||
|
throw new Error('Cannot derive hardened.');
|
||||||
|
|
||||||
|
if (!HDPublicKey.isValidPath(path))
|
||||||
|
throw new Error('Invalid path.');
|
||||||
|
|
||||||
|
indexes = HDPrivateKey._getIndexes(path);
|
||||||
|
|
||||||
return indexes.reduce(function(prev, index) {
|
return indexes.reduce(function(prev, index) {
|
||||||
return prev.derive(index);
|
return prev.derive(index);
|
||||||
}, this);
|
}, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
HDPublicKey.prototype.toJSON = HDPrivateKey.prototype.toJSON;
|
||||||
|
HDPublicKey.fromJSON = HDPrivateKey.fromJSON;
|
||||||
|
|
||||||
|
HDPublicKey.isExtended = function isExtended(data) {
|
||||||
|
if (typeof data !== 'string')
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return network.xpubkeys[data.slice(0, 4)];
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPublicKey.parse = function parse(xkey) {
|
||||||
|
var raw = utils.fromBase58(xkey);
|
||||||
|
var p = new BufferReader(raw, true);
|
||||||
|
var data = {};
|
||||||
|
var i, type, prefix;
|
||||||
|
|
||||||
|
data.version = p.readU32BE();
|
||||||
|
data.depth = p.readU8();
|
||||||
|
data.parentFingerPrint = p.readBytes(4);
|
||||||
|
data.childIndex = p.readU32BE();
|
||||||
|
data.chainCode = p.readBytes(32);
|
||||||
|
data.publicKey = p.readBytes(33);
|
||||||
|
p.verifyChecksum();
|
||||||
|
|
||||||
|
for (i = 0; i < network.types.length; i++) {
|
||||||
|
type = network.types[i];
|
||||||
|
prefix = network[type].prefixes.xpubkey;
|
||||||
|
if (data.version === prefix)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(i < network.types.length, 'Network not found.');
|
||||||
|
|
||||||
|
return {
|
||||||
|
networkType: type,
|
||||||
|
xpubkey: xkey,
|
||||||
|
data: data
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPublicKey.render = function render(data) {
|
||||||
|
var p = new BufferWriter();
|
||||||
|
p.writeU32BE(data.version);
|
||||||
|
p.writeU8(data.depth);
|
||||||
|
p.writeBytes(data.parentFingerPrint);
|
||||||
|
p.writeU32BE(data.childIndex);
|
||||||
|
p.writeBytes(data.chainCode);
|
||||||
|
p.writeBytes(data.publicKey);
|
||||||
|
p.writeChecksum();
|
||||||
|
return utils.toBase58(p.render());
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPublicKey.fromBase58 = function fromBase58(xkey) {
|
||||||
|
var data = HDPublicKey.parse(xkey);
|
||||||
|
return new HDPublicKey(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
HDPublicKey.isHDPublicKey = function isHDPublicKey(obj) {
|
||||||
|
return obj && obj.isPublic && typeof obj.derive === 'function';
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make HD keys behave like elliptic KeyPairs
|
* Make HD keys behave like KeyPairs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[HDPrivateKey, HDPublicKey].forEach(function(HD) {
|
[HDPrivateKey, HDPublicKey].forEach(function(HD) {
|
||||||
@ -925,16 +879,6 @@ HDPrivateKey.prototype.toSecret = function toSecret() {
|
|||||||
return KeyPair.toSecret.call(this);
|
return KeyPair.toSecret.call(this);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Helpers
|
|
||||||
*/
|
|
||||||
|
|
||||||
function array32(data) {
|
|
||||||
var b = new Buffer(4);
|
|
||||||
utils.writeU32BE(b, data, 0);
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -283,6 +283,21 @@ BufferReader.prototype.seek = function seek(off) {
|
|||||||
return off;
|
return off;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BufferReader.prototype.createChecksum = function createChecksum() {
|
||||||
|
assert(this.offset + 4 >= 0);
|
||||||
|
assert(this.offset + 4 <= this.data.length);
|
||||||
|
var start = this.stack[this.stack.length - 1] || 0;
|
||||||
|
var data = this.data.slice(start, this.offset);
|
||||||
|
return utils.readU32BE(utils.checksum(data), 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
BufferReader.prototype.verifyChecksum = function verifyChecksum() {
|
||||||
|
var chk = this.createChecksum();
|
||||||
|
var checksum = this.readU32BE();
|
||||||
|
assert(chk === checksum, 'Checksum mismatch.');
|
||||||
|
return checksum;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -36,7 +36,7 @@ function Wallet(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!options.master)
|
if (!options.master)
|
||||||
options.master = bcoin.hd.fromSeed();
|
options.master = bcoin.hd.privateKey.fromSeed();
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.provider = options.provider || null;
|
this.provider = options.provider || null;
|
||||||
@ -188,9 +188,9 @@ Wallet.prototype.addKey = function addKey(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bcoin.hd.privateKey.isExtended(key))
|
if (bcoin.hd.privateKey.isExtended(key))
|
||||||
key = bcoin.hd.privateKey(key);
|
key = bcoin.hd.privateKey.fromBase58(key);
|
||||||
else if (bcoin.hd.publicKey.isExtended(key))
|
else if (bcoin.hd.publicKey.isExtended(key))
|
||||||
key = bcoin.hd.publicKey(key);
|
key = bcoin.hd.publicKey.fromBase58(key);
|
||||||
|
|
||||||
if (key instanceof bcoin.hd.privateKey)
|
if (key instanceof bcoin.hd.privateKey)
|
||||||
key = key.hdPublicKey;
|
key = key.hdPublicKey;
|
||||||
@ -229,9 +229,9 @@ Wallet.prototype.removeKey = function removeKey(key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bcoin.hd.privateKey.isExtended(key))
|
if (bcoin.hd.privateKey.isExtended(key))
|
||||||
key = bcoin.hd.privateKey(key);
|
key = bcoin.hd.privateKey.fromBase58(key);
|
||||||
else if (bcoin.hd.publicKey.isExtended(key))
|
else if (bcoin.hd.publicKey.isExtended(key))
|
||||||
key = bcoin.hd.publicKey(key);
|
key = bcoin.hd.publicKey.fromBase58(key);
|
||||||
|
|
||||||
if (key instanceof bcoin.hd.privateKey)
|
if (key instanceof bcoin.hd.privateKey)
|
||||||
key = key.hdPublicKey;
|
key = key.hdPublicKey;
|
||||||
|
|||||||
@ -46,6 +46,7 @@ BufferWriter.prototype.render = function render(keep) {
|
|||||||
case '64be': off += utils.write64BE(data, item[1], off); break;
|
case '64be': off += utils.write64BE(data, item[1], off); break;
|
||||||
case 'varint': off += utils.writeVarint(data, item[1], off); break;
|
case 'varint': off += utils.writeVarint(data, item[1], off); break;
|
||||||
case 'bytes': off += utils.copy(item[1], data, off); break;
|
case 'bytes': off += utils.copy(item[1], data, off); break;
|
||||||
|
case 'checksum': off += utils.copy(utils.checksum(data.slice(0, off)), data, off); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,6 +177,11 @@ BufferWriter.prototype.writeVarint = function writeVarint(value) {
|
|||||||
this.data.push(['varint', value]);
|
this.data.push(['varint', value]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BufferWriter.prototype.writeChecksum = function writeChecksum() {
|
||||||
|
this.written += 4;
|
||||||
|
this.data.push(['checksum']);
|
||||||
|
};
|
||||||
|
|
||||||
BufferWriter.prototype.fill = function fill(value, size) {
|
BufferWriter.prototype.fill = function fill(value, size) {
|
||||||
assert(size >= 0);
|
assert(size >= 0);
|
||||||
var buf = new Buffer(size);
|
var buf = new Buffer(size);
|
||||||
|
|||||||
@ -37,7 +37,9 @@ describe('HD', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should create master private key', function() {
|
it('should create master private key', function() {
|
||||||
master = bcoin.hd.priv({ seed: seed });
|
var s = new bcoin.hd.seed();
|
||||||
|
s.seed = new Buffer(seed, 'hex');
|
||||||
|
master = bcoin.hd.priv.fromSeed(s);
|
||||||
assert.equal(master.xprivkey, master_priv);
|
assert.equal(master.xprivkey, master_priv);
|
||||||
assert.equal(master.xpubkey, master_pub);
|
assert.equal(master.xpubkey, master_pub);
|
||||||
});
|
});
|
||||||
@ -78,11 +80,11 @@ describe('HD', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should deserialize master private key', function() {
|
it('should deserialize master private key', function() {
|
||||||
master._unbuild(master.xprivkey);
|
bcoin.hd.priv.parse(master.xprivkey);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should deserialize master public key', function() {
|
it('should deserialize master public key', function() {
|
||||||
master.hdPublicKey._unbuild(master.hdPublicKey.xpubkey);
|
bcoin.hd.pub.parse(master.hdPublicKey.xpubkey);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should deserialize and reserialize', function() {
|
it('should deserialize and reserialize', function() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user