mirror of https://github.com/BTCPrivate/copay.git
rebased
This commit is contained in:
commit
550d82cacd
|
@ -24,7 +24,7 @@
|
||||||
"zeroclipboard": "~1.3.5",
|
"zeroclipboard": "~1.3.5",
|
||||||
"ng-idle": "*",
|
"ng-idle": "*",
|
||||||
"underscore": "~1.7.0",
|
"underscore": "~1.7.0",
|
||||||
"assert": "~0.1.0"
|
"inherits": "~0.0.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"angular": "=1.2.19"
|
"angular": "=1.2.19"
|
||||||
|
|
|
@ -17,7 +17,7 @@ angular.module('copayApp.controllers').controller('JoinController',
|
||||||
|
|
||||||
$scope.hideAdv=true;
|
$scope.hideAdv=true;
|
||||||
|
|
||||||
|
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||||
|
|
||||||
var _scan = function(evt) {
|
var _scan = function(evt) {
|
||||||
if (localMediaStream) {
|
if (localMediaStream) {
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
|
|
||||||
var bitcore = require('bitcore');
|
|
||||||
var Transaction = bitcore.Transaction;
|
|
||||||
|
|
||||||
function BuilderMockV0 (data) {
|
|
||||||
this.vanilla = data;
|
|
||||||
this.tx = new Transaction();
|
|
||||||
this.tx.parse(new Buffer(data.tx, 'hex'));
|
|
||||||
};
|
|
||||||
|
|
||||||
BuilderMockV0.prototype.build = function() {
|
|
||||||
return this.tx;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
BuilderMockV0.prototype.getSelectedUnspent = function() {
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
|
|
||||||
BuilderMockV0.prototype.toObj = function() {
|
|
||||||
return this.vanilla;
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = BuilderMockV0;
|
|
|
@ -1,53 +1,117 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
// 90.2% typed (by google's closure-compiler account)
|
||||||
|
|
||||||
var preconditions = require('preconditions').singleton();
|
var preconditions = require('preconditions').singleton();
|
||||||
|
var _ = require('underscore');
|
||||||
|
|
||||||
function HDPath() {}
|
/**
|
||||||
|
* @namespace
|
||||||
/*
|
*
|
||||||
|
* HDPath contains helper functions to handle BIP32 branches as
|
||||||
|
* Copay uses them.
|
||||||
|
*
|
||||||
* Based on https://github.com/maraoz/bips/blob/master/bip-NNNN.mediawiki
|
* Based on https://github.com/maraoz/bips/blob/master/bip-NNNN.mediawiki
|
||||||
* m / purpose' / copayerIndex / change / addressIndex
|
* <pre>
|
||||||
|
* m / purpose' / copayerIndex / change:boolean / addressIndex
|
||||||
|
* </pre>
|
||||||
*/
|
*/
|
||||||
var PURPOSE = 45;
|
var HDPath = {};
|
||||||
var MAX_NON_HARDENED = 0x80000000 - 1;
|
|
||||||
|
|
||||||
var SHARED_INDEX = MAX_NON_HARDENED - 0;
|
/**
|
||||||
var ID_INDEX = MAX_NON_HARDENED - 1;
|
* @desc Copay's BIP45 purpose code
|
||||||
|
* @const
|
||||||
|
* @type number
|
||||||
|
*/
|
||||||
|
HDPath.PURPOSE = 45;
|
||||||
|
|
||||||
var BIP45_PUBLIC_PREFIX = 'm/' + PURPOSE + '\'';
|
/**
|
||||||
HDPath.BIP45_PUBLIC_PREFIX = BIP45_PUBLIC_PREFIX;
|
* @desc Maximum number for non-hardened values (BIP32)
|
||||||
|
* @const
|
||||||
|
* @type number
|
||||||
|
*/
|
||||||
|
HDPath.MAX_NON_HARDENED = 0x80000000 - 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Shared Index: used for creating addresses for no particular purpose
|
||||||
|
* @const
|
||||||
|
* @type number
|
||||||
|
*/
|
||||||
|
HDPath.SHARED_INDEX = HDPath.MAX_NON_HARDENED - 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc ???
|
||||||
|
* @const
|
||||||
|
* @type number
|
||||||
|
*/
|
||||||
|
HDPath.ID_INDEX = HDPath.MAX_NON_HARDENED - 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc BIP45 prefix for COPAY
|
||||||
|
* @const
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
HDPath.BIP45_PUBLIC_PREFIX = 'm/' + HDPath.PURPOSE + '\'';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Retrieve a string to be used with bitcore representing a Copay branch
|
||||||
|
* @param {number} addressIndex - the last value of the HD derivation
|
||||||
|
* @param {boolean} isChange - whether this is a change address or a receive
|
||||||
|
* @param {number} copayerIndex - the index of the copayer in the pubkeyring
|
||||||
|
* @return {string} - the path for the HD derivation
|
||||||
|
*/
|
||||||
HDPath.Branch = function(addressIndex, isChange, copayerIndex) {
|
HDPath.Branch = function(addressIndex, isChange, copayerIndex) {
|
||||||
preconditions.shouldBeNumber(addressIndex);
|
preconditions.checkArgument(_.isNumber(addressIndex));
|
||||||
preconditions.shouldBeBoolean(isChange);
|
preconditions.checkArgument(_.isBoolean(isChange));
|
||||||
|
|
||||||
var ret = 'm/' +
|
var ret = 'm/' +
|
||||||
(typeof copayerIndex !== 'undefined' ? copayerIndex : SHARED_INDEX) + '/' +
|
(typeof copayerIndex !== 'undefined' ? copayerIndex : HDPath.SHARED_INDEX) + '/' +
|
||||||
(isChange ? 1 : 0) + '/' +
|
(isChange ? 1 : 0) + '/' +
|
||||||
addressIndex;
|
addressIndex;
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc ???
|
||||||
|
* @param {number} addressIndex - the last value of the HD derivation
|
||||||
|
* @param {boolean} isChange - whether this is a change address or a receive
|
||||||
|
* @param {number} copayerIndex - the index of the copayer in the pubkeyring
|
||||||
|
* @return {string} - the path for the HD derivation
|
||||||
|
*/
|
||||||
HDPath.FullBranch = function(addressIndex, isChange, copayerIndex) {
|
HDPath.FullBranch = function(addressIndex, isChange, copayerIndex) {
|
||||||
|
preconditions.checkArgument(_.isNumber(addressIndex));
|
||||||
|
preconditions.checkArgument(_.isBoolean(isChange));
|
||||||
|
|
||||||
var sub = HDPath.Branch(addressIndex, isChange, copayerIndex);
|
var sub = HDPath.Branch(addressIndex, isChange, copayerIndex);
|
||||||
sub = sub.substring(2);
|
sub = sub.substring(2);
|
||||||
return BIP45_PUBLIC_PREFIX + '/' + sub;
|
return HDPath.BIP45_PUBLIC_PREFIX + '/' + sub;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Decompose a string and retrieve its arguments as if it where a Copay address.
|
||||||
|
* @param {string} path - the HD path
|
||||||
|
* @returns {Object} an object with three keys: addressIndex, isChange, and
|
||||||
|
* copayerIndex
|
||||||
|
*/
|
||||||
HDPath.indexesForPath = function(path) {
|
HDPath.indexesForPath = function(path) {
|
||||||
preconditions.shouldBeString(path);
|
preconditions.checkArgument(_.isString(path));
|
||||||
|
|
||||||
var s = path.split('/');
|
var s = path.split('/');
|
||||||
return {
|
return {
|
||||||
isChange: s[3] === '1',
|
isChange: s[3] === '1',
|
||||||
addressIndex: parseInt(s[4]),
|
addressIndex: parseInt(s[4], 10),
|
||||||
copayerIndex: parseInt(s[2])
|
copayerIndex: parseInt(s[2], 10)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
HDPath.IdFullBranch = HDPath.FullBranch(0, false, ID_INDEX);
|
/**
|
||||||
HDPath.IdBranch = HDPath.Branch(0, false, ID_INDEX);
|
* @desc The ID for a shared branch
|
||||||
HDPath.PURPOSE = PURPOSE;
|
*/
|
||||||
HDPath.MAX_NON_HARDENED = MAX_NON_HARDENED;
|
HDPath.IdFullBranch = HDPath.FullBranch(0, false, HDPath.ID_INDEX);
|
||||||
HDPath.SHARED_INDEX = SHARED_INDEX;
|
/**
|
||||||
HDPath.ID_INDEX = ID_INDEX;
|
* @desc Partial ID for a shared branch
|
||||||
|
*/
|
||||||
|
HDPath.IdBranch = HDPath.Branch(0, false, HDPath.ID_INDEX);
|
||||||
|
|
||||||
module.exports = HDPath;
|
module.exports = HDPath;
|
||||||
|
|
|
@ -1,14 +1,30 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var preconditions = require('preconditions').instance();
|
var preconditions = require('preconditions').instance();
|
||||||
|
var _ = require('underscore');
|
||||||
|
var log = require('../../log');
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var HK = bitcore.HierarchicalKey;
|
var HK = bitcore.HierarchicalKey;
|
||||||
|
var Address = bitcore.Address;
|
||||||
|
var Script = bitcore.Script;
|
||||||
var PrivateKey = require('./PrivateKey');
|
var PrivateKey = require('./PrivateKey');
|
||||||
var HDPath = require('./HDPath');
|
var HDPath = require('./HDPath');
|
||||||
var HDParams = require('./HDParams');
|
var HDParams = require('./HDParams');
|
||||||
var Address = bitcore.Address;
|
|
||||||
var Script = bitcore.Script;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {string} opts.walletId
|
||||||
|
* @param {string} opts.network 'livenet' to signal the bitcoin main network, all others are testnet
|
||||||
|
* @param {number=} opts.requiredCopayers - defaults to 3
|
||||||
|
* @param {number=} opts.totalCopayers - defaults to 5
|
||||||
|
* @param {Object[]=} opts.indexes - an array to be deserialized using {@link HDParams#fromList}
|
||||||
|
* (defaults to all indexes in zero)
|
||||||
|
* @param {Object=} opts.nicknameFor - nicknames for other copayers
|
||||||
|
* @param {boolean[]=} opts.copayersBackup - whether other copayers have backed up their wallets
|
||||||
|
*/
|
||||||
function PublicKeyRing(opts) {
|
function PublicKeyRing(opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
|
||||||
|
@ -29,22 +45,50 @@ function PublicKeyRing(opts) {
|
||||||
this.copayerIds = [];
|
this.copayerIds = [];
|
||||||
this.copayersBackup = opts.copayersBackup || [];
|
this.copayersBackup = opts.copayersBackup || [];
|
||||||
this.addressToPath = {};
|
this.addressToPath = {};
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Returns an object with only the keys needed to rebuild a PublicKeyRing
|
||||||
|
*
|
||||||
|
* @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 {string} data.walletId - a string to identify a wallet
|
||||||
|
* @param {string} data.networkName - the name of the bitcoin network
|
||||||
|
* @param {number} data.requiredCopayers - the number of required copayers
|
||||||
|
* @param {number} data.totalCopayers - the number of copayers in the ring
|
||||||
|
* @param {Object[]} data.indexes - an array of objects that can be turned into
|
||||||
|
* an array of HDParams
|
||||||
|
* @param {Object} data.nicknameFor - a registry of nicknames for other copayers
|
||||||
|
* @param {boolean[]} data.copayersBackup - whether copayers have backed up their wallets
|
||||||
|
* @param {string[]} data.copayersExtPubKeys - the extended public keys of copayers
|
||||||
|
* @returns {Object} a trimmed down version of PublicKeyRing that can be used
|
||||||
|
* as a parameter
|
||||||
|
*/
|
||||||
PublicKeyRing.trim = function(data) {
|
PublicKeyRing.trim = function(data) {
|
||||||
var opts = {};
|
var opts = {};
|
||||||
['walletId', 'networkName', 'requiredCopayers', 'totalCopayers','indexes','nicknameFor','copayersBackup', 'copayersExtPubKeys' ].forEach(function(k){
|
['walletId', 'networkName', 'requiredCopayers', 'totalCopayers',
|
||||||
|
'indexes','nicknameFor','copayersBackup', 'copayersExtPubKeys'
|
||||||
|
].forEach(function(k){
|
||||||
opts[k] = data[k];
|
opts[k] = data[k];
|
||||||
});
|
});
|
||||||
|
|
||||||
return opts;
|
return opts;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Deserializes a PublicKeyRing from a plain object
|
||||||
|
*
|
||||||
|
* If the <tt>data</tt> parameter is an instance of PublicKeyRing already,
|
||||||
|
* it will fail, throwing an assertion error.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
* @param {object} data - a serialized version of PublicKeyRing {@see PublicKeyRing#trim}
|
||||||
|
* @return {PublicKeyRing} - the deserialized object
|
||||||
|
*/
|
||||||
PublicKeyRing.fromObj = function(data) {
|
PublicKeyRing.fromObj = function(data) {
|
||||||
preconditions.checkArgument(!(data instanceof PublicKeyRing), 'bad data format: Did you use .toObj()?');
|
preconditions.checkArgument(!(data instanceof PublicKeyRing), 'bad data format: Did you use .toObj()?');
|
||||||
|
|
||||||
|
|
||||||
var opts = PublicKeyRing.trim(data);
|
var opts = PublicKeyRing.trim(data);
|
||||||
|
|
||||||
// Support old indexes schema
|
// Support old indexes schema
|
||||||
|
@ -61,6 +105,12 @@ PublicKeyRing.fromObj = function(data) {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Serialize this object to a plain object with all the data needed to
|
||||||
|
* rebuild it
|
||||||
|
*
|
||||||
|
* @return {Object} a serialized version of a PublicKeyRing
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.toObj = function() {
|
PublicKeyRing.prototype.toObj = function() {
|
||||||
return {
|
return {
|
||||||
walletId: this.walletId,
|
walletId: this.walletId,
|
||||||
|
@ -73,91 +123,175 @@ PublicKeyRing.prototype.toObj = function() {
|
||||||
copayersExtPubKeys: this.copayersHK.map(function(b) {
|
copayersExtPubKeys: this.copayersHK.map(function(b) {
|
||||||
return b.extendedPublicKeyString();
|
return b.extendedPublicKeyString();
|
||||||
}),
|
}),
|
||||||
nicknameFor: this.nicknameFor,
|
nicknameFor: this.nicknameFor
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype.getCopayerId = function(i) {
|
/**
|
||||||
preconditions.checkArgument(typeof i !== 'undefined');
|
* @desc
|
||||||
return this.copayerIds[i];
|
* Retrieve a copayer's public key as a hexadecimal encoded string
|
||||||
|
*
|
||||||
|
* @param {number} copayerId - the copayer id
|
||||||
|
* @returns {string} the extended public key of the i-th copayer
|
||||||
|
*/
|
||||||
|
PublicKeyRing.prototype.getCopayerId = function(copayerId) {
|
||||||
|
preconditions.checkArgument(!_.isUndefined(copayerId))
|
||||||
|
preconditions.checkArgument(_.isNumber(copayerId));
|
||||||
|
|
||||||
|
return this.copayerIds[copayerId];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Get the amount of registered copayers in this PubKeyRing
|
||||||
|
*
|
||||||
|
* @returns {number} amount of copayers present
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.registeredCopayers = function() {
|
PublicKeyRing.prototype.registeredCopayers = function() {
|
||||||
return this.copayersHK.length;
|
return this.copayersHK.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Returns true if all the needed copayers have joined the public key ring
|
||||||
|
*
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.isComplete = function() {
|
PublicKeyRing.prototype.isComplete = function() {
|
||||||
return this.remainingCopayers() == 0;
|
return this.remainingCopayers() == 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Returns the number of copayers yet to join to make the public key ring complete
|
||||||
|
*
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.remainingCopayers = function() {
|
PublicKeyRing.prototype.remainingCopayers = function() {
|
||||||
return this.totalCopayers - this.registeredCopayers();
|
return this.totalCopayers - this.registeredCopayers();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Returns an array of copayer's public keys
|
||||||
|
*
|
||||||
|
* @returns {string[]} a list of hexadecimal strings with the public keys for
|
||||||
|
* the copayers in this ring
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getAllCopayerIds = function() {
|
PublicKeyRing.prototype.getAllCopayerIds = function() {
|
||||||
return this.copayerIds;
|
return this.copayerIds;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype.myCopayerId = function(i) {
|
/**
|
||||||
|
* @desc
|
||||||
|
* Gets the current user's copayerId
|
||||||
|
*
|
||||||
|
* @returns {string} the extended public key hexadecimal-encoded
|
||||||
|
*/
|
||||||
|
PublicKeyRing.prototype.myCopayerId = function() {
|
||||||
return this.getCopayerId(0);
|
return this.getCopayerId(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Throws an error if the public key ring isn't complete
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype._checkKeys = function() {
|
PublicKeyRing.prototype._checkKeys = function() {
|
||||||
|
if (!this.isComplete()) throw new Error('dont have required keys yet');
|
||||||
if (!this.isComplete())
|
|
||||||
throw new Error('dont have required keys yet');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Updates the internal register of the public hex string for a copayer, based
|
||||||
|
* on the value of the hierarchical key stored in copayersHK
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {number} index - the index of the copayer to update
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype._updateBip = function(index) {
|
PublicKeyRing.prototype._updateBip = function(index) {
|
||||||
var hk = this.copayersHK[index].derive(HDPath.IdBranch);
|
var hk = this.copayersHK[index].derive(HDPath.IdBranch);
|
||||||
this.copayerIds[index] = hk.eckey.public.toString('hex');
|
this.copayerIds[index] = hk.eckey.public.toString('hex');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Sets a nickname for one of the copayers
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {number} index - the index of the copayer to update
|
||||||
|
* @param {string} nickname - the new nickname for that copayer
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype._setNicknameForIndex = function(index, nickname) {
|
PublicKeyRing.prototype._setNicknameForIndex = function(index, nickname) {
|
||||||
this.nicknameFor[this.copayerIds[index]] = nickname;
|
this.nicknameFor[this.copayerIds[index]] = nickname;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Fetch the name of a copayer
|
||||||
|
*
|
||||||
|
* @param {number} index - the index of the copayer
|
||||||
|
* @return {string} the nickname of the index-th copayer
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.nicknameForIndex = function(index) {
|
PublicKeyRing.prototype.nicknameForIndex = function(index) {
|
||||||
return this.nicknameFor[this.copayerIds[index]];
|
return this.nicknameFor[this.copayerIds[index]];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Fetch the name of a copayer using its public key
|
||||||
|
*
|
||||||
|
* @param {string} copayerId - the public key ring of a copayer, hex encoded
|
||||||
|
* @return {string} the nickname of the copayer with such pubkey
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.nicknameForCopayer = function(copayerId) {
|
PublicKeyRing.prototype.nicknameForCopayer = function(copayerId) {
|
||||||
return this.nicknameFor[copayerId] || 'NN';
|
return this.nicknameFor[copayerId] || 'NN';
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype.addCopayer = function(newEpk, nickname) {
|
/**
|
||||||
preconditions.checkArgument(newEpk);
|
* @desc
|
||||||
|
* Add a copayer into the public key ring.
|
||||||
|
*
|
||||||
|
* @param {string} newHexaExtendedPublicKey - an hex encoded string with the copayer's pubkey
|
||||||
|
* @param {string} nickname - a nickname for this copayer
|
||||||
|
* @return {string} the newHexaExtendedPublicKey parameter
|
||||||
|
*/
|
||||||
|
PublicKeyRing.prototype.addCopayer = function(newHexaExtendedPublicKey, nickname) {
|
||||||
|
preconditions.checkArgument(newHexaExtendedPublicKey && _.isString(newHexaExtendedPublicKey));
|
||||||
|
preconditions.checkArgument(!this.isComplete());
|
||||||
|
preconditions.checkArgument(!nickname || _.isString(nickname));
|
||||||
|
preconditions.checkArgument(!_.any(this.copayersHK,
|
||||||
|
function(copayer) { return copayer.extendedPublicKeyString === newHexaExtendedPublicKey; }
|
||||||
|
));
|
||||||
|
|
||||||
if (this.isComplete())
|
var newCopayerIndex = this.copayersHK.length;
|
||||||
throw new Error('PKR already has all required key:' + this.totalCopayers);
|
var hierarchicalKey = new HK(newHexaExtendedPublicKey);
|
||||||
|
|
||||||
this.copayersHK.forEach(function(b) {
|
this.copayersHK.push(hierarchicalKey);
|
||||||
if (b.extendedPublicKeyString() === newEpk)
|
this._updateBip(newCopayerIndex);
|
||||||
throw new Error('PKR already has that key');
|
|
||||||
});
|
|
||||||
|
|
||||||
var i = this.copayersHK.length;
|
|
||||||
var bip = new HK(newEpk);
|
|
||||||
this.copayersHK.push(bip);
|
|
||||||
this._updateBip(i);
|
|
||||||
if (nickname) {
|
if (nickname) {
|
||||||
this._setNicknameForIndex(i, nickname);
|
this._setNicknameForIndex(newCopayerIndex, nickname);
|
||||||
}
|
}
|
||||||
return newEpk;
|
return newHexaExtendedPublicKey;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Get all the public keys for the copayers in this ring, for a given branch of Copay
|
||||||
|
*
|
||||||
|
* @param {number} index - the index for the shared address
|
||||||
|
* @param {boolean} isChange - whether to derive a change address o receive address
|
||||||
|
* @param {number} copayerIndex - the index of the copayer that requested the derivation
|
||||||
|
* @return {Buffer[]} an array of derived public keys in hexa format
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getPubKeys = function(index, isChange, copayerIndex) {
|
PublicKeyRing.prototype.getPubKeys = function(index, isChange, copayerIndex) {
|
||||||
this._checkKeys();
|
this._checkKeys();
|
||||||
|
|
||||||
var path = HDPath.Branch(index, isChange, copayerIndex);
|
var path = HDPath.Branch(index, isChange, copayerIndex);
|
||||||
var pubKeys = this.publicKeysCache[path];
|
var pubKeys = this.publicKeysCache[path];
|
||||||
if (!pubKeys) {
|
if (!pubKeys) {
|
||||||
pubKeys = [];
|
pubKeys = _.map(this.copayersHK, function(hdKey) {
|
||||||
var l = this.copayersHK.length;
|
return hdKey.derive(path).eckey.public;
|
||||||
for (var i = 0; i < l; i++) {
|
});
|
||||||
var hk = this.copayersHK[i].derive(path);
|
|
||||||
pubKeys[i] = hk.eckey.public;
|
|
||||||
}
|
|
||||||
this.publicKeysCache[path] = pubKeys.map(function(pk) {
|
this.publicKeysCache[path] = pubKeys.map(function(pk) {
|
||||||
return pk.toString('hex');
|
return pk.toString('hex');
|
||||||
});
|
});
|
||||||
|
@ -166,19 +300,39 @@ PublicKeyRing.prototype.getPubKeys = function(index, isChange, copayerIndex) {
|
||||||
return new Buffer(s, 'hex');
|
return new Buffer(s, 'hex');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return pubKeys;
|
return pubKeys;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO this could be cached
|
/**
|
||||||
|
* @desc
|
||||||
|
* Generate a new Script for a copay address generated by index, isChange, and copayerIndex
|
||||||
|
*
|
||||||
|
* @TODO this could be cached
|
||||||
|
*
|
||||||
|
* @param {number} index - the index for the shared address
|
||||||
|
* @param {boolean} isChange - whether to derive a change address o receive address
|
||||||
|
* @param {number} copayerIndex - the index of the copayer that requested the derivation
|
||||||
|
* @returns {bitcore.Script}
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getRedeemScript = function(index, isChange, copayerIndex) {
|
PublicKeyRing.prototype.getRedeemScript = function(index, isChange, copayerIndex) {
|
||||||
var pubKeys = this.getPubKeys(index, isChange, copayerIndex);
|
var pubKeys = this.getPubKeys(index, isChange, copayerIndex);
|
||||||
var script = Script.createMultisig(this.requiredCopayers, pubKeys);
|
var script = Script.createMultisig(this.requiredCopayers, pubKeys);
|
||||||
return script;
|
return script;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO this could be cached
|
/**
|
||||||
|
* @desc
|
||||||
|
* Get the address for a multisig based on the given params.
|
||||||
|
*
|
||||||
|
* Caches the address to the branch in the member addressToPath
|
||||||
|
*
|
||||||
|
* @TODO this could be cached
|
||||||
|
*
|
||||||
|
* @param {number} index - the index for the shared address
|
||||||
|
* @param {boolean} isChange - whether to derive a change address o receive address
|
||||||
|
* @param {number} copayerIndex - the index of the copayer that requested the derivation
|
||||||
|
* @returns {bitcore.Address}
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getAddress = function(index, isChange, id) {
|
PublicKeyRing.prototype.getAddress = function(index, isChange, id) {
|
||||||
var copayerIndex = this.getCosigner(id);
|
var copayerIndex = this.getCosigner(id);
|
||||||
var script = this.getRedeemScript(index, isChange, copayerIndex);
|
var script = this.getRedeemScript(index, isChange, copayerIndex);
|
||||||
|
@ -187,7 +341,17 @@ PublicKeyRing.prototype.getAddress = function(index, isChange, id) {
|
||||||
return address;
|
return address;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Overloaded to receive a PubkeyString or a consigner index
|
/**
|
||||||
|
* @desc
|
||||||
|
* Get the parameters used to derive a pubkey or a cosigner index
|
||||||
|
*
|
||||||
|
* Overloaded to receive a PubkeyString or a consigner index
|
||||||
|
*
|
||||||
|
* @TODO: Couldn't really figure out what does this do
|
||||||
|
*
|
||||||
|
* @param {number|string} id public key in hex format, or the copayer's index
|
||||||
|
* @return ????
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getHDParams = function(id) {
|
PublicKeyRing.prototype.getHDParams = function(id) {
|
||||||
var copayerIndex = this.getCosigner(id);
|
var copayerIndex = this.getCosigner(id);
|
||||||
var index = this.indexes.filter(function(i) {
|
var index = this.indexes.filter(function(i) {
|
||||||
|
@ -198,20 +362,44 @@ PublicKeyRing.prototype.getHDParams = function(id) {
|
||||||
return index[0];
|
return index[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Get the path used to derive a pubkey or a cosigner index for an address
|
||||||
|
*
|
||||||
|
* @param {string} address a multisig p2sh address
|
||||||
|
* @return {HDPath}
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.pathForAddress = function(address) {
|
PublicKeyRing.prototype.pathForAddress = function(address) {
|
||||||
var path = this.addressToPath[address];
|
var path = this.addressToPath[address];
|
||||||
if (!path) throw new Error('Couldn\'t find path for address ' + address);
|
if (!path) throw new Error('Couldn\'t find path for address ' + address);
|
||||||
return path;
|
return path;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO this could be cached
|
/**
|
||||||
|
* @desc
|
||||||
|
* Get the hexadecimal representation of a P2SH script
|
||||||
|
*
|
||||||
|
* @param {number} index - index to use when generating the address
|
||||||
|
* @param {boolean} isChange - generate a change address or a receive addres
|
||||||
|
* @param {number|string} pubkey - index of the copayer, or his public key
|
||||||
|
* @returns {string} hexadecimal encoded P2SH hash
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getScriptPubKeyHex = function(index, isChange, pubkey) {
|
PublicKeyRing.prototype.getScriptPubKeyHex = function(index, isChange, pubkey) {
|
||||||
var copayerIndex = this.getCosigner(pubkey);
|
var copayerIndex = this.getCosigner(pubkey);
|
||||||
var addr = this.getAddress(index, isChange, copayerIndex);
|
var addr = this.getAddress(index, isChange, copayerIndex);
|
||||||
return Script.createP2SH(addr.payload()).getBuffer().toString('hex');
|
return Script.createP2SH(addr.payload()).getBuffer().toString('hex');
|
||||||
};
|
};
|
||||||
|
|
||||||
//generate a new address, update index.
|
/**
|
||||||
|
* @desc
|
||||||
|
* Generates a new address and updates the last index used
|
||||||
|
*
|
||||||
|
* @param {truthy} isChange - generate a change address if true, otherwise
|
||||||
|
* generates a receive
|
||||||
|
* @param {number|string} pubkey - the pubkey for the copayer that generates the
|
||||||
|
* address (or index in the keyring)
|
||||||
|
* @returns {bitpay.Address}
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.generateAddress = function(isChange, pubkey) {
|
PublicKeyRing.prototype.generateAddress = function(isChange, pubkey) {
|
||||||
isChange = !!isChange;
|
isChange = !!isChange;
|
||||||
var HDParams = this.getHDParams(pubkey);
|
var HDParams = this.getHDParams(pubkey);
|
||||||
|
@ -221,15 +409,31 @@ PublicKeyRing.prototype.generateAddress = function(isChange, pubkey) {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Retrieve the addresses from a getAddressInfo return object
|
||||||
|
*
|
||||||
|
* {@see PublicKeyRing#getAddressInfo}
|
||||||
|
* @returns {string[]} the result of retrieving the addresses from calling
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getAddresses = function(opts) {
|
PublicKeyRing.prototype.getAddresses = function(opts) {
|
||||||
return this.getAddressesInfo(opts).map(function(info) {
|
return this.getAddressesInfo(opts).map(function(info) {
|
||||||
return info.address;
|
return info.address;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Maps a copayer's public key to his index in the keyring
|
||||||
|
*
|
||||||
|
* @param {number|string|undefined} pubKey - if undefined, returns the SHARED_INDEX
|
||||||
|
* - if a number, just return it
|
||||||
|
* - if a string, assume is the hex encoded public key
|
||||||
|
* @returns {number} the index of the copayer with the given pubkey
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getCosigner = function(pubKey) {
|
PublicKeyRing.prototype.getCosigner = function(pubKey) {
|
||||||
if (typeof pubKey == 'undefined') return HDPath.SHARED_INDEX;
|
if (_.isUndefined(pubKey)) return HDPath.SHARED_INDEX;
|
||||||
if (typeof pubKey == 'number') return pubKey;
|
if (_.isNumber(pubKey)) return pubKey;
|
||||||
|
|
||||||
var sorted = this.copayersHK.map(function(h, i) {
|
var sorted = this.copayersHK.map(function(h, i) {
|
||||||
return h.eckey.public.toString('hex');
|
return h.eckey.public.toString('hex');
|
||||||
|
@ -241,9 +445,17 @@ PublicKeyRing.prototype.getCosigner = function(pubKey) {
|
||||||
if (index == -1) throw new Error('public key is not on the ring');
|
if (index == -1) throw new Error('public key is not on the ring');
|
||||||
|
|
||||||
return index;
|
return index;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Gets information about addresses for a copayer
|
||||||
|
*
|
||||||
|
* @see PublicKeyRing#getAddressesInfoForIndex
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {string|number} pubkey - the pubkey or index of a copayer in the ring
|
||||||
|
* @returns {AddressInfo[]}
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getAddressesInfo = function(opts, pubkey) {
|
PublicKeyRing.prototype.getAddressesInfo = function(opts, pubkey) {
|
||||||
var ret = [];
|
var ret = [];
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -252,53 +464,88 @@ PublicKeyRing.prototype.getAddressesInfo = function(opts, pubkey) {
|
||||||
ret = ret.concat(self.getAddressesInfoForIndex(index, opts, copayerIndex));
|
ret = ret.concat(self.getAddressesInfoForIndex(index, opts, copayerIndex));
|
||||||
});
|
});
|
||||||
return ret;
|
return ret;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef AddressInfo
|
||||||
|
* @property {bitcore.Address} address - the address generated
|
||||||
|
* @property {string} addressStr - the base58 encoded address
|
||||||
|
* @property {boolean} isChange - true if it's a change address
|
||||||
|
* @property {boolean} owned - true if it's an address generated by a copayer
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Retrieves info about addresses generated by a copayer
|
||||||
|
*
|
||||||
|
* @param {HDParams} index - HDParams of the copayer
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {boolean} opts.excludeChange - don't append information about change addresses
|
||||||
|
* @param {boolean} opts.excludeMain - don't append information about receive addresses
|
||||||
|
* @param {string|number|undefined} copayerIndex - copayer index, pubkey, or undefined to fetch info
|
||||||
|
* about shared addresses
|
||||||
|
* @return {AddressInfo[]} a list of AddressInfo
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getAddressesInfoForIndex = function(index, opts, copayerIndex) {
|
PublicKeyRing.prototype.getAddressesInfoForIndex = function(index, opts, copayerIndex) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
|
||||||
var isOwned = index.copayerIndex == HDPath.SHARED_INDEX || index.copayerIndex == copayerIndex;
|
var isOwned = index.copayerIndex === HDPath.SHARED_INDEX || index.copayerIndex === copayerIndex;
|
||||||
|
|
||||||
var ret = [];
|
var ret = [];
|
||||||
if (!opts.excludeChange) {
|
var appendAddressInfo = function(address, isChange) {
|
||||||
for (var i = 0; i < index.changeIndex; i++) {
|
|
||||||
var a = this.getAddress(i, true, index.copayerIndex);
|
|
||||||
ret.unshift({
|
ret.unshift({
|
||||||
address: a,
|
address: address,
|
||||||
addressStr: a.toString(),
|
addressStr: address.toString(),
|
||||||
isChange: true,
|
isChange: isChange,
|
||||||
owned: isOwned
|
owned: isOwned
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if (!opts.excludeMain) {
|
for (var i = 0; !opts.excludeChange && i < index.changeIndex; i++) {
|
||||||
for (var i = 0; i < index.receiveIndex; i++) {
|
appendAddressInfo(this.getAddress(i, true, index.copayerIndex), true);
|
||||||
var a = this.getAddress(i, false, index.copayerIndex);
|
|
||||||
ret.unshift({
|
|
||||||
address: a,
|
|
||||||
addressStr: a.toString(),
|
|
||||||
isChange: false,
|
|
||||||
owned: isOwned
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
for (var i = 0; !opts.excludeMain && i < index.receiveIndex; i++) {
|
||||||
|
appendAddressInfo(this.getAddress(i, false, index.copayerIndex), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Retrieve the public keys for all cosigners for a given path
|
||||||
|
*
|
||||||
|
* @param {string} path - the BIP32 path
|
||||||
|
* @return {Buffer[]} the public keys, in buffer format
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getForPath = function(path) {
|
PublicKeyRing.prototype.getForPath = function(path) {
|
||||||
var p = HDPath.indexesForPath(path);
|
var p = HDPath.indexesForPath(path);
|
||||||
var pubKeys = this.getPubKeys(p.addressIndex, p.isChange, p.copayerIndex);
|
return this.getPubKeys(p.addressIndex, p.isChange, p.copayerIndex);
|
||||||
return pubKeys;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Retrieve the public keys for all cosigners for multiple paths
|
||||||
|
* @see PublicKeyRing#getForPath
|
||||||
|
*
|
||||||
|
* @param {string[]} paths - the BIP32 paths
|
||||||
|
* @return {Buffer[][]} the public keys, in buffer format
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.getForPaths = function(paths) {
|
PublicKeyRing.prototype.getForPaths = function(paths) {
|
||||||
preconditions.checkArgument(paths);
|
preconditions.checkArgument(!_.isUndefined(paths));
|
||||||
|
preconditions.checkArgument(_.isArray(paths));
|
||||||
|
preconditions.checkArgument(_.all(paths, _.isString));
|
||||||
|
|
||||||
return paths.map(this.getForPath.bind(this));
|
return paths.map(this.getForPath.bind(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Retrieve the public keys for derived addresses and the public keys for copayers
|
||||||
|
*
|
||||||
|
* @TODO: Should this exist? A user should just call getForPath(paths)
|
||||||
|
*
|
||||||
|
* @param {string[]} paths - the paths to be derived
|
||||||
|
* @return {Object} with keys pubKeys and copayerIds
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.forPaths = function(paths) {
|
PublicKeyRing.prototype.forPaths = function(paths) {
|
||||||
return {
|
return {
|
||||||
pubKeys: paths.map(this.getForPath.bind(this)),
|
pubKeys: paths.map(this.getForPath.bind(this)),
|
||||||
|
@ -306,8 +553,13 @@ PublicKeyRing.prototype.forPaths = function(paths) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
// returns pubkey -> copayerId.
|
* @desc
|
||||||
|
* Returns a map from a pubkey of an address to the id that generated it
|
||||||
|
*
|
||||||
|
* @param {string[]} pubkeys - the pubkeys to query
|
||||||
|
* @param {string[]} paths - the paths to query
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.copayersForPubkeys = function(pubkeys, paths) {
|
PublicKeyRing.prototype.copayersForPubkeys = function(pubkeys, paths) {
|
||||||
preconditions.checkArgument(pubkeys);
|
preconditions.checkArgument(pubkeys);
|
||||||
preconditions.checkArgument(paths);
|
preconditions.checkArgument(paths);
|
||||||
|
@ -328,71 +580,87 @@ PublicKeyRing.prototype.copayersForPubkeys = function(pubkeys, paths) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_.size(inKeyMap)) {
|
||||||
for(var i in inKeyMap)
|
for (var i in inKeyMap) {
|
||||||
throw new Error('Pubkey not identified')
|
log.error('Pubkey ' + i + ' not identified');
|
||||||
|
}
|
||||||
|
throw new Error('Pubkeys not identified');
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
// TODO this could be cached
|
* @desc
|
||||||
PublicKeyRing.prototype._addScriptMap = function(map, path) {
|
* Returns a map from address -> public key needed
|
||||||
var p = HDPath.indexesForPath(path);
|
*
|
||||||
var script = this.getRedeemScript(p.addressIndex, p.isChange, p.copayerIndex);
|
* @param {HDPath[]} paths - paths to be solved
|
||||||
map[Address.fromScript(script, this.network.name).toString()] = script.getBuffer().toString('hex');
|
* @returns {Object} a map from addresses to Buffer with the hex pubkeys
|
||||||
};
|
*/
|
||||||
|
|
||||||
PublicKeyRing.prototype.getRedeemScriptMap = function(paths) {
|
PublicKeyRing.prototype.getRedeemScriptMap = function(paths) {
|
||||||
var ret = {};
|
var ret = {};
|
||||||
for (var i = 0; i < paths.length; i++) {
|
for (var i = 0; i < paths.length; i++) {
|
||||||
var path = paths[i];
|
var path = paths[i];
|
||||||
this._addScriptMap(ret, path);
|
var p = HDPath.indexesForPath(path);
|
||||||
|
var script = this.getRedeemScript(p.addressIndex, p.isChange, p.copayerIndex);
|
||||||
|
ret[Address.fromScript(script, this.network.name).toString()] = script.getBuffer().toString('hex');
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Check if another PubKeyRing is similar to this one (checks network name,
|
||||||
|
* requiredCopayers, and totalCopayers). If ignoreId is falsy, also check that
|
||||||
|
* both walletIds match.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {PubKeyRing} inPKR - the other PubKeyRing
|
||||||
|
* @param {boolean} ignoreId - whether to ignore checking for equal walletId
|
||||||
|
* @throws {Error} if the wallets mismatch
|
||||||
|
* @return true
|
||||||
|
*/
|
||||||
|
|
||||||
PublicKeyRing.prototype._checkInPKR = function(inPKR, ignoreId) {
|
PublicKeyRing.prototype._checkInPKR = function(inPKR, ignoreId) {
|
||||||
|
|
||||||
if (!ignoreId && this.walletId !== inPKR.walletId) {
|
if (!ignoreId && this.walletId !== inPKR.walletId)
|
||||||
throw new Error('inPKR walletId mismatch');
|
throw new Error('inPKR walletId mismatch');
|
||||||
}
|
|
||||||
|
|
||||||
if (this.network.name !== inPKR.network.name) {
|
if (this.network.name !== inPKR.network.name)
|
||||||
throw new Error('Network mismatch. Should be ' + this.network.name +
|
throw new Error('Network mismatch. Should be ' + this.network.name +
|
||||||
' and found ' + inPKR.network.name);
|
' and found ' + inPKR.network.name);
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (this.requiredCopayers && inPKR.requiredCopayers &&
|
||||||
this.requiredCopayers && inPKR.requiredCopayers &&
|
|
||||||
(this.requiredCopayers !== inPKR.requiredCopayers))
|
(this.requiredCopayers !== inPKR.requiredCopayers))
|
||||||
throw new Error('inPKR requiredCopayers mismatch ' + this.requiredCopayers + '!=' + inPKR.requiredCopayers);
|
throw new Error('inPKR requiredCopayers mismatch ' + this.requiredCopayers +
|
||||||
|
'!=' + inPKR.requiredCopayers);
|
||||||
|
|
||||||
if (
|
if (this.totalCopayers && inPKR.totalCopayers &&
|
||||||
this.totalCopayers && inPKR.totalCopayers &&
|
this.totalCopayers !== inPKR.totalCopayers)
|
||||||
(this.totalCopayers !== inPKR.totalCopayers))
|
throw new Error('inPKR totalCopayers mismatch' + this.totalCopayers +
|
||||||
throw new Error('inPKR totalCopayers mismatch' + this.totalCopayers + '!=' + inPKR.requiredCopayers);
|
'!=' + inPKR.requiredCopayers);
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Merges the public keys of the wallet passed in as a parameter with ours.
|
||||||
|
*
|
||||||
|
* @param {PublicKeyRing} inPKR
|
||||||
|
* @return {boolean} true if there where changes in our internal state
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
var hasChanged = false;
|
var hasChanged = false;
|
||||||
var l = self.copayersHK.length;
|
|
||||||
|
|
||||||
if (self.isComplete())
|
if (self.isComplete())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
inPKR.copayersHK.forEach(function(b) {
|
inPKR.copayersHK.forEach(function(b) {
|
||||||
var haveIt = false;
|
|
||||||
var epk = b.extendedPublicKeyString();
|
var epk = b.extendedPublicKeyString();
|
||||||
for (var j = 0; j < l; j++) {
|
var haveIt = _.any(self.copayersHK, function(hk) { return hk.extendedPublicKeyString() === epk; });
|
||||||
if (self.copayersHK[j].extendedPublicKeyString() === epk) {
|
|
||||||
haveIt = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!haveIt) {
|
if (!haveIt) {
|
||||||
if (self.isComplete()) {
|
if (self.isComplete()) {
|
||||||
throw new Error('trying to add more pubkeys, when PKR isComplete at merge');
|
throw new Error('trying to add more pubkeys, when PKR isComplete at merge');
|
||||||
|
@ -408,27 +676,57 @@ PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
||||||
return hasChanged;
|
return hasChanged;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype.setBackupReady = function(copayerId) {
|
/**
|
||||||
|
* @desc
|
||||||
|
* Mark backup as done for us
|
||||||
|
*
|
||||||
|
* @TODO: REVIEW FUNCTIONALITY - it used to have a parameter that was not used at all!
|
||||||
|
*
|
||||||
|
* @return {boolean} true if everybody has backed up their wallet
|
||||||
|
*/
|
||||||
|
PublicKeyRing.prototype.setBackupReady = function() {
|
||||||
if (this.isBackupReady()) return false;
|
if (this.isBackupReady()) return false;
|
||||||
|
|
||||||
var cid = this.myCopayerId();
|
var cid = this.myCopayerId();
|
||||||
this.copayersBackup.push(cid);
|
this.copayersBackup.push(cid);
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc returns true if a copayer has backed up his wallet
|
||||||
|
* @param {string=} copayerId - the pubkey of a copayer, defaults to our own's
|
||||||
|
* @return {boolean} if this copayer has backed up
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.isBackupReady = function(copayerId) {
|
PublicKeyRing.prototype.isBackupReady = function(copayerId) {
|
||||||
var cid = copayerId || this.myCopayerId();
|
var cid = copayerId || this.myCopayerId();
|
||||||
return this.copayersBackup.indexOf(cid) != -1;
|
return this.copayersBackup.indexOf(cid) != -1;
|
||||||
}
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype.isFullyBackup = function(copayerId) {
|
/**
|
||||||
|
* @desc returns true if all copayers have backed up their wallets
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
PublicKeyRing.prototype.isFullyBackup = function() {
|
||||||
return this.remainingBackups() == 0;
|
return this.remainingBackups() == 0;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc returns the amount of backups remaining
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.remainingBackups = function() {
|
PublicKeyRing.prototype.remainingBackups = function() {
|
||||||
return this.totalCopayers - this.copayersBackup.length;
|
return this.totalCopayers - this.copayersBackup.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Merges this public key ring with another one, optionally ignoring the
|
||||||
|
* wallet id
|
||||||
|
*
|
||||||
|
* @param {PublicKeyRing} inPkr
|
||||||
|
* @param {boolean} ignoreId
|
||||||
|
* @return {boolean} true if the internal state has changed
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.merge = function(inPKR, ignoreId) {
|
PublicKeyRing.prototype.merge = function(inPKR, ignoreId) {
|
||||||
this._checkInPKR(inPKR, ignoreId);
|
this._checkInPKR(inPKR, ignoreId);
|
||||||
|
|
||||||
|
@ -440,6 +738,15 @@ PublicKeyRing.prototype.merge = function(inPKR, ignoreId) {
|
||||||
return !!hasChanged;
|
return !!hasChanged;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Merges the indexes for addresses generated with another copy of a list of
|
||||||
|
* HDParams
|
||||||
|
*
|
||||||
|
* @param {HDParams[]} indexes - indexes as received from another sources
|
||||||
|
* @return {boolean} true if the internal state has changed
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype.mergeIndexes = function(indexes) {
|
PublicKeyRing.prototype.mergeIndexes = function(indexes) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var hasChanged = false;
|
var hasChanged = false;
|
||||||
|
@ -452,6 +759,11 @@ PublicKeyRing.prototype.mergeIndexes = function(indexes) {
|
||||||
return !!hasChanged
|
return !!hasChanged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc merges information about backups done by another copy of PublicKeyRing
|
||||||
|
* @param {string[]} backups - another copy of backups
|
||||||
|
* @return {boolean} true if the internal state has changed
|
||||||
|
*/
|
||||||
PublicKeyRing.prototype._mergeBackups = function(backups) {
|
PublicKeyRing.prototype._mergeBackups = function(backups) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var hasChanged = false;
|
var hasChanged = false;
|
||||||
|
@ -463,7 +775,6 @@ PublicKeyRing.prototype._mergeBackups = function(backups) {
|
||||||
});
|
});
|
||||||
|
|
||||||
return !!hasChanged
|
return !!hasChanged
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
module.exports = PublicKeyRing;
|
module.exports = PublicKeyRing;
|
||||||
|
|
|
@ -4,7 +4,6 @@ var bitcore = require('bitcore');
|
||||||
var _ = require('underscore');
|
var _ = require('underscore');
|
||||||
var util = bitcore.util;
|
var util = bitcore.util;
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var BuilderMockV0 = require('./BuilderMockV0');;
|
|
||||||
var TransactionBuilder = bitcore.TransactionBuilder;
|
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||||
var Script = bitcore.Script;
|
var Script = bitcore.Script;
|
||||||
var Key = bitcore.Key;
|
var Key = bitcore.Key;
|
||||||
|
@ -134,12 +133,7 @@ TxProposal.fromObj = function(o, forceOpts) {
|
||||||
}
|
}
|
||||||
o.builder = TransactionBuilder.fromObj(o.builderObj);
|
o.builder = TransactionBuilder.fromObj(o.builderObj);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
throw new Error("Invalid or Incompatible Backup Detected.");
|
||||||
// backwards (V0) compatatibility fix.
|
|
||||||
if (!o.version) {
|
|
||||||
o.builder = new BuilderMockV0(o.builderObj);
|
|
||||||
o.readonly = 1;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return new TxProposal(o);
|
return new TxProposal(o);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var BuilderMockV0 = require('./BuilderMockV0');;
|
|
||||||
var bitcore = require('bitcore');
|
var bitcore = require('bitcore');
|
||||||
var util = bitcore.util;
|
var util = bitcore.util;
|
||||||
var Transaction = bitcore.Transaction;
|
var Transaction = bitcore.Transaction;
|
||||||
var BuilderMockV0 = require('./BuilderMockV0');;
|
var TxProposal = require('./TxProposal');
|
||||||
var TxProposal = require('./TxProposal');;
|
|
||||||
var Script = bitcore.Script;
|
var Script = bitcore.Script;
|
||||||
var Key = bitcore.Key;
|
var Key = bitcore.Key;
|
||||||
var buffertools = bitcore.buffertools;
|
var buffertools = bitcore.buffertools;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,18 +4,34 @@ var TxProposals = require('./TxProposals');
|
||||||
var PublicKeyRing = require('./PublicKeyRing');
|
var PublicKeyRing = require('./PublicKeyRing');
|
||||||
var PrivateKey = require('./PrivateKey');
|
var PrivateKey = require('./PrivateKey');
|
||||||
var Wallet = require('./Wallet');
|
var Wallet = require('./Wallet');
|
||||||
var preconditions = require('preconditions').instance();
|
var _ = require('underscore');
|
||||||
|
|
||||||
var log = require('../../log');
|
var log = require('../../log');
|
||||||
|
|
||||||
var Async = module.exports.Async = require('../network/Async');
|
var Async = module.exports.Async = require('../network/Async');
|
||||||
var Insight = module.exports.Insight = require('../blockchain/Insight');
|
var Insight = module.exports.Insight = require('../blockchain/Insight');
|
||||||
var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('../storage/LocalEncrypted');
|
var StorageLocalEncrypted = module.exports.StorageLocalEncrypted = require('../storage/LocalEncrypted');
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* WalletFactory
|
* @desc
|
||||||
|
* WalletFactory - stores the state for a wallet in creation
|
||||||
|
*
|
||||||
|
* @param {Object} config - configuration for this wallet
|
||||||
|
*
|
||||||
|
* @TODO: Don't pass a class for these three components
|
||||||
|
* -- send a factory or instance, the 'new' call considered harmful for refactoring
|
||||||
|
* -- arguable, since all of them is called with an object as argument.
|
||||||
|
* -- Still, could it be hard to refactor? (for example, what if we want to fail hard if a network call gets interrupted?)
|
||||||
|
* @param {Storage} config.Storage - the class to instantiate to store the wallet (StorageLocalEncrypted by default)
|
||||||
|
* @param {Object} config.storage - the configuration to be sent to the Storage constructor
|
||||||
|
* @param {Network} config.Network - the class to instantiate to make network requests to copayers (the Async module by default)
|
||||||
|
* @param {Object} config.network - the configuration to be sent to the Network constructor
|
||||||
|
* @param {Blockchain} config.Blockchain - the class to instantiate to get information about the blockchain (Insight by default)
|
||||||
|
* @param {Object} config.blockchain - the configuration to be sent to the Blockchain constructor
|
||||||
|
* @param {string} config.networkName - the name of the bitcoin network to use ('testnet' or 'livenet')
|
||||||
|
* @TODO: Investigate what parameters go inside this object
|
||||||
|
* @param {Object} config.wallet - default configuration for the wallet
|
||||||
|
* @TODO: put `version` inside of the config object
|
||||||
|
* @param {string} version - the version of copay for which this wallet was generated (for example, 0.4.7)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function WalletFactory(config, version) {
|
function WalletFactory(config, version) {
|
||||||
var self = this;
|
var self = this;
|
||||||
config = config || {};
|
config = config || {};
|
||||||
|
@ -31,8 +47,20 @@ function WalletFactory(config, version) {
|
||||||
this.networkName = config.networkName;
|
this.networkName = config.networkName;
|
||||||
this.walletDefaults = config.wallet;
|
this.walletDefaults = config.wallet;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc
|
||||||
|
* Returns true if the storage instance can retrieve the following keys using a given walletId
|
||||||
|
* <ul>
|
||||||
|
* <li><tt>publicKeyRing</tt></li>
|
||||||
|
* <li><tt>txProposals</tt></li>
|
||||||
|
* <li><tt>opts</tt></li>
|
||||||
|
* <li><tt>privateKey</tt></li>
|
||||||
|
* </ul>
|
||||||
|
* @param {string} walletId
|
||||||
|
* @return {boolean} true if all the keys are present in the storage instance
|
||||||
|
*/
|
||||||
WalletFactory.prototype._checkRead = function(walletId) {
|
WalletFactory.prototype._checkRead = function(walletId) {
|
||||||
var s = this.storage;
|
var s = this.storage;
|
||||||
var ret =
|
var ret =
|
||||||
|
@ -43,6 +71,12 @@ WalletFactory.prototype._checkRead = function(walletId) {
|
||||||
return !!ret;
|
return !!ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Deserialize an object to a Wallet
|
||||||
|
* @param {Object} obj
|
||||||
|
* @param {string[]} skipFields - fields to skip when importing
|
||||||
|
* @return {Wallet}
|
||||||
|
*/
|
||||||
WalletFactory.prototype.fromObj = function(obj, skipFields) {
|
WalletFactory.prototype.fromObj = function(obj, skipFields) {
|
||||||
|
|
||||||
// not stored options
|
// not stored options
|
||||||
|
@ -67,6 +101,13 @@ WalletFactory.prototype.fromObj = function(obj, skipFields) {
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Imports a wallet from an encrypted base64 object
|
||||||
|
* @param {string} base64 - the base64 encoded object
|
||||||
|
* @param {string} password - password to decrypt it
|
||||||
|
* @param {string[]} skipFields - fields to ignore when importing
|
||||||
|
* @return {Wallet}
|
||||||
|
*/
|
||||||
WalletFactory.prototype.fromEncryptedObj = function(base64, password, skipFields) {
|
WalletFactory.prototype.fromEncryptedObj = function(base64, password, skipFields) {
|
||||||
this.storage._setPassphrase(password);
|
this.storage._setPassphrase(password);
|
||||||
var walletObj = this.storage.import(base64);
|
var walletObj = this.storage.import(base64);
|
||||||
|
@ -75,14 +116,29 @@ WalletFactory.prototype.fromEncryptedObj = function(base64, password, skipFields
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @TODO: import is a reserved keyword! DONT USE IT
|
||||||
|
* @TODO: this is essentialy the same method as {@link WalletFactory#fromEncryptedObj}!
|
||||||
|
* @desc Imports a wallet from an encrypted base64 object
|
||||||
|
* @param {string} base64 - the base64 encoded object
|
||||||
|
* @param {string} password - password to decrypt it
|
||||||
|
* @param {string[]} skipFields - fields to ignore when importing
|
||||||
|
* @return {Wallet}
|
||||||
|
*/
|
||||||
WalletFactory.prototype.import = function(base64, password, skipFields) {
|
WalletFactory.prototype.import = function(base64, password, skipFields) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var w = self.fromEncryptedObj(base64, password, skipFields);
|
var w = self.fromEncryptedObj(base64, password, skipFields);
|
||||||
|
|
||||||
if (!w) throw new Error('Wrong password');
|
if (!w) throw new Error('Wrong password');
|
||||||
return w;
|
return w;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Retrieve a wallet from storage
|
||||||
|
* @param {string} walletId - the wallet id
|
||||||
|
* @param {string[]} skipFields - parameters to ignore when importing
|
||||||
|
* @return {Wallet}
|
||||||
|
*/
|
||||||
WalletFactory.prototype.read = function(walletId, skipFields) {
|
WalletFactory.prototype.read = function(walletId, skipFields) {
|
||||||
if (!this._checkRead(walletId))
|
if (!this._checkRead(walletId))
|
||||||
return false;
|
return false;
|
||||||
|
@ -103,6 +159,25 @@ WalletFactory.prototype.read = function(walletId, skipFields) {
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc This method instantiates a wallet
|
||||||
|
*
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {string} opts.id
|
||||||
|
* @param {PrivateKey=} opts.privateKey
|
||||||
|
* @param {string=} opts.privateKeyHex
|
||||||
|
* @param {number} opts.requiredCopayers
|
||||||
|
* @param {number} opts.totalCopayers
|
||||||
|
* @param {PublicKeyRing=} opts.publicKeyRing
|
||||||
|
* @param {string} opts.nickname
|
||||||
|
* @param {string} opts.passphrase
|
||||||
|
* @TODO: Figure out what is this parameter
|
||||||
|
* @param {?} opts.spendUnconfirmed this.walletDefaults.spendUnconfirmed ??
|
||||||
|
* @TODO: Figure out in what unit is this reconnect delay.
|
||||||
|
* @param {number} opts.reconnectDelay milliseconds?
|
||||||
|
* @param {number=} opts.version
|
||||||
|
* @return {Wallet}
|
||||||
|
*/
|
||||||
WalletFactory.prototype.create = function(opts) {
|
WalletFactory.prototype.create = function(opts) {
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
log.debug('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID') + (opts.privateKey ? ' USING PrivateKey: ' + opts.privateKey.getId() : ' NEW PrivateKey'));
|
log.debug('### CREATING NEW WALLET.' + (opts.id ? ' USING ID: ' + opts.id : ' NEW ID') + (opts.privateKey ? ' USING PrivateKey: ' + opts.privateKey.getId() : ' NEW PrivateKey'));
|
||||||
|
@ -156,7 +231,11 @@ WalletFactory.prototype.create = function(opts) {
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Checks if a version is compatible with the current version
|
||||||
|
* @param {string} inVersion - a version, with major, minor, and revision, period-separated (x.y.z)
|
||||||
|
* @throws {Error} if there's a major version difference
|
||||||
|
*/
|
||||||
WalletFactory.prototype._checkVersion = function(inVersion) {
|
WalletFactory.prototype._checkVersion = function(inVersion) {
|
||||||
var thisV = this.version.split('.');
|
var thisV = this.version.split('.');
|
||||||
var thisV0 = parseInt(thisV[0]);
|
var thisV0 = parseInt(thisV[0]);
|
||||||
|
@ -172,14 +251,23 @@ WalletFactory.prototype._checkVersion = function(inVersion) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Throw an error if the network name is different to {@link WalletFactory#networkName}
|
||||||
|
* @param {string} inNetworkName - the network name to check
|
||||||
|
* @throws {Error}
|
||||||
|
*/
|
||||||
WalletFactory.prototype._checkNetwork = function(inNetworkName) {
|
WalletFactory.prototype._checkNetwork = function(inNetworkName) {
|
||||||
if (this.networkName !== inNetworkName) {
|
if (this.networkName !== inNetworkName) {
|
||||||
throw new Error('This Wallet is configured for ' + inNetworkName + ' while currently Copay is configured for: ' + this.networkName + '. Check your settings.');
|
throw new Error('This Wallet is configured for ' + inNetworkName + ' while currently Copay is configured for: ' + this.networkName + '. Check your settings.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Retrieve a wallet from the storage
|
||||||
|
* @param {string} walletId - the id of the wallet
|
||||||
|
* @param {string} passphrase - the passphrase to decode it
|
||||||
|
* @return {Wallet}
|
||||||
|
*/
|
||||||
WalletFactory.prototype.open = function(walletId, passphrase) {
|
WalletFactory.prototype.open = function(walletId, passphrase) {
|
||||||
this.storage._setPassphrase(passphrase);
|
this.storage._setPassphrase(passphrase);
|
||||||
var w = this.read(walletId);
|
var w = this.read(walletId);
|
||||||
|
@ -190,6 +278,10 @@ WalletFactory.prototype.open = function(walletId, passphrase) {
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Retrieve all wallets stored without encription in the storage instance
|
||||||
|
* @returns {Wallet[]}
|
||||||
|
*/
|
||||||
WalletFactory.prototype.getWallets = function() {
|
WalletFactory.prototype.getWallets = function() {
|
||||||
var ret = this.storage.getWallets();
|
var ret = this.storage.getWallets();
|
||||||
ret.forEach(function(i) {
|
ret.forEach(function(i) {
|
||||||
|
@ -198,6 +290,14 @@ WalletFactory.prototype.getWallets = function() {
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Deletes this wallet. This involves removing it from the storage instance
|
||||||
|
* @TODO: delete is a reserved javascript keyword. NEVER USE IT.
|
||||||
|
* @param {string} walletId
|
||||||
|
* @TODO: Why is there a callback?
|
||||||
|
* @callback cb
|
||||||
|
* @return {?} the result of the callback
|
||||||
|
*/
|
||||||
WalletFactory.prototype.delete = function(walletId, cb) {
|
WalletFactory.prototype.delete = function(walletId, cb) {
|
||||||
var s = this.storage;
|
var s = this.storage;
|
||||||
s.deleteWallet(walletId);
|
s.deleteWallet(walletId);
|
||||||
|
@ -205,6 +305,9 @@ WalletFactory.prototype.delete = function(walletId, cb) {
|
||||||
return cb();
|
return cb();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Pass through to {@link Wallet#secret}
|
||||||
|
*/
|
||||||
WalletFactory.prototype.decodeSecret = function(secret) {
|
WalletFactory.prototype.decodeSecret = function(secret) {
|
||||||
try {
|
try {
|
||||||
return Wallet.decodeSecret(secret);
|
return Wallet.decodeSecret(secret);
|
||||||
|
@ -213,7 +316,26 @@ WalletFactory.prototype.decodeSecret = function(secret) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @callback walletCreationCallback
|
||||||
|
* @param {?=} err - an error, if any, that happened during the wallet creation
|
||||||
|
* @param {Wallet=} wallet - the wallet created
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Start the network functionality.
|
||||||
|
*
|
||||||
|
* Start up the Network instance and try to join a wallet defined by the
|
||||||
|
* parameter <tt>secret</tt> using the parameter <tt>nickname</tt>. Encode
|
||||||
|
* information locally using <tt>passphrase</tt>. <tt>privateHex</tt> is the
|
||||||
|
* private extended master key. <tt>cb</tt> has two params: error and wallet.
|
||||||
|
*
|
||||||
|
* @param {string} secret - the wallet secret
|
||||||
|
* @param {string} nickname - a nickname for the current user
|
||||||
|
* @param {string} passphrase - a passphrase to use to encrypt the wallet for persistance
|
||||||
|
* @param {string} privateHex - the private extended master key
|
||||||
|
* @param {walletCreationCallback} cb - a callback
|
||||||
|
*/
|
||||||
WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphrase, privateHex, cb) {
|
WalletFactory.prototype.joinCreateSession = function(secret, nickname, passphrase, privateHex, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var s = self.decodeSecret(secret);
|
var s = self.decodeSecret(secret);
|
||||||
|
|
|
@ -235,7 +235,8 @@ Network.prototype._setupConnectionHandlers = function(cb) {
|
||||||
self.socket.on('connect', function() {
|
self.socket.on('connect', function() {
|
||||||
|
|
||||||
self.socket.on('disconnect', function() {
|
self.socket.on('disconnect', function() {
|
||||||
self.cleanUp();
|
var pubKey = self.getKey().public.toString('hex');
|
||||||
|
self.socket.emit('subscribe', pubKey);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (typeof cb === 'function') cb();
|
if (typeof cb === 'function') cb();
|
||||||
|
|
|
@ -29,7 +29,7 @@ module.exports = function(config) {
|
||||||
'lib/angular-foundation/mm-foundation.min.js',
|
'lib/angular-foundation/mm-foundation.min.js',
|
||||||
'lib/angular-foundation/mm-foundation-tpls.min.js',
|
'lib/angular-foundation/mm-foundation-tpls.min.js',
|
||||||
'lib/angular-gettext/dist/angular-gettext.min.js',
|
'lib/angular-gettext/dist/angular-gettext.min.js',
|
||||||
'lib/assert/assert.js',
|
'lib/inherits/inherits.js',
|
||||||
'lib/bitcore.js',
|
'lib/bitcore.js',
|
||||||
'lib/underscore/underscore.js',
|
'lib/underscore/underscore.js',
|
||||||
'lib/crypto-js/rollups/sha256.js',
|
'lib/crypto-js/rollups/sha256.js',
|
||||||
|
|
19
package.json
19
package.json
|
@ -11,8 +11,8 @@
|
||||||
},
|
},
|
||||||
"version": "0.4.7",
|
"version": "0.4.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"assert": "^1.1.2",
|
|
||||||
"browser-request": "^0.3.2",
|
"browser-request": "^0.3.2",
|
||||||
|
"inherits": "^2.0.1",
|
||||||
"mocha": "^1.18.2",
|
"mocha": "^1.18.2",
|
||||||
"mocha-lcov-reporter": "0.0.1",
|
"mocha-lcov-reporter": "0.0.1",
|
||||||
"optimist": "^0.6.1",
|
"optimist": "^0.6.1",
|
||||||
|
@ -31,7 +31,8 @@
|
||||||
"test": "sh test/run.sh",
|
"test": "sh test/run.sh",
|
||||||
"dist": "node shell/scripts/dist.js",
|
"dist": "node shell/scripts/dist.js",
|
||||||
"sign": "gpg -u 1112CFA1 --output browser-extensions/firefox/copay.xpi.sig --detach-sig browser-extensions/firefox/copay.xpi; gpg -u 1112CFA1 --output browser-extensions/chrome/copay-chrome-extension.zip.sig --detach-sig browser-extensions/chrome/copay-chrome-extension.zip",
|
"sign": "gpg -u 1112CFA1 --output browser-extensions/firefox/copay.xpi.sig --detach-sig browser-extensions/firefox/copay.xpi; gpg -u 1112CFA1 --output browser-extensions/chrome/copay-chrome-extension.zip.sig --detach-sig browser-extensions/chrome/copay-chrome-extension.zip",
|
||||||
"verify": "gpg --verify browser-extensions/firefox/copay.xpi.sig browser-extensions/firefox/copay.xpi; gpg --verify browser-extensions/chrome/copay-chrome-extension.zip.sig browser-extensions/chrome/copay-chrome-extension.zip"
|
"verify": "gpg --verify browser-extensions/firefox/copay.xpi.sig browser-extensions/firefox/copay.xpi; gpg --verify browser-extensions/chrome/copay-chrome-extension.zip.sig browser-extensions/chrome/copay-chrome-extension.zip",
|
||||||
|
"postinstall": "./node_modules/.bin/grunt"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"wallet",
|
"wallet",
|
||||||
|
@ -41,23 +42,27 @@
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"async": "0.9.0",
|
"async": "0.9.0",
|
||||||
|
"bitcore": "0.1.35",
|
||||||
"blanket": "1.1.6",
|
"blanket": "1.1.6",
|
||||||
"browser-pack": "2.0.1",
|
"browser-pack": "2.0.1",
|
||||||
|
"browser-request": "0.3.2",
|
||||||
"browserify": "3.32.1",
|
"browserify": "3.32.1",
|
||||||
"buffertools": "2.0.1",
|
"buffertools": "2.0.1",
|
||||||
"chai": "1.9.1",
|
"chai": "1.9.1",
|
||||||
"cli-color": "0.3.2",
|
"cli-color": "0.3.2",
|
||||||
"commander": "2.1.0",
|
"commander": "2.1.0",
|
||||||
"coveralls": "2.10.0",
|
"coveralls": "2.10.0",
|
||||||
|
"crypto-js": "3.1.2",
|
||||||
"express": "4.0.0",
|
"express": "4.0.0",
|
||||||
"github-releases": "0.2.0",
|
"github-releases": "0.2.0",
|
||||||
|
"grunt": "^0.4.5",
|
||||||
"grunt-browserify": "2.0.8",
|
"grunt-browserify": "2.0.8",
|
||||||
|
"grunt-cli": "^0.1.13",
|
||||||
"grunt-contrib-concat": "0.5.0",
|
"grunt-contrib-concat": "0.5.0",
|
||||||
"grunt-contrib-cssmin": "0.10.0",
|
"grunt-contrib-cssmin": "0.10.0",
|
||||||
"grunt-contrib-uglify": "^0.5.1",
|
"grunt-contrib-uglify": "^0.5.1",
|
||||||
"grunt-contrib-watch": "0.5.3",
|
"grunt-contrib-watch": "0.5.3",
|
||||||
"grunt-markdown": "0.5.0",
|
"grunt-markdown": "0.5.0",
|
||||||
"bitcore": "0.1.35",
|
|
||||||
"grunt-mocha-test": "0.8.2",
|
"grunt-mocha-test": "0.8.2",
|
||||||
"grunt-shell": "0.6.4",
|
"grunt-shell": "0.6.4",
|
||||||
"grunt-angular-gettext": "^0.2.15",
|
"grunt-angular-gettext": "^0.2.15",
|
||||||
|
@ -71,13 +76,11 @@
|
||||||
"mocha-lcov-reporter": "0.0.1",
|
"mocha-lcov-reporter": "0.0.1",
|
||||||
"mock-fs": "^2.3.1",
|
"mock-fs": "^2.3.1",
|
||||||
"node-cryptojs-aes": "0.4.0",
|
"node-cryptojs-aes": "0.4.0",
|
||||||
|
"request": "2.40.0",
|
||||||
|
"shelljs": "0.3.0",
|
||||||
"socket.io-client": "1.0.6",
|
"socket.io-client": "1.0.6",
|
||||||
"travis-cov": "0.2.5",
|
"travis-cov": "0.2.5",
|
||||||
"uglifyify": "1.2.3",
|
"uglifyify": "1.2.3"
|
||||||
"crypto-js": "3.1.2",
|
|
||||||
"shelljs": "0.3.0",
|
|
||||||
"browser-request": "0.3.2",
|
|
||||||
"request": "2.40.0"
|
|
||||||
},
|
},
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"homepage": "https://github.com/bitpay/copay",
|
"homepage": "https://github.com/bitpay/copay",
|
||||||
|
|
|
@ -133,7 +133,7 @@ describe('TxProposal', function() {
|
||||||
builderObj: b.toObj(),
|
builderObj: b.toObj(),
|
||||||
inputChainPaths: ['m/1'],
|
inputChainPaths: ['m/1'],
|
||||||
});
|
});
|
||||||
}).should.throw('Invalid');
|
}).should.throw('Invalid or Incompatible Backup Detected');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -432,16 +432,13 @@ describe('WalletFactory model', function() {
|
||||||
wf.network.start.getCall(0).args[0].privkey.length.should.equal(64); //privkey is hex of private key buffer
|
wf.network.start.getCall(0).args[0].privkey.length.should.equal(64); //privkey is hex of private key buffer
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('dont break backwards compatibility of wallets', function() {
|
describe('break backwards compatibility with older versions', function() {
|
||||||
it('should be able to import unencrypted legacy wallet TxProposal: v0', function() {
|
it('should\'nt be able to import unencrypted legacy wallet TxProposal: v0', function() {
|
||||||
|
|
||||||
|
(function() {
|
||||||
var wf = new WalletFactory(config, '0.0.5');
|
var wf = new WalletFactory(config, '0.0.5');
|
||||||
var w = wf.fromObj(JSON.parse(legacyO));
|
var w = wf.fromObj(JSON.parse(legacyO));
|
||||||
|
}).should.throw('Invalid or Incompatible Backup Detected');
|
||||||
should.exist(w);
|
|
||||||
w.id.should.equal('55d4bd062d32f90a');
|
|
||||||
should.exist(w.publicKeyRing.getCopayerId);
|
|
||||||
should.exist(w.txProposals.toObj());
|
|
||||||
should.exist(w.privateKey.toObj());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to import simple 1-of-1 encrypted legacy testnet wallet', function(done) {
|
it('should be able to import simple 1-of-1 encrypted legacy testnet wallet', function(done) {
|
||||||
|
|
|
@ -46,9 +46,6 @@ var createBundle = function(opts) {
|
||||||
b.require('underscore', {
|
b.require('underscore', {
|
||||||
expose: 'underscore'
|
expose: 'underscore'
|
||||||
});
|
});
|
||||||
b.require('assert', {
|
|
||||||
expose: 'assert'
|
|
||||||
});
|
|
||||||
|
|
||||||
b.require('./copay', {
|
b.require('./copay', {
|
||||||
expose: 'copay'
|
expose: 'copay'
|
||||||
|
|
Loading…
Reference in New Issue