This commit is contained in:
Matias Alejo Garcia 2014-07-30 21:20:08 -03:00
commit 72e1dfc114
6 changed files with 221 additions and 63 deletions

View file

@ -1,6 +1,7 @@
// core // core
module.exports.PublicKeyRing = require('./js/models/core/PublicKeyRing'); module.exports.PublicKeyRing = require('./js/models/core/PublicKeyRing');
module.exports.TxProposals = require('./js/models/core/TxProposals'); module.exports.TxProposal = require('./js/models/core/TxProposal');
module.exports.TxProposalsSet = require('./js/models/core/TxProposalsSet');
module.exports.PrivateKey = require('./js/models/core/PrivateKey'); module.exports.PrivateKey = require('./js/models/core/PrivateKey');
module.exports.Passphrase = require('./js/models/core/Passphrase'); module.exports.Passphrase = require('./js/models/core/Passphrase');
module.exports.HDPath = require('./js/models/core/HDPath'); module.exports.HDPath = require('./js/models/core/HDPath');

View file

@ -33,12 +33,12 @@ HDPath.FullBranch = function(addressIndex, isChange, copayerIndex) {
return BIP45_PUBLIC_PREFIX + '/' + sub; return BIP45_PUBLIC_PREFIX + '/' + sub;
}; };
HDPath.indicesForPath = function(path) { HDPath.indexesForPath = function(path) {
preconditions.shouldBeString(path); preconditions.shouldBeString(path);
var s = path.split('/'); var s = path.split('/');
return { return {
isChange: s[3] === '1', isChange: s[3] === '1',
index: parseInt(s[4]), addressIndex: parseInt(s[4]),
copayerIndex: parseInt(s[2]) copayerIndex: parseInt(s[2])
}; };
}; };

View file

