2014-04-09 16:37:14 -07:00
|
|
|
'use strict';
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
// 62.9% typed (by google's closure-compiler account)
|
2014-04-09 16:37:14 -07:00
|
|
|
|
2014-06-24 08:36:32 -07:00
|
|
|
var bitcore = require('bitcore');
|
|
|
|
var HK = bitcore.HierarchicalKey;
|
|
|
|
var WalletKey = bitcore.WalletKey;
|
|
|
|
var networks = bitcore.networks;
|
|
|
|
var util = bitcore.util;
|
2014-10-25 15:57:12 -07:00
|
|
|
var _ = require('lodash');
|
2014-09-03 05:58:24 -07:00
|
|
|
var preconditions = require('preconditions').instance();
|
2014-07-29 07:23:58 -07:00
|
|
|
var HDPath = require('./HDPath');
|
2014-04-09 16:37:14 -07:00
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc
|
|
|
|
* Wrapper for bitcore.HierarchicalKey to be used inside of Copay.
|
|
|
|
*
|
|
|
|
* @param {Object} opts
|
|
|
|
* @param {string} opts.networkName if set to 'testnet', use the test3 bitcoin
|
|
|
|
* network constants (livenet otherwise)
|
|
|
|
* @param {string} opts.extendedPrivateKeyString if set, use this private key
|
|
|
|
* string, othewise create a new
|
|
|
|
* private key
|
2014-09-08 11:42:55 -07:00
|
|
|
* @constructor
|
2014-09-03 05:58:24 -07:00
|
|
|
*/
|
2014-04-09 16:37:14 -07:00
|
|
|
function PrivateKey(opts) {
|
2014-04-17 13:01:31 -07:00
|
|
|
opts = opts || {};
|
2014-09-03 05:58:24 -07:00
|
|
|
this.network = opts.networkName === 'testnet' ? networks.testnet : networks.livenet;
|
2014-04-11 09:26:36 -07:00
|
|
|
var init = opts.extendedPrivateKeyString || this.network.name;
|
2014-08-19 10:31:35 -07:00
|
|
|
this.bip = new HK(init);
|
|
|
|
this.privateKeyCache = {};
|
2014-07-03 07:18:01 -07:00
|
|
|
this.publicHex = this.deriveBIP45Branch().eckey.public.toString('hex');
|
2014-04-09 16:37:14 -07:00
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc Retrieve this derivated private key's public key in hexa format
|
|
|
|
*
|
|
|
|
* The value returned is calculated using the path from PrivateKey's
|
|
|
|
* <tt>HDParams.IdFullBranch</tt>. This key is used to identify the copayer
|
|
|
|
* (signing messages mostly).
|
|
|
|
*
|
|
|
|
* @returns {string} the public key in a hexadecimal string
|
|
|
|
*/
|
2014-04-23 18:43:17 -07:00
|
|
|
PrivateKey.prototype.getId = function() {
|
|
|
|
if (!this.id) {
|
2014-06-08 12:59:48 -07:00
|
|
|
this.cacheId();
|
2014-04-18 10:40:16 -07:00
|
|
|
}
|
2014-04-23 18:43:17 -07:00
|
|
|
return this.id;
|
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc Retrieve this private key's private key in hex format
|
|
|
|
*
|
|
|
|
* The value returned is calculated using the path from PrivateKey's
|
|
|
|
* <tt>HDParams.IdFullBranch</tt>. This key is used to identify the copayer
|
|
|
|
* (signing messages mostly).
|
|
|
|
*
|
|
|
|
* @returns {string} the private key in a hexadecimal string
|
|
|
|
*/
|
2014-06-08 12:59:48 -07:00
|
|
|
PrivateKey.prototype.getIdPriv = function() {
|
|
|
|
if (!this.idpriv) {
|
|
|
|
this.cacheId();
|
|
|
|
}
|
|
|
|
return this.idpriv;
|
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc Retrieve this private key's private key
|
|
|
|
*
|
|
|
|
* The value returned is calculated using the path from PrivateKey's
|
|
|
|
* <tt>HDParams.IdFullBranch</tt>. This key is used to identify the copayer
|
|
|
|
* (signing messages mostly).
|
|
|
|
*
|
|
|
|
* @returns {bitcore.PrivateKey} the private key
|
|
|
|
*/
|
2014-06-26 08:49:22 -07:00
|
|
|
PrivateKey.prototype.getIdKey = function() {
|
|
|
|
if (!this.idkey) {
|
|
|
|
this.cacheId();
|
|
|
|
}
|
|
|
|
return this.idkey;
|
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc Caches the result of deriving IdFullBranch
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
*/
|
2014-06-08 12:59:48 -07:00
|
|
|
PrivateKey.prototype.cacheId = function() {
|
2014-07-29 07:23:58 -07:00
|
|
|
var path = HDPath.IdFullBranch;
|
2014-06-08 12:59:48 -07:00
|
|
|
var idhk = this.bip.derive(path);
|
2014-06-26 08:49:22 -07:00
|
|
|
this.idkey = idhk.eckey;
|
2014-06-08 12:59:48 -07:00
|
|
|
this.id = idhk.eckey.public.toString('hex');
|
|
|
|
this.idpriv = idhk.eckey.private.toString('hex');
|
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc Derive the master branch for Copay.
|
|
|
|
*/
|
2014-05-28 12:10:05 -07:00
|
|
|
PrivateKey.prototype.deriveBIP45Branch = function() {
|
|
|
|
if (!this.bip45Branch) {
|
2014-07-29 07:23:58 -07:00
|
|
|
this.bip45Branch = this.bip.derive(HDPath.BIP45_PUBLIC_PREFIX);
|
2014-05-28 12:10:05 -07:00
|
|
|
}
|
|
|
|
return this.bip45Branch;
|
2014-09-03 05:58:24 -07:00
|
|
|
};
|
2014-08-19 10:31:35 -07:00
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc Returns an object with information needed to rebuild a PrivateKey
|
|
|
|
* (as most of its properties are derived from the extended private key).
|
|
|
|
*
|
|
|
|
* @TODO: Figure out if this is the correct pattern
|
|
|
|
* This is a static method and is probably used for serialization.
|
|
|
|
*
|
|
|
|
* @static
|
|
|
|
* @param {Object} data
|
|
|
|
* @param {*} data.networkName - a name for a bitcoin network
|
|
|
|
* @param {*} data.extendedPrivateKeyString - a bip32 extended private key
|
|
|
|
* @returns {Object} an object with two properties: networkName and
|
|
|
|
* extendedPrivateKeyString, taken from the <tt>data</tt>
|
|
|
|
* parameter.
|
|
|
|
*/
|
2014-08-19 10:31:35 -07:00
|
|
|
PrivateKey.trim = function(data) {
|
2014-09-03 05:58:24 -07:00
|
|
|
var opts = {};
|
2014-08-19 10:31:35 -07:00
|
|
|
['networkName', 'extendedPrivateKeyString'].forEach(function(k){
|
2014-09-03 05:58:24 -07:00
|
|
|
opts[k] = data[k];
|
2014-08-19 10:31:35 -07:00
|
|
|
});
|
2014-09-03 05:58:24 -07:00
|
|
|
return opts
|
2014-08-19 10:31:35 -07:00
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc Generate a private Key from a serialized object
|
|
|
|
*
|
|
|
|
* @TODO: This method uses PrivateKey.trim but it's actually not needed...
|
|
|
|
*
|
|
|
|
* @param {Object} data
|
|
|
|
* @param {*} data.networkName - a name for a bitcoin network
|
|
|
|
* @param {*} data.extendedPrivateKeyString - a bip32 extended private key
|
|
|
|
* @returns {PrivateKey}
|
|
|
|
*/
|
2014-04-17 13:01:31 -07:00
|
|
|
PrivateKey.fromObj = function(obj) {
|
2014-08-19 10:31:35 -07:00
|
|
|
return new PrivateKey(PrivateKey.trim(obj));
|
2014-04-09 22:16:57 -07:00
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc Serialize a private key, keeping only the data necessary to rebuild it
|
|
|
|
*
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
2014-04-09 22:16:57 -07:00
|
|
|
PrivateKey.prototype.toObj = function() {
|
|
|
|
return {
|
2014-04-17 13:01:31 -07:00
|
|
|
extendedPrivateKeyString: this.getExtendedPrivateKeyString(),
|
2014-09-03 05:58:24 -07:00
|
|
|
networkName: this.network.name
|
2014-04-09 22:16:57 -07:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc Retrieve a BIP32 extended public key as generated by bitcore
|
|
|
|
*
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2014-04-17 13:01:31 -07:00
|
|
|
PrivateKey.prototype.getExtendedPublicKeyString = function() {
|
|
|
|
return this.bip.extendedPublicKeyString();
|
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc Retrieve a BIP32 extended private key as generated by bitcore
|
|
|
|
*
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
2014-04-17 13:01:31 -07:00
|
|
|
PrivateKey.prototype.getExtendedPrivateKeyString = function() {
|
|
|
|
return this.bip.extendedPrivateKeyString();
|
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc
|
|
|
|
* Retrieve a HierarchicalKey derived from the given path as generated by
|
|
|
|
* bitcore
|
|
|
|
* @param {string} path - a string for derivation (something like "m/234'/1/2")
|
|
|
|
* @returns {bitcore.HierarchicalKey}
|
|
|
|
*/
|
2014-05-14 16:55:34 -07:00
|
|
|
PrivateKey.prototype._getHK = function(path) {
|
2014-09-03 05:58:24 -07:00
|
|
|
if (_.isUndefined(path)) {
|
2014-04-17 13:01:31 -07:00
|
|
|
return this.bip;
|
|
|
|
}
|
2014-06-12 10:27:53 -07:00
|
|
|
var ret = this.bip.derive(path);
|
|
|
|
return ret;
|
2014-04-17 13:01:31 -07:00
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc
|
|
|
|
* Retrieve an array of WalletKey derived from given paths. {@see PrivateKey#getForPath}
|
|
|
|
*
|
|
|
|
* @param {string[]} paths - the paths to derive
|
|
|
|
* @returns {bitcore.WalletKey[]} - the derived keys
|
|
|
|
*/
|
2014-05-30 11:07:52 -07:00
|
|
|
PrivateKey.prototype.getForPaths = function(paths) {
|
|
|
|
return paths.map(this.getForPath.bind(this));
|
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc
|
|
|
|
* Retrieve a WalletKey derived from a path.
|
|
|
|
*
|
|
|
|
* @param {string} paths - the path to derive
|
|
|
|
* @returns {bitcore.WalletKey} - the derived key
|
|
|
|
*/
|
2014-05-30 11:07:52 -07:00
|
|
|
PrivateKey.prototype.getForPath = function(path) {
|
2014-04-17 13:01:31 -07:00
|
|
|
var pk = this.privateKeyCache[path];
|
|
|
|
if (!pk) {
|
2014-06-24 08:36:32 -07:00
|
|
|
var derivedHK = this._getHK(path);
|
2014-05-14 16:55:34 -07:00
|
|
|
pk = this.privateKeyCache[path] = derivedHK.eckey.private.toString('hex');
|
2014-04-17 13:01:31 -07:00
|
|
|
}
|
2014-06-24 08:36:32 -07:00
|
|
|
var wk = new WalletKey({
|
|
|
|
network: this.network
|
|
|
|
});
|
|
|
|
wk.fromObj({
|
|
|
|
priv: pk
|
|
|
|
});
|
2014-04-09 16:37:14 -07:00
|
|
|
return wk;
|
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc
|
|
|
|
* Retrieve a Branch for Copay using the given path
|
|
|
|
*
|
|
|
|
* @TODO: Investigate when is this called and if this is really needed
|
|
|
|
*
|
|
|
|
* @param {number} index - the index of the key to generate
|
|
|
|
* @param {boolean} isChange - whether this is a change adderess or a receive
|
|
|
|
* @param {number} cosigner - the cosigner index
|
|
|
|
* @return {bitcore.HierarchicalKey}
|
|
|
|
*/
|
2014-07-03 07:18:01 -07:00
|
|
|
PrivateKey.prototype.get = function(index, isChange, cosigner) {
|
2014-09-03 05:58:24 -07:00
|
|
|
|
|
|
|
// TODO: Add parameter validation?
|
|
|
|
|
2014-07-29 07:23:58 -07:00
|
|
|
var path = HDPath.FullBranch(index, isChange, cosigner);
|
2014-05-30 11:07:52 -07:00
|
|
|
return this.getForPath(path);
|
|
|
|
};
|
|
|
|
|
2014-09-03 05:58:24 -07:00
|
|
|
/**
|
|
|
|
* @desc
|
|
|
|
* Retrieve multiple branches for Copay up to the received indexes
|
|
|
|
*
|
|
|
|
* @TODO: Investigate when is this called and if this is really needed
|
|
|
|
*
|
|
|
|
* @param {number} receiveIndex - the number of receive addresses to generate
|
|
|
|
* @param {number} changeIndex - the number of change addresses to generate
|
|
|
|
* @param {number} cosigner - the cosigner index
|
|
|
|
* @return {bitcore.HierarchicalKey}
|
|
|
|
*/
|
2014-07-03 07:18:01 -07:00
|
|
|
PrivateKey.prototype.getAll = function(receiveIndex, changeIndex, cosigner) {
|
2014-09-03 05:58:24 -07:00
|
|
|
preconditions.checkArgument(!_.isUndefined(receiveIndex) && !_.isUndefined(changeIndex));
|
2014-06-04 09:44:32 -07:00
|
|
|
|
2014-04-09 19:04:22 -07:00
|
|
|
var ret = [];
|
2014-06-24 08:36:32 -07:00
|
|
|
for (var i = 0; i < receiveIndex; i++) {
|
2014-07-03 07:18:01 -07:00
|
|
|
ret.push(this.get(i, false, cosigner));
|
2014-04-09 19:04:22 -07:00
|
|
|
}
|
2014-06-24 08:36:32 -07:00
|
|
|
for (var i = 0; i < changeIndex; i++) {
|
2014-07-03 07:18:01 -07:00
|
|
|
ret.push(this.get(i, true, cosigner));
|
2014-04-09 19:04:22 -07:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2014-07-29 07:23:58 -07:00
|
|
|
module.exports = PrivateKey;
|