simplified and cached address derivation

This commit is contained in:
Matias Alejo Garcia 2014-11-28 18:43:22 -03:00
commit a8f0401e8e
6 changed files with 297 additions and 381 deletions

View file

@ -13,13 +13,12 @@ angular.module('copayApp.controllers').controller('ReceiveController',
var w = $rootScope.wallet; var w = $rootScope.wallet;
$scope.loading = true; $scope.loading = true;
$scope.isNewAddr = false; $scope.isNewAddr = false;
w.generateAddress(null, function() { w.generateAddress(null);
$timeout(function() { $timeout(function() {
controllerUtils.updateAddressList(); controllerUtils.updateAddressList();
$scope.loading = false; $scope.loading = false;
$scope.isNewAddr = true; $scope.isNewAddr = true;
}, 1); }, 1);
});
}; };
$scope.openAddressModal = function(address) { $scope.openAddressModal = function(address) {

View file

@ -42,10 +42,21 @@ function PublicKeyRing(opts) {
this.publicKeysCache = {}; this.publicKeysCache = {};
this.nicknameFor = opts.nicknameFor || {}; this.nicknameFor = opts.nicknameFor || {};
this.copayerIds = []; this.copayerIds = [];
this.addressToPath = {};
this.resetCache();
}; };
PublicKeyRing.prototype.resetCache = function() {
this.cache = {};
this.cache.addressToPath = {};
this.cache.receiveAddresses = [];
this.cache.changeAddresses = [];
// Non persistent cache
this._isChange = {};
};
/** /**
* @desc Returns an object with only the keys needed to rebuild a PublicKeyRing * @desc Returns an object with only the keys needed to rebuild a PublicKeyRing
* *
@ -99,9 +110,9 @@ PublicKeyRing.fromObj = function(opts) {
pkr.addCopayer(opts.copayersExtPubKeys[k]); pkr.addCopayer(opts.copayersExtPubKeys[k]);
} }
if (opts._cache){ if (opts.cache) {
log.debug('PublicKeyRing: Using address cache'); log.debug('PublicKeyRing: Using address cache');
pkr._cacheAddressMap = opts._cache; pkr.cache = opts.cache;
} }
return pkr; return pkr;
@ -129,7 +140,7 @@ PublicKeyRing.prototype.toObj = function() {
return b.extendedPublicKeyString(); return b.extendedPublicKeyString();
}), }),
nicknameFor: this.nicknameFor, nicknameFor: this.nicknameFor,
_cache: this._cacheAddressMap cache: this.cache,
}; };
}; };
@ -300,20 +311,12 @@ PublicKeyRing.prototype.addCopayer = function(newHexaExtendedPublicKey, nickname
PublicKeyRing.prototype.getPubKeys = function(index, isChange, copayerIndex) { PublicKeyRing.prototype.getPubKeys = function(index, isChange, copayerIndex) {
this._checkKeys(); this._checkKeys();
log.warn('Slow pubkey derivation...');
var path = HDPath.Branch(index, isChange, copayerIndex); var path = HDPath.Branch(index, isChange, copayerIndex);
var pubKeys = this.publicKeysCache[path]; var pubKeys = _.map(this.copayersHK, function(hdKey) {
if (!pubKeys) {
pubKeys = _.map(this.copayersHK, function(hdKey) {
return hdKey.derive(path).eckey.public; return hdKey.derive(path).eckey.public;
}); });
this.publicKeysCache[path] = pubKeys.map(function(pk) {
return pk.toString('hex');
});
} else {
pubKeys = pubKeys.map(function(s) {
return new Buffer(s, 'hex');
});
}
return pubKeys; return pubKeys;
}; };
@ -347,31 +350,24 @@ PublicKeyRing.prototype.getRedeemScript = function(index, isChange, copayerIndex
* @param {number} copayerIndex - the index of the copayer that requested the derivation * @param {number} copayerIndex - the index of the copayer that requested the derivation
* @returns {bitcore.Address} * @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);
if (!this._cachedAddress(index, isChange, id)) { var path = HDPath.FullBranch(index, isChange, copayerIndex);
console.log('[PublicKeyRing.js.338] CACHE MISS'); //TODO log.info('Generating Address:', index, isChange, copayerIndex);
var script = this.getRedeemScript(index, isChange, copayerIndex); var script = this.getRedeemScript(index, isChange, copayerIndex);
var address = Address.fromScript(script, this.network.name); var address = Address.fromScript(script, this.network.name).toString();
this.addressToPath[address.toString()] = HDPath.FullBranch(index, isChange, copayerIndex);
this._cacheAddress(index, isChange, copayerIndex, address); this._cacheAddress(address, path, isChange);
} return address;
return this._cachedAddress(index, isChange, copayerIndex);
}; };
PublicKeyRing.prototype._cacheAddress = function(index, isChange, copayerIndex, address) {
var changeIndex = isChange ? 1 : 0; PublicKeyRing.prototype._cacheAddress = function(address, path, isChange) {
if (!this._cacheAddressMap) this._cacheAddressMap = {}; this.cache.addressToPath[address] = path;
if (!this._cacheAddressMap[index]) this._cacheAddressMap[index] = {}; if (isChange)
if (!this._cacheAddressMap[index][changeIndex]) this._cacheAddressMap[index][changeIndex] = {}; this.cache.changeAddresses.push(address);
this._cacheAddressMap[index][changeIndex][copayerIndex] = address; else
}; this.cache.receiveAddresses.push(address);
PublicKeyRing.prototype._cachedAddress = function(index, isChange, copayerIndex) {
var changeIndex = isChange ? 1 : 0;
if (!this._cacheAddressMap) return undefined;
if (!this._cacheAddressMap[index]) return undefined;
if (!this._cacheAddressMap[index][changeIndex]) return undefined;
return this._cacheAddressMap[index][changeIndex][copayerIndex];
}; };
/** /**
@ -401,26 +397,12 @@ PublicKeyRing.prototype.getHDParams = function(id) {
* @return {HDPath} * @return {HDPath}
*/ */
PublicKeyRing.prototype.pathForAddress = function(address) { PublicKeyRing.prototype.pathForAddress = function(address) {
var path = this.addressToPath[address]; this._checkAndRebuildCache();
var path = this.cache.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;
}; };
/**
* @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) {
var copayerIndex = this.getCosigner(pubkey);
var addr = this.getAddress(index, isChange, copayerIndex);
return Script.createP2SH(addr.payload()).getBuffer().toString('hex');
};
/** /**
* @desc * @desc
* Generates a new address and updates the last index used * Generates a new address and updates the last index used
@ -433,26 +415,44 @@ PublicKeyRing.prototype.getScriptPubKeyHex = function(index, isChange, pubkey) {
*/ */
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);
var index = isChange ? HDParams.getChangeIndex() : HDParams.getReceiveIndex(); var index = isChange ? hdParams.getChangeIndex() : hdParams.getReceiveIndex();
var ret = this.getAddress(index, isChange, HDParams.copayerIndex); var ret = this._getAddress(index, isChange, hdParams.copayerIndex);
HDParams.increment(isChange); hdParams.increment(isChange);
return ret; return ret;
}; };
/** /**
* @desc * @desc Is an address is from this wallet?
* Retrieve the addresses from a getAddressInfo return object
* *
* {@see PublicKeyRing#getAddressInfo} * @param {string} address
* @returns {string[]} the result of retrieving the addresses from calling * @return {boolean}
*/ */
PublicKeyRing.prototype.getAddresses = function(opts) { PublicKeyRing.prototype.addressIsOwn = function(address) {
return this.getAddressesInfo(opts).map(function(info) { return !!this.cache.addressToPath[address];
return info.address;
});
}; };
/**
* @desc Is an address is a change address?
*
* @param {string} address
* @return {boolean}
*/
PublicKeyRing.prototype.addressIsChange = function(address) {
this._checkAndRebuildCache();
if (!this.cache.addressToPath[address])
return null;
//Memoization Only, never stored.
if (_.isUndefined(this._isChange[address])) {
this._isChange[address] = _.indexOf(this.cache.changeAddresses, address) >= 0;
}
return !!this._isChange[address];
};
/** /**
* @desc * @desc
* Maps a copayer's public key to his index in the keyring * Maps a copayer's public key to his index in the keyring
@ -478,79 +478,46 @@ PublicKeyRing.prototype.getCosigner = function(pubKey) {
return index; return index;
}; };
PublicKeyRing.prototype.buildAddressCache = function() {
var ret = [];
var self = this;
log.info('Rebuilding Address Cache...this will take a while');
_.each(this.indexes, function(index) {
for (var i = 0; i < index.receiveIndex; i++) {
self._getAddress(i, false, index.copayerIndex);
}
for (var i = 0; i < index.changeIndex; i++) {
self._getAddress(i, true, index.copayerIndex);
}
});
log.info('...done!');
};
PublicKeyRing.prototype._checkAndRebuildCache = function(opts) {
// If cache exists, it has to be updated
if (_.isEmpty(this.cache.addressToPath)) {
this.buildAddressCache();
}
};
/** /**
* @desc * @desc
* Gets information about addresses for a copayer * Gets information about addresses for a copayer
* *
* @see PublicKeyRing#getAddressesInfoForIndex
* @param {Object} opts * @param {Object} opts
* @param {string|number} pubkey - the pubkey or index of a copayer in the ring
* @returns {AddressInfo[]} * @returns {AddressInfo[]}
*/ */
PublicKeyRing.prototype.getAddressesInfo = function(opts, pubkey) { PublicKeyRing.prototype.getAddresses = function() {
this._checkAndRebuildCache();
console.log('[PublicKeyRing.js.474] STARTED'); //TODO var ret = this.cache.receiveAddresses.concat(this.cache.changeAddresses);
var ret = [];
var self = this;
var copayerIndex = pubkey && this.getCosigner(pubkey);
console.log('[PublicKeyRing.js.478:copayerIndex:]',copayerIndex); //TODO
this.indexes.forEach(function(index) {
console.log('[PublicKeyRing.js.479:index:]',index); //TODO
ret = ret.concat(self.getAddressesInfoForIndex(index, opts, copayerIndex));
});
console.log('[PublicKeyRing.js.474] END'); //TODO
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) {
opts = opts || {};
var isOwned = index.copayerIndex === HDPath.SHARED_INDEX || index.copayerIndex === copayerIndex;
var ret = [];
var appendAddressInfo = function(address, isChange) {
ret.push({
address: address,
addressStr: address.toString(),
isChange: isChange,
owned: isOwned
});
console.log('[PublicKeyRing.js.518] Appending address'); //TODO
};
console.log('[PublicKeyRing.js.519] getAddressesInfoForIndex'); //TODO
for (var i = 0; !opts.excludeChange && i < index.changeIndex; i++) {
appendAddressInfo(this.getAddress(i, true, index.copayerIndex), true);
}
console.log('[PublicKeyRing.js.526]'); //TODO
for (var i = 0; !opts.excludeMain && i < index.receiveIndex; i++) {
appendAddressInfo(this.getAddress(i, false, index.copayerIndex), false);
}
console.log('[PublicKeyRing.js.534] CACHE IS' , this._cacheAddressMap); //TODO
return ret;
};
/** /**
* @desc * @desc
@ -722,25 +689,6 @@ PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
return hasChanged; return hasChanged;
}; };
/**
* @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) {
this._checkInPKR(inPKR, ignoreId);
var hasChanged = false;
hasChanged |= this.mergeIndexes(inPKR.indexes);
hasChanged |= this._mergePubkeys(inPKR);
return !!hasChanged;
};
/** /**
* @desc * @desc
@ -763,4 +711,25 @@ PublicKeyRing.prototype.mergeIndexes = function(indexes) {
} }
/**
* @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) {
this._checkInPKR(inPKR, ignoreId);
var hasChanged = false;
hasChanged |= this.mergeIndexes(inPKR.indexes);
hasChanged |= this._mergePubkeys(inPKR);
return !!hasChanged;
};
module.exports = PublicKeyRing; module.exports = PublicKeyRing;

View file

@ -887,7 +887,6 @@ Wallet.prototype._lockIncomming = function() {
Wallet.prototype._setBlockchainListeners = function() { Wallet.prototype._setBlockchainListeners = function() {
console.log('[Wallet.js.889] address'); //TODO
var self = this; var self = this;
self.blockchain.removeAllListeners(); self.blockchain.removeAllListeners();
self.subscribeToAddresses(); self.subscribeToAddresses();
@ -902,14 +901,12 @@ console.log('[Wallet.js.889] address'); //TODO
log.debug('Wallet:' + self.id + 'blockchain disconnect event'); log.debug('Wallet:' + self.id + 'blockchain disconnect event');
self.emitAndKeepAlive('insightError'); self.emitAndKeepAlive('insightError');
}); });
self.blockchain.on('tx', function(tx) { self.blockchain.on('tx', function(tx) {
log.debug('Wallet:' + self.id + ' blockchain tx event'); log.debug('Wallet:' + self.id + ' blockchain tx event');
var addresses = self.getAddressesInfo(); var addresses = self.getAddresses();
var addr = _.findWhere(addresses, { if (_.indexOf(addresses,tx.address)>=0) {
addressStr: tx.address self.emitAndKeepAlive('tx', tx.address, self.addressIsChange(tx.address));
});
if (addr) {
self.emitAndKeepAlive('tx', tx.address, addr.isChange);
} }
}); });
@ -1055,6 +1052,7 @@ Wallet.prototype.toObj = function() {
settings: this.settings, settings: this.settings,
networkNonce: this.network.getHexNonce(), //yours networkNonce: this.network.getHexNonce(), //yours
networkNonces: this.network.getHexNonces(), //copayers networkNonces: this.network.getHexNonces(), //copayers
publicKeyRing: this.publicKeyRing.toObj(),
txProposals: this.txProposals.toObj(), txProposals: this.txProposals.toObj(),
privateKey: this.privateKey ? this.privateKey.toObj() : undefined, privateKey: this.privateKey ? this.privateKey.toObj() : undefined,
addressBook: this.addressBook, addressBook: this.addressBook,
@ -1376,21 +1374,15 @@ Wallet.prototype._doGenerateAddress = function(isChange) {
return this.publicKeyRing.generateAddress(isChange, this.publicKey); return this.publicKeyRing.generateAddress(isChange, this.publicKey);
}; };
/**
* @callback addressCallback
* @param {string} addr - all the addresses of the wallet
*/
/** /**
* @desc Generate a new address * @desc Generate a new address
* @param {boolean} isChange - whether to generate a change address or a receive address * @param {boolean} isChange - whether to generate a change address or a receive address
* @param {addressCallback} cb
* @return {string[]} a list of all the addresses generated so far for the wallet * @return {string[]} a list of all the addresses generated so far for the wallet
*/ */
Wallet.prototype.generateAddress = function(isChange, cb) { Wallet.prototype.generateAddress = function(isChange) {
var addr = this._doGenerateAddress(isChange); var addr = this._doGenerateAddress(isChange);
this.sendIndexes(); this.sendIndexes();
this._newAddresses(); this._newAddresses();
if (cb) return cb(addr);
return addr; return addr;
}; };
@ -1844,12 +1836,21 @@ Wallet.prototype.parsePaymentRequest = function(options, rawData) {
*/ */
Wallet.prototype._getPayProRefundOutputs = function(txp) { Wallet.prototype._getPayProRefundOutputs = function(txp) {
var pkr = this.publicKeyRing; var pkr = this.publicKeyRing;
var index = pkr.getHDParams(this.publicKey);
var amount = +txp.merchant.total.toString(10); var amount = +txp.merchant.total.toString(10);
var output = new PayPro.Output(); var output = new PayPro.Output();
var script = pkr.getScriptPubKeyHex(index.changeIndex, true, this.pubkey); var opts = JSON.parse(txp.builder.vanilla.opts);
output.set('script', new Buffer(script, 'hex')); if (!opts.remainderOut) {
log.warn('no remainder set. Not setting refund in PayPro');
return;
}
console.log('[Wallet.js.1842:builder:]',txp.builder.vanilla.opts); //TODO
var addrStr = opts.remainderOut.address;
var addr = new bitcore.Address(addrStr);
var script = bitcore.Script.createP2SH(addr.payload()).getBuffer();
log.debug('PayPro refund address set to:' + addrStr);
output.set('script', script);
output.set('amount', amount); output.set('amount', amount);
return [output]; return [output];
}; };
@ -1867,7 +1868,6 @@ Wallet.prototype.createPayProPayment = function(txp) {
var tx = txp.builder.build(); var tx = txp.builder.build();
var txBuf = tx.serialize(); var txBuf = tx.serialize();
var refund_outputs = this._getPayProRefundOutputs(txp);
// We send this to the serve after receiving a PaymentRequest // We send this to the serve after receiving a PaymentRequest
var pay = new PayPro(); var pay = new PayPro();
@ -1878,9 +1878,11 @@ Wallet.prototype.createPayProPayment = function(txp) {
merchant_data = new Buffer(merchant_data, 'hex'); merchant_data = new Buffer(merchant_data, 'hex');
pay.set('merchant_data', merchant_data); pay.set('merchant_data', merchant_data);
} }
pay.set('transactions', [txBuf]); pay.set('transactions', [txBuf]);
pay.set('refund_to', refund_outputs);
var refund_outputs = this._getPayProRefundOutputs(txp);
if (refund_outputs)
pay.set('refund_to', refund_outputs);
// Unused for now // Unused for now
// options.memo = ''; // options.memo = '';
@ -2002,28 +2004,32 @@ Wallet.prototype.getAddressesStr = function(opts) {
Wallet.prototype.subscribeToAddresses = function() { Wallet.prototype.subscribeToAddresses = function() {
if (!this.publicKeyRing.isComplete()) return; if (!this.publicKeyRing.isComplete()) return;
console.log('[Wallet.js.2002:subscribeToAddresses:]'); //TODO
var addrInfo = this.publicKeyRing.getAddressesInfo(); var addresses = this.publicKeyRing.getAddresses();
this.blockchain.subscribe(_.pluck(addrInfo, 'addressStr')); this.blockchain.subscribe(addresses);
log.debug('Subscribed to ' + addrInfo.length + ' addresses'); log.debug('Subscribed to ' + addresses.length + ' addresses');
}; };
/**
* @desc Alias for {@link PublicKeyRing#getAddressesInfo}
*/
Wallet.prototype.getAddressesInfo = function(opts) {
return this.publicKeyRing.getAddressesInfo(opts, this.publicKey);
};
/** /**
* @desc Returns true if a given address was generated by deriving our master public key * @desc Returns true if a given address was generated by deriving our master public key
* @return {boolean} * @return {boolean}
*/ */
Wallet.prototype.addressIsOwn = function(addrStr) { Wallet.prototype.addressIsOwn = function(addrStr) {
return !!this.publicKeyRing.addressToPath[addrStr]; return this.publicKeyRing.addressIsOwn(addrStr);
}; };
/**
* @desc Returns true if a given address is a change address (remainder)
* @param addrStr
* @return {boolean}
*/
Wallet.prototype.addressIsChange = function(addrStr) {
return this.publicKeyRing.addressIsChange(addrStr);
};
/** /**
* Estimate a tx fee in satoshis given its input count * Estimate a tx fee in satoshis given its input count
* (only used when spending all wallet funds) * (only used when spending all wallet funds)
@ -2110,7 +2116,7 @@ Wallet.prototype.maxRejectCount = function() {
// TODO: Can we add cache to getUnspent? // TODO: Can we add cache to getUnspent?
Wallet.prototype.getUnspent = function(cb) { Wallet.prototype.getUnspent = function(cb) {
var self = this; var self = this;
this.blockchain.getUnspent(this.getAddressesStr(), function(err, unspentList) { this.blockchain.getUnspent(this.getAddresses(), function(err, unspentList) {
if (err) { if (err) {
return cb(err); return cb(err);
@ -2372,7 +2378,8 @@ Wallet.prototype.deriveAddresses = function(index, amount, isChange, copayerInde
var ret = new Array(amount); var ret = new Array(amount);
for (var i = 0; i < amount; i++) { for (var i = 0; i < amount; i++) {
ret[i] = this.publicKeyRing.getAddress(index + i, isChange, copayerIndex).toString(); // TODO
ret[i] = this.publicKeyRing._getAddress(index + i, isChange, copayerIndex).toString();
} }
return ret; return ret;
}; };
@ -2534,7 +2541,7 @@ Wallet.prototype.getTransactionHistory = function(opts, cb) {
} }
opts = opts || {}; opts = opts || {};
var addresses = self.getAddressesInfo(); var addresses = self.getAddresses();
var proposals = self.txProposals.txps; var proposals = self.txProposals.txps;
var satToUnit = 1 / self.settings.unitToSatoshi; var satToUnit = 1 / self.settings.unitToSatoshi;
@ -2544,33 +2551,26 @@ Wallet.prototype.getTransactionHistory = function(opts, cb) {
function extractInsOuts(tx) { function extractInsOuts(tx) {
// Inputs // Inputs
var inputs = _.map(tx.vin, function(item) { var inputs = _.map(tx.vin, function(item) {
var addr = _.findWhere(addresses, {
addressStr: item.addr
});
return { return {
type: 'in', type: 'in',
address: addr ? addr.addressStr : item.addr, address: item.addr,
isMine: !_.isUndefined(addr), isMine: self.addressIsOwn(item.addr),
isChange: addr ? !!addr.isChange : false, isChange: self.addressIsChange(item.addr),
amountSat: item.valueSat, amountSat: item.valueSat,
} }
}); });
var outputs = _.map(tx.vout, function(item) { var outputs = _.map(tx.vout, function(item) {
var addr;
var itemAddr; var itemAddr;
// If classic multisig, ignore // If classic multisig, ignore
if (item.scriptPubKey && item.scriptPubKey.addresses.length == 1) { if (item.scriptPubKey && item.scriptPubKey.addresses.length == 1) {
itemAddr = item.scriptPubKey.addresses[0]; itemAddr = item.scriptPubKey.addresses[0];
addr = _.findWhere(addresses, {
addressStr: itemAddr,
});
} }
return { return {
type: 'out', type: 'out',
address: addr ? addr.addressStr : itemAddr, address: itemAddr,
isMine: !_.isUndefined(addr), isMine: self.addressIsOwn(itemAddr),
isChange: addr ? !!addr.isChange : false, isChange: self.addressIsChange(itemAddr),
label: self.addressBook[itemAddr] ? self.addressBook[itemAddr].label : undefined, label: self.addressBook[itemAddr] ? self.addressBook[itemAddr].label : undefined,
amountSat: parseInt((item.value * bitcore.util.COIN).toFixed(0)), amountSat: parseInt((item.value * bitcore.util.COIN).toFixed(0)),
} }
@ -2608,6 +2608,8 @@ Wallet.prototype.getTransactionHistory = function(opts, cb) {
var fees = parseInt((tx.fees * bitcore.util.COIN).toFixed(0)); var fees = parseInt((tx.fees * bitcore.util.COIN).toFixed(0));
var amount; var amount;
if (amountIn == (amountOut + amountOutChange + (amountIn > 0 ? fees : 0))) { if (amountIn == (amountOut + amountOutChange + (amountIn > 0 ? fees : 0))) {
tx.action = 'moved'; tx.action = 'moved';
amount = amountOut; amount = amountOut;

View file

@ -136,7 +136,8 @@ describe('PublicKeyRing model', function() {
[true, false].forEach(function(isChange) { [true, false].forEach(function(isChange) {
for (var i = 0; i < 2; i++) { for (var i = 0; i < 2; i++) {
var a = w.generateAddress(isChange, k.pub); var aStr = w.generateAddress(isChange, k.pub);
var a= new bitcore.Address(aStr);
a.isValid().should.equal(true); a.isValid().should.equal(true);
a.isScript().should.equal(true); a.isScript().should.equal(true);
a.network().name.should.equal('livenet'); a.network().name.should.equal('livenet');
@ -152,27 +153,32 @@ describe('PublicKeyRing model', function() {
var setup = getCachedW(); var setup = getCachedW();
var pubkeyring = setup.w; var pubkeyring = setup.w;
var address = pubkeyring.getAddress(3, false, 4); var address = pubkeyring._getAddress(3, false, 4);
(pubkeyring._cacheAddressMap[3][0][4]).should.equal(address); pubkeyring.cache.addressToPath[address].should.equal("m/45'/4/0/3");
_.indexOf(pubkeyring.cache.receiveAddresses,address).should.be.above(0);
_.indexOf(pubkeyring.cache.changeAddresses,address).should.be.equal(-1);
}); });
it('getAddress cache hit doesn\'t alter state', function() { it('should generate one address by default', function() {
var setup = getCachedW(); var k = createW();
var pubkeyring = setup.w; var w = k.w;
var spySave; var a = w.getAddresses();
a.length.should.equal(1);
pubkeyring.getAddress(3, false, 4);
spySave = sinon.stub(pubkeyring, '_cacheAddress');
spySave.onFirstCall().throws(new Error());
pubkeyring.getAddress(3, false, 4);
spySave.restore();
}); });
it('should return PublicKeyRing addresses', function() { it('should generate one address by default', function() {
var k = createW();
var w = k.w;
var a = w.getAddresses();
a.length.should.equal(1);
a = w.getAddresses();
a.length.should.equal(1);
});
it('should generate 4+1 addresses', function() {
var k = createW(); var k = createW();
var w = k.w; var w = k.w;
@ -184,15 +190,16 @@ describe('PublicKeyRing model', function() {
w.generateAddress(isChange, k.pub); w.generateAddress(isChange, k.pub);
} }
}); });
});
var as = w.getAddressesInfo(); it('should check isChange 4+1 addresses', function() {
as.length.should.equal(5); // include pre-generated shared one var k = createW();
for (var j in as) { var w = k.w;
var a = as[j]; var a = w.getAddresses();
a.address.isValid().should.equal(true); _.each(a, function(a, j) {
a.addressStr.should.equal(a.address.toString()); var addr = new bitcore.Address(a);
a.isChange.should.equal([false, true, true, false, false][j]); w.addressIsChange(a).should.equal([false, true, true, false, false][j]);
} });
}); });
@ -405,7 +412,7 @@ describe('PublicKeyRing model', function() {
(function() { (function() {
PublicKeyRing.fromObj(pkr); PublicKeyRing.fromObj(pkr);
}).should.throw('bad data format: Did you use .toObj()?'); }).should.throw('format');
}); });

View file

@ -8,6 +8,7 @@ var Transaction = bitcore.Transaction;
var Address = bitcore.Address; var Address = bitcore.Address;
var PayPro = bitcore.PayPro; var PayPro = bitcore.PayPro;
var Buffer = bitcore.Buffer; var Buffer = bitcore.Buffer;
var Script = bitcore.Script;
function assertObjectEqual(a, b) { function assertObjectEqual(a, b) {
@ -163,17 +164,13 @@ describe('Wallet model', function() {
should.exist(w.addressBook); should.exist(w.addressBook);
}); });
it('should provide some basic features', function(done) { it('should provide some basic features', function() {
var opts = {}; var opts = {};
var w = cachedCreateW(); var w = cachedCreateW();
addCopayers(w); addCopayers(w);
w.publicKeyRing.generateAddress(false, w.publicKey); w.publicKeyRing.generateAddress(false, w.publicKey);
w.publicKeyRing.isComplete().should.equal(true); w.publicKeyRing.isComplete().should.equal(true);
w.generateAddress(true).isValid().should.equal(true); (new bitcore.Address(w.generateAddress(true))).isValid().should.equal(true);
w.generateAddress(true, function(addr) {
addr.isValid().should.equal(true);
done();
});
}); });
var unspentTest = [{ var unspentTest = [{
@ -224,14 +221,18 @@ describe('Wallet model', function() {
return w; return w;
}; };
var unSpentTestFromWallet = function(w, addrStr) {
unspentTest[0].address = addrStr;
var a = new bitcore.Address(addrStr);
unspentTest[0].scriptPubKey = Script.createP2SH(a.payload()).getBuffer().toString('hex');
};
it('#create, fail for network', function() { it('#create, fail for network', function() {
var w = cachedCreateW2(); var w = cachedCreateW2();
unSpentTestFromWallet(unspentTest[0], w.publicKeyRing.generateAddress(true));
unspentTest[0].address = w.publicKeyRing.getAddress(1, true).toString();
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true);
var f = function() { var f = function() {
w._createTxProposal( w._createTxProposal(
'15q6HKjWHAksHcH91JW23BJEuzZgFwydBt', '15q6HKjWHAksHcH91JW23BJEuzZgFwydBt',
@ -240,15 +241,14 @@ describe('Wallet model', function() {
unspentTest unspentTest
); );
}; };
f.should.throw(Error); f.should.throw('networkname');
}); });
it('#create, check builder opts', function() { it('#create, check builder opts', function() {
var w = cachedCreateW2(); var w = cachedCreateW2();
unspentTest[0].address = w.publicKeyRing.getAddress(1, true, w.publicKey).toString(); unSpentTestFromWallet(unspentTest[0], w.publicKeyRing.generateAddress(true));
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true, w.publicKey);
var txp = w._createTxProposal( var txp = w._createTxProposal(
'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79', 'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79',
'123456789', '123456789',
@ -264,9 +264,7 @@ describe('Wallet model', function() {
it('#create, 1 sign', function() { it('#create, 1 sign', function() {
var w = cachedCreateW2(); var w = cachedCreateW2();
unSpentTestFromWallet(unspentTest[0], w.publicKeyRing.generateAddress(true));
unspentTest[0].address = w.publicKeyRing.getAddress(1, true, w.publicKey).toString();
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true, w.publicKey);
var txp = w._createTxProposal( var txp = w._createTxProposal(
'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79', 'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79',
@ -288,9 +286,7 @@ describe('Wallet model', function() {
var w = cachedCreateW2(); var w = cachedCreateW2();
var comment = 'This is a comment'; var comment = 'This is a comment';
unSpentTestFromWallet(unspentTest[0], w.publicKeyRing.generateAddress(true));
unspentTest[0].address = w.publicKeyRing.getAddress(1, true, w.publicKey).toString();
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true, w.publicKey);
var txp = w._createTxProposal( var txp = w._createTxProposal(
'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79', 'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79',
@ -308,9 +304,7 @@ describe('Wallet model', function() {
var w = cachedCreateW2(); var w = cachedCreateW2();
var comment = 'Lorem ipsum dolor sit amet, suas euismod vis te, velit deleniti vix an. Pri ex suscipit similique, inermis per'; var comment = 'Lorem ipsum dolor sit amet, suas euismod vis te, velit deleniti vix an. Pri ex suscipit similique, inermis per';
unSpentTestFromWallet(unspentTest[0], w.publicKeyRing.generateAddress(true));
unspentTest[0].address = w.publicKeyRing.getAddress(1, true, w.publicKey).toString();
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true, w.publicKey);
(function() { (function() {
w._createTxProposal( w._createTxProposal(
@ -340,8 +334,7 @@ describe('Wallet model', function() {
var ts = Date.now(); var ts = Date.now();
for (var isChange = false; !isChange; isChange = true) { for (var isChange = false; !isChange; isChange = true) {
for (var index = 0; index < 3; index++) { for (var index = 0; index < 3; index++) {
unspentTest[0].address = w.publicKeyRing.getAddress(index, isChange, w.publicKey).toString(); unSpentTestFromWallet(unspentTest[0], w.publicKeyRing.generateAddress(true));
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(index, isChange, w.publicKey);
var txp = w._createTxProposal( var txp = w._createTxProposal(
'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79', 'mgGJEugdPnvhmRuFdbdQcFfoFLc1XXeB79',
'123456789', '123456789',
@ -910,7 +903,7 @@ describe('Wallet model', function() {
w.issueTx(ntxid, function(err, txid, status) { w.issueTx(ntxid, function(err, txid, status) {
should.not.exist(err); should.not.exist(err);
txp.getSent().should.be.above(now-1); txp.getSent().should.be.above(now - 1);
txp.sentTxid.should.be.equal(txid); txp.sentTxid.should.be.equal(txid);
txid.should.equal(1234); txid.should.equal(1234);
status.should.equal(Wallet.TX_BROADCASTED); status.should.equal(Wallet.TX_BROADCASTED);
@ -1170,16 +1163,14 @@ describe('Wallet model', function() {
describe('#subscribeToAddresses', function() { describe('#subscribeToAddresses', function() {
it('should subscribe successfully', function() { it('should subscribe successfully', function() {
var w = cachedCreateW2(); var w = cachedCreateW2();
var addr1 = w.getAddresses()[0].toString();
var addr2 = w.generateAddress().toString(); var addr2 = w.generateAddress().toString();
var addr3 = w.generateAddress(true).toString(); var addr3 = w.generateAddress(true).toString();
chai.expect(w.getAddresses().length).to.equal(3);
w.blockchain.subscribe = sinon.spy(); w.blockchain.subscribe = sinon.spy();
w.subscribeToAddresses(); w.subscribeToAddresses();
w.blockchain.subscribe.calledOnce.should.equal(true); w.blockchain.subscribe.calledOnce.should.equal(true);
var arg = w.blockchain.subscribe.getCall(0).args[0]; var arg = w.blockchain.subscribe.getCall(0).args[0];
chai.expect(_.difference(arg, [addr1, addr2, addr3]).length).to.equal(0); _.intersection(arg, [addr2, addr3]).length.should.be.equal(2);
}); });
}); });
@ -1967,27 +1958,40 @@ describe('Wallet model', function() {
it('should emit notification when tx received', function(done) { it('should emit notification when tx received', function() {
var w = cachedCreateW2(); var w = cachedCreateW2();
var addr1 = w.generateAddress(false);
sinon.stub(w,'subscribeToAddresses');
w.blockchain.removeAllListeners = sinon.stub(); w.blockchain.removeAllListeners = sinon.stub();
var spy = sinon.spy(w, 'emit'); w.blockchain.on = sinon.stub();
w.generateAddress(false, function(addr1) { w.blockchain.on.withArgs('tx').yields({
w.generateAddress(true, function(addr2) { address: addr1,
w.blockchain.on = sinon.stub().withArgs('tx').yields({
address: addr1.toString(),
});
w._setBlockchainListeners();
spy.calledWith('tx', addr1.toString(), false).should.be.true;
w.blockchain.on = sinon.stub().withArgs('tx').yields({
address: addr2.toString(),
});
w._setBlockchainListeners();
spy.calledWith('tx', addr2.toString(), true).should.be.true;
done();
});
}); });
var spy = sinon.spy(w, 'emit');
w._setBlockchainListeners();
spy.calledWith('tx', addr1, false).should.equal(true);
});
it('should emit notification when tx received (change addr)', function() {
var w = cachedCreateW2();
var addr1 = w.generateAddress(true);
sinon.stub(w,'subscribeToAddresses');
w.blockchain.removeAllListeners = sinon.stub();
w.blockchain.on = sinon.stub();
w.blockchain.on.withArgs('tx').yields({
address: addr1,
});
var spy = sinon.spy(w, 'emit');
w._setBlockchainListeners();
spy.calledWith('tx', addr1, true).should.equal(true);
}); });
describe('#fromObj / #toObj', function() { describe('#fromObj / #toObj', function() {
@ -2041,13 +2045,28 @@ describe('Wallet model', function() {
should.exist(w.txProposals.toObj); should.exist(w.txProposals.toObj);
should.exist(w.privateKey.toObj); should.exist(w.privateKey.toObj);
assertObjectEqual(w.toObj(), JSON.parse(o2)); var obj = w.toObj();
// remove data from new versions
delete obj.publicKeyRing['cache'];
assertObjectEqual(obj, JSON.parse(o2));
}); });
}); });
describe('#getTransactionHistory', function() { describe('#getTransactionHistory', function() {
var w;
beforeEach(function() {
w = cachedCreateW2();
});
afterEach(function() {
if (w.publicKeyRing.addressIsOwn.restore)
w.publicKeyRing.addressIsOwn.restore();
if (w.publicKeyRing.addressIsChange.restore)
w.publicKeyRing.addressIsChange.restore();
});
it('should return list of txs', function(done) { it('should return list of txs', function(done) {
var w = cachedCreateW2();
var txs = [{ var txs = [{
vin: [{ vin: [{
addr: 'addr_in_1', addr: 'addr_in_1',
@ -2092,11 +2111,17 @@ describe('Wallet model', function() {
items: txs, items: txs,
totalItems: txs.length, totalItems: txs.length,
}); });
w.getAddressesInfo = sinon.stub().returns([{
addressStr: 'addr_in_1' sinon.stub(w,'getAddresses').returns([ 'addr_in_1', 'addr_out_2' ]);
}, { var s = sinon.stub(w.publicKeyRing,'addressIsOwn');
addressStr: 'addr_out_2' s.withArgs('addr_in_1').returns(true);
}]); s.withArgs('addr_in_2').returns(false);
s.withArgs('addr_out_2').returns(true);
var s2 = sinon.stub(w.publicKeyRing,'addressIsChange');
s2.withArgs('addr_out_1').returns(false);
s2.withArgs('addr_out_2').returns(false);
w.getTransactionHistory(function(err, res) { w.getTransactionHistory(function(err, res) {
res.should.exist; res.should.exist;
@ -2113,7 +2138,6 @@ describe('Wallet model', function() {
}); });
}); });
it('should return paginated list of txs', function(done) { it('should return paginated list of txs', function(done) {
var w = cachedCreateW2();
var txs = [{ var txs = [{
txid: 'id1', txid: 'id1',
vin: [{ vin: [{
@ -2161,11 +2185,6 @@ describe('Wallet model', function() {
items: txs.slice(2, 3), items: txs.slice(2, 3),
totalItems: txs.length, totalItems: txs.length,
}); });
w.getAddressesInfo = sinon.stub().returns([{
addressStr: 'addr_in_1'
}, {
addressStr: 'addr_out_2'
}]);
w.getTransactionHistory({ w.getTransactionHistory({
currentPage: 2, currentPage: 2,
@ -2182,17 +2201,11 @@ describe('Wallet model', function() {
}); });
}); });
it('should paginate empty list', function(done) { it('should paginate empty list', function(done) {
var w = cachedCreateW2();
var txs = []; var txs = [];
w.blockchain.getTransactions = sinon.stub().yields(null, { w.blockchain.getTransactions = sinon.stub().yields(null, {
items: txs, items: txs,
totalItems: txs.length, totalItems: txs.length,
}); });
w.getAddressesInfo = sinon.stub().returns([{
addressStr: 'addr_in_1'
}, {
addressStr: 'addr_out_2'
}]);
w.getTransactionHistory({ w.getTransactionHistory({
currentPage: 2, currentPage: 2,
@ -2207,7 +2220,6 @@ describe('Wallet model', function() {
}); });
}); });
it('should compute sent amount correctly', function(done) { it('should compute sent amount correctly', function(done) {
var w = cachedCreateW2();
var txs = [{ var txs = [{
vin: [{ vin: [{
addr: 'addr_in_1', addr: 'addr_in_1',
@ -2234,14 +2246,17 @@ describe('Wallet model', function() {
items: txs, items: txs,
totalItems: txs.length, totalItems: txs.length,
}); });
w.getAddressesInfo = sinon.stub().returns([{
addressStr: 'addr_in_1'
}, { sinon.stub(w,'getAddresses').returns([ 'addr_in_1', 'addr_in_2', 'change']);
addressStr: 'addr_in_2' var s = sinon.stub(w.publicKeyRing,'addressIsOwn');
}, { s.withArgs('addr_in_1').returns(true);
addressStr: 'change', s.withArgs('addr_in_2').returns(true);
isChange: true, s.withArgs('change').returns(true);
}]);
var s2 = sinon.stub(w.publicKeyRing,'addressIsChange');
s2.withArgs('addr_out_2').returns(false);
s2.withArgs('change').returns(true);
w.getTransactionHistory(function(err, res) { w.getTransactionHistory(function(err, res) {
res.should.exist; res.should.exist;
@ -2253,7 +2268,6 @@ describe('Wallet model', function() {
}); });
}); });
it('should compute moved amount correctly', function(done) { it('should compute moved amount correctly', function(done) {
var w = cachedCreateW2();
var txs = [{ var txs = [{
vin: [{ vin: [{
addr: 'addr_1', addr: 'addr_1',
@ -2280,14 +2294,16 @@ describe('Wallet model', function() {
items: txs, items: txs,
totalItems: txs.length, totalItems: txs.length,
}); });
w.getAddressesInfo = sinon.stub().returns([{
addressStr: 'addr_1' sinon.stub(w,'getAddresses').returns([ 'addr_in_1', 'addr_in_2', 'change']);
}, { var s = sinon.stub(w.publicKeyRing,'addressIsOwn');
addressStr: 'addr_2' s.withArgs('addr_1').returns(true);
}, { s.withArgs('addr_2').returns(true);
addressStr: 'change', s.withArgs('change').returns(true);
isChange: true,
}]); var s2 = sinon.stub(w.publicKeyRing,'addressIsChange');
s2.withArgs('addr_1').returns(false);
s2.withArgs('change').returns(true);
w.getTransactionHistory(function(err, res) { w.getTransactionHistory(function(err, res) {
res.should.exist; res.should.exist;
@ -2299,7 +2315,6 @@ describe('Wallet model', function() {
}); });
}); });
it('should assign label when address in address book', function(done) { it('should assign label when address in address book', function(done) {
var w = cachedCreateW2();
var txs = [{ var txs = [{
vin: [{ vin: [{
addr: 'addr_in_1', addr: 'addr_in_1',
@ -2349,7 +2364,6 @@ describe('Wallet model', function() {
}); });
}); });
it('should assign comment from tx proposal if found', function(done) { it('should assign comment from tx proposal if found', function(done) {
var w = cachedCreateW2();
var txs = [{ var txs = [{
txid: 'id1', txid: 'id1',
vin: [{ vin: [{
@ -2517,7 +2531,7 @@ describe('Wallet model', function() {
// DATA // DATA
var o = '{"opts":{"id":"dbfe10c3fae71cea", "spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2,"changeIndex":0,"receiveIndex":0}],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{},"settings":{"unitName":"BTC","unitToSatoshi":100000000,"unitDecimals":8,"alternativeName":"Argentine Peso","alternativeIsoCode":"ARS"}}'; var o = '{"opts":{"id":"dbfe10c3fae71cea", "spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5","networkName":"testnet"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{ "cache": { "addressToPath": {}, "changeAddresses": [], "receiveAddresses": [] }, "walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2,"changeIndex":0,"receiveIndex":0}],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{},"settings":{"unitName":"BTC","unitToSatoshi":100000000,"unitDecimals":8,"alternativeName":"Argentine Peso","alternativeIsoCode":"ARS"}}';
}); });

View file

@ -1,75 +0,0 @@
'use strict';
var PrivateKey = copay.PrivateKey;
var PublicKeyRing = copay.PublicKeyRing;
var getNewEpk = function() {
return new PrivateKey({
networkName: 'livenet',
})
.deriveBIP45Branch()
.extendedPublicKeyString();
}
describe('Performance tests', function() {
describe('PrivateKey', function() {
it('should optimize BIP32 private key gen time with cache', function() {
var k1 = new PrivateKey();
var generateN = 25;
var generated = [];
var start1 = new Date().getTime();
for (var i = 0; i < generateN; i++) {
var k = JSON.stringify(k1.get(i, false).storeObj());
generated.push(k);
}
var delta1 = new Date().getTime() - start1;
var start2 = new Date().getTime();
for (var i = 0; i < generateN; i++) {
var k = JSON.stringify(k1.get(i, false).storeObj());
generated[i].should.equal(k);
}
var delta2 = new Date().getTime() - start2;
delta2.should.be.below(delta1);
});
});
describe('PublicKeyRing', function() {
var maxN = 7;
for (var n = 1; n < maxN; n++) {
for (var m = 1; m <= n; m++) {
if ((m === 3 && n === 5) ||
(m === 2 && n === 3)) {
var M = m;
var N = n;
(function(M, N) {
it('should optimize BIP32 publickey gen time with cache for ' + M + '-of-' + N, function() {
var pkr1 = new PublicKeyRing({
totalCopayers: N,
requiredCopayers: M
});
for (var i = 0; i < N; i++) {
pkr1.addCopayer(getNewEpk()); // add new random ext public key
}
var generateN = 5;
var generated = [];
var start1 = new Date().getTime();
for (var i = 0; i < generateN; i++) {
var pubKeys = JSON.stringify(pkr1.getPubKeys(i, false));
generated.push(pubKeys);
}
var delta1 = new Date().getTime() - start1;
var start2 = new Date().getTime();
for (var i = 0; i < generateN; i++) {
var pubKeys = JSON.stringify(pkr1.getPubKeys(i, false));
generated[i].should.equal(pubKeys);
}
var delta2 = new Date().getTime() - start2;
delta2.should.be.below(delta1);
});
})(M, N);
}
}
}
});
});