@ -0,0 +1,160 @@
'use strict';
var BuilderMockV0 = require('./BuilderMockV0');;
var bitcore = require('bitcore');
var util = bitcore.util;
var Transaction = bitcore.Transaction;
var BuilderMockV0 = require('./BuilderMockV0');;
var Script = bitcore.Script;
var Key = bitcore.Key;
var buffertools = bitcore.buffertools;
var preconditions = require('preconditions').instance();
function TxProposalsSet(opts) {
opts = opts || {};
this.walletId = opts.walletId;
this.network = opts.networkName === 'livenet' ?
bitcore.networks.livenet : bitcore.networks.testnet;
this.txps = {};
}
TxProposalsSet.fromObj = function(o, forceOpts) {
var ret = new TxProposalsSet({
networkName: o.networkName,
walletId: o.walletId,
});
o.txps.forEach(function(o2) {
var t = TxProposal.fromObj(o2, forceOpts);
if (t.builder) {
var id = t.getID();
ret.txps[id] = t;
}
});
return ret;
};
TxProposalsSet.prototype.getNtxids = function() {
return Object.keys(this.txps);
};
TxProposalsSet.prototype.toObj = function(onlyThisNtxid) {
if (onlyThisNtxid) throw new Error();
var ret = [];
for (var id in this.txps) {
if (onlyThisNtxid && id != onlyThisNtxid)
continue;
var t = this.txps[id];
if (!t.sent)
ret.push(t.toObj());
}
return {
txps: ret,
walletId: this.walletId,
networkName: this.network.name,
};
};
TxProposalsSet.prototype.mergeFromObj = function(txProposalObj, allowedPubKeys, opts) {
var inTxp = TxProposal.fromObj(txProposalObj, opts);
var mergeInfo = this.txProposals.merge(inTxp, allowedPubKeys);
mergeInfo.inTxp = inTxp;
return mergeInfo;
};
TxProposalsSet.prototype.merge = function(inTxp, allowedPubKeys) {
var myTxps = this.txps;
var ntxid = inTxp.getID();
var ret = {};
ret.events = [];
ret.events.hasChanged = false;
if (myTxps[ntxid]) {
var v0 = myTxps[ntxid];
var v1 = inTxp;
ret = v0.merge(v1, allowedPubKeys);
} else {
this.txps[ntxid] = inTxp;
ret.hasChanged = true;
ret.events.push({
type: 'new',
cid: inTxp.creator,
tx: ntxid
});
}
return ret;
};
// Add a LOCALLY CREATED (trusted) tx proposal
TxProposalsSet.prototype.add = function(data) {
var txp = new TxProposal(data);
var ntxid = txp.getID();
this.txps[ntxid] = txp;
return ntxid;
};
TxProposalsSet.prototype.setSent = function(ntxid, txid) {
//sent TxProposalsSet are local an not broadcasted.
this.txps[ntxid].setSent(txid);
};
TxProposalsSet.prototype.getTxProposal = function(ntxid, copayers) {
var txp = this.txps[ntxid];
var i = JSON.parse(JSON.stringify(txp));
i.builder = txp.builder;
i.ntxid = ntxid;
i.peerActions = {};
if (copayers) {
for (var j = 0; j < copayers.length; j++) {
var p = copayers[j];
i.peerActions[p] = {};
}
}
for (var p in txp.seenBy) {
i.peerActions[p] = {
seen: txp.seenBy[p]
};
}
for (var p in txp.signedBy) {
i.peerActions[p] = i.peerActions[p] || {};
i.peerActions[p].sign = txp.signedBy[p];
}
var r = 0;
for (var p in txp.rejectedBy) {
i.peerActions[p] = i.peerActions[p] || {};
i.peerActions[p].rejected = txp.rejectedBy[p];
r++;
}
i.rejectCount = r;
var c = txp.creator;
i.peerActions[c] = i.peerActions[c] || {};
i.peerActions[c].create = txp.createdTs;
return i;
};
//returns the unspent txid-vout used in PENDING Txs
TxProposalsSet.prototype.getUsedUnspent = function(maxRejectCount) {
var ret = {};
for (var i in this.txps) {
var u = this.txps[i].builder.getSelectedUnspent();
var p = this.getTxProposal(i);
if (p.rejectCount > maxRejectCount || p.sentTxid)
continue;
for (var j in u) {
ret[u[j].txid + ',' + u[j].vout] = 1;
}
}
return ret;
};

View file

@ -17,7 +17,7 @@ var Address = bitcore.Address;
var HDParams = require('./HDParams'); var HDParams = require('./HDParams');
var PublicKeyRing = require('./PublicKeyRing'); var PublicKeyRing = require('./PublicKeyRing');
var TxProposals = require('./TxProposals'); var TxProposalsSet = require('./TxProposalsSet');
var PrivateKey = require('./PrivateKey'); var PrivateKey = require('./PrivateKey');
var copayConfig = require('../../../config'); var copayConfig = require('../../../config');
@ -36,7 +36,7 @@ function Wallet(opts) {
}); });
if (copayConfig.forceNetwork && this.getNetworkName() !== copayConfig.networkName) if (copayConfig.forceNetwork && this.getNetworkName() !== copayConfig.networkName)
throw new Error('Network forced to ' + copayConfig.networkName + throw new Error('Network forced to ' + copayConfig.networkName +
' and tried to create a Wallet with network ' + this.getNetworkName()); ' and tried to create a Wallet with network ' + this.getNetworkName());
this.log('creating ' + opts.requiredCopayers + ' of ' + opts.totalCopayers + ' wallet'); this.log('creating ' + opts.requiredCopayers + ' of ' + opts.totalCopayers + ' wallet');
@ -59,10 +59,10 @@ function Wallet(opts) {
Wallet.builderOpts = { Wallet.builderOpts = {
lockTime: null, lockTime: null,
signhash: bitcore.Transaction.SIGNHASH_ALL, signhash: bitcore.Transaction.SIGNHASH_ALL,
fee: null, fee: null,
feeSat: null, feeSat: null,
}; };
Wallet.parent = EventEmitter; Wallet.parent = EventEmitter;
@ -129,28 +129,25 @@ Wallet.prototype._handlePublicKeyRing = function(senderId, data, isInbound) {
}; };
Wallet.prototype._handleTxProposal = function(senderId, data) { Wallet.prototype._handleTxProposal = function(senderId, data) {
this.log('RECV TXPROPOSAL: ', data); this.log('RECV TXPROPOSAL: ', data);
var inTxp = TxProposals.TxProposal.fromObj(data.txProposal, Wallet.builderOpts); var mergeInfo;
try {
mergeInfo = this.txProposals.mergeFromObj(data.txProposal, senderId, Wallet.builderOpts);
var valid = inTxp.isValid(); } catch (e) {
if (!valid) {
var corruptEvent = { var corruptEvent = {
type: 'corrupt', type: 'corrupt',
cId: inTxp.creator cId: mergeInfo.inTxp.creator
}; };
this.emit('txProposalEvent', corruptEvent); this.emit('txProposalEvent', corruptEvent);
return; return;
} }
var mergeInfo = this.txProposals.merge(inTxp, senderId);
var added = this.addSeenToTxProposals();
var added = this.addSeenToTxProposals();
if (added) { if (added) {
this.log('### BROADCASTING txProposals with my seenBy updated.'); this.log('### BROADCASTING txProposals with my seenBy updated.');
this.sendTxProposal(inTxp.getID()); this.sendTxProposal(mergeInfo.inTxp.getID());
} }
this.emit('txProposalsUpdated'); this.emit('txProposalsUpdated');
@ -193,24 +190,24 @@ Wallet.prototype._handleData = function(senderId, data, isInbound) {
// This handler is repeaded on WalletFactory (#join). TODO // This handler is repeaded on WalletFactory (#join). TODO
case 'walletId': case 'walletId':
this.sendWalletReady(senderId); this.sendWalletReady(senderId);
break; break;
case 'walletReady': case 'walletReady':
this.sendPublicKeyRing(senderId); this.sendPublicKeyRing(senderId);
this.sendAddressBook(senderId); this.sendAddressBook(senderId);
this.sendAllTxProposals(senderId); // send old txps this.sendAllTxProposals(senderId); // send old txps
break; break;
case 'publicKeyRing': case 'publicKeyRing':
this._handlePublicKeyRing(senderId, data, isInbound); this._handlePublicKeyRing(senderId, data, isInbound);
break; break;
case 'txProposal': case 'txProposal':
this._handleTxProposal(senderId, data, isInbound); this._handleTxProposal(senderId, data, isInbound);
break; break;
case 'indexes': case 'indexes':
this._handleIndexes(senderId, data, isInbound); this._handleIndexes(senderId, data, isInbound);
break; break;
case 'addressbook': case 'addressbook':
this._handleAddressBook(senderId, data, isInbound); this._handleAddressBook(senderId, data, isInbound);
break; break;
} }
}; };
@ -389,7 +386,7 @@ Wallet.fromObj = function(o, storage, network, blockchain) {
opts.addressBook = o.addressBook; opts.addressBook = o.addressBook;
opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing); opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing);
opts.txProposals = TxProposals.fromObj(o.txProposals, Wallet.builderOpts); opts.txProposals = TxProposalsSet.fromObj(o.txProposals, Wallet.builderOpts);
opts.privateKey = PrivateKey.fromObj(o.privateKey); opts.privateKey = PrivateKey.fromObj(o.privateKey);
opts.storage = storage; opts.storage = storage;
@ -497,7 +494,7 @@ Wallet.prototype.generateAddress = function(isChange, cb) {
}; };
Wallet.prototype.getTxProposals = function() { Wallet.prototype.getTxProposalsSet = function() {
var ret = []; var ret = [];
var copayers = this.getRegisteredCopayerIds(); var copayers = this.getRegisteredCopayerIds();
for (var ntxid in this.txProposals.txps) { for (var ntxid in this.txProposals.txps) {
@ -734,11 +731,11 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
} }
var b = new Builder(opts) var b = new Builder(opts)
.setUnspent(utxos) .setUnspent(utxos)
.setOutputs([{ .setOutputs([{
address: toAddress, address: toAddress,
amountSatStr: amountSatStr, amountSatStr: amountSatStr,
}]); }]);
var selectedUtxos = b.getSelectedUnspent(); var selectedUtxos = b.getSelectedUnspent();
var inputChainPaths = selectedUtxos.map(function(utxo) { var inputChainPaths = selectedUtxos.map(function(utxo) {
@ -831,29 +828,29 @@ Wallet.prototype.indexDiscovery = function(start, change, cosigner, gap, cb) {
var self = this; var self = this;
async.doWhilst( async.doWhilst(
function _do(next) { function _do(next) {
// Optimize window to minimize the derivations. // Optimize window to minimize the derivations.
var scanWindow = (lastActive == -1) ? gap : gap - (scanIndex - lastActive) + 1; var scanWindow = (lastActive == -1) ? gap : gap - (scanIndex - lastActive) + 1;
var addresses = self.deriveAddresses(scanIndex, scanWindow, change, cosigner); var addresses = self.deriveAddresses(scanIndex, scanWindow, change, cosigner);
self.blockchain.checkActivity(addresses, function(err, actives) { self.blockchain.checkActivity(addresses, function(err, actives) {
if (err) throw err; if (err) throw err;
// Check for new activities in the newlly scanned addresses // Check for new activities in the newlly scanned addresses
var recentActive = actives.reduce(function(r, e, i) { var recentActive = actives.reduce(function(r, e, i) {
return e ? scanIndex + i : r; return e ? scanIndex + i : r;
}, lastActive); }, lastActive);
hasActivity = lastActive != recentActive; hasActivity = lastActive != recentActive;
lastActive = recentActive; lastActive = recentActive;
scanIndex += scanWindow; scanIndex += scanWindow;
next(); next();
}); });
}, },
function _while() { function _while() {
return hasActivity; return hasActivity;
}, },
function _finnaly(err) { function _finnaly(err) {
if (err) return cb(err); if (err) return cb(err);
cb(null, lastActive); cb(null, lastActive);
} }
); );
} }

View file

@ -2,7 +2,7 @@
var imports = require('soop').imports(); var imports = require('soop').imports();
var TxProposals = require('./TxProposals'); var TxProposalsSet = require('./TxProposalsSet');
var PublicKeyRing = require('./PublicKeyRing'); var PublicKeyRing = require('./PublicKeyRing');
var PrivateKey = require('./PrivateKey'); var PrivateKey = require('./PrivateKey');
var Wallet = require('./Wallet'); var Wallet = require('./Wallet');
@ -124,10 +124,10 @@ WalletFactory.prototype.create = function(opts) {
opts.nickname); opts.nickname);
this.log('\t### PublicKeyRing Initialized'); this.log('\t### PublicKeyRing Initialized');
opts.txProposals = opts.txProposals || new TxProposals({ opts.txProposals = opts.txProposals || new TxProposalsSet({
networkName: this.networkName, networkName: this.networkName,
}); });
this.log('\t### TxProposals Initialized'); this.log('\t### TxProposalsSet Initialized');
this.storage._setPassphrase(opts.passphrase); this.storage._setPassphrase(opts.passphrase);

View file

@ -70,9 +70,9 @@ describe('HDPath model', function() {
].forEach(function(datum) { ].forEach(function(datum) {
var path = datum[0]; var path = datum[0];
var result = datum[1]; var result = datum[1];
it('should get the correct indices for path ' + path, function() { it('should get the correct indexes for path ' + path, function() {
var i = HDPath.indicesForPath(path); var i = HDPath.indexesForPath(path);
i.index.should.equal(result.index); i.addressIndex.should.equal(result.index);
i.isChange.should.equal(result.isChange); i.isChange.should.equal(result.isChange);
}); });
}); });