add rebroadcast test

This commit is contained in:
Matias Alejo Garcia 2014-08-05 16:25:02 -03:00
commit 5a2dfe690d
5 changed files with 131 additions and 66 deletions

View file

@ -2,6 +2,7 @@
var imports = require('soop').imports(); var imports = require('soop').imports();
var bitcore = require('bitcore'); var bitcore = require('bitcore');
var coinUtil = bitcore.util;
var preconditions = require('preconditions').singleton(); var preconditions = require('preconditions').singleton();
var http; var http;
@ -39,9 +40,9 @@ function _asyncForEach(array, fn, callback) {
function removeRepeatedElements(ar) { function removeRepeatedElements(ar) {
var ya = false, var ya = false,
v = "", v = "",
aux = [].concat(ar), aux = [].concat(ar),
r = Array(); r = Array();
for (var i in aux) { // for (var i in aux) { //
v = aux[i]; v = aux[i];
ya = false; ya = false;
@ -78,6 +79,25 @@ Insight.prototype._getOptions = function(method, path, data) {
}; };
}; };
// This is vulneable to txid maneability
// TODO: if ret = false,
// check output address from similar transactions.
//
Insight.prototype.checkSentTx = function(tx, cb) {
var hash = coinUtil.formatHashFull(tx.getHash());
var options = this._getOptions('GET', '/api/tx/' + hash);
this._request(options, function(err, res) {
if (err) return cb(err);
var ret = false;
if (res && res.txid === hash) {
ret = hash;
}
return cb(err, ret);
});
};
Insight.prototype.getTransactions = function(addresses, cb) { Insight.prototype.getTransactions = function(addresses, cb) {
preconditions.shouldBeArray(addresses); preconditions.shouldBeArray(addresses);
preconditions.shouldBeFunction(cb); preconditions.shouldBeFunction(cb);
@ -164,8 +184,8 @@ Insight.prototype.checkActivity = function(addresses, cb) {
var getOutputs = function(t) { var getOutputs = function(t) {
return flatArray( return flatArray(
t.vout.map(function(vout) { t.vout.map(function(vout) {
return vout.scriptPubKey.addresses; return vout.scriptPubKey.addresses;
}) })
); );
}; };

View file

@ -74,7 +74,7 @@ TxProposals.prototype.merge = function(inObj, builderOpts) {
} else { } else {
// Create a new one // Create a new one
ret.new = 1; ret.new = ret.hasChanged = 1;
this.txps[ntxid] = incomingTx; this.txps[ntxid] = incomingTx;
} }
@ -91,16 +91,16 @@ TxProposals.prototype.add = function(txp) {
}; };
TxProposals.prototype._getTxp = function(ntxid) { TxProposals.prototype.get = function(ntxid) {
var ret = this.txps[ntxid]; var ret = this.txps[ntxid];
if (!ret) if (!ret)
throw new Error('Could not find txp: '+ntxid); throw new Error('Unknown TXP: '+ntxid);
return ret; return ret;
}; };
TxProposals.prototype.getTxProposal = function(ntxid, copayers) { TxProposals.prototype.getTxProposal = function(ntxid, copayers) {
var txp = this._getTxp(ntxid); var txp = this.get(ntxid);
var i = JSON.parse(JSON.stringify(txp)); var i = JSON.parse(JSON.stringify(txp));
i.builder = txp.builder; i.builder = txp.builder;
@ -139,12 +139,12 @@ TxProposals.prototype.getTxProposal = function(ntxid, copayers) {
TxProposals.prototype.reject = function(ntxid, copayerId) { TxProposals.prototype.reject = function(ntxid, copayerId) {
var txp = this._getTxp(ntxid); var txp = this.get(ntxid);
txp.setRejected(copayerId); txp.setRejected(copayerId);
}; };
TxProposals.prototype.seen = function(ntxid, copayerId) { TxProposals.prototype.seen = function(ntxid, copayerId) {
var txp = this._getTxp(ntxid); var txp = this.get(ntxid);
txp.setSeen(copayerId); txp.setSeen(copayerId);
}; };

View file

@ -138,8 +138,8 @@ Wallet.prototype._processProposalEvents = function(senderId, m) {
type: 'new', type: 'new',
cid: senderId cid: senderId
} }
} else if(m.newCopayer){ } else if (m.newCopayer) {
ev={ ev = {
type: 'signed', type: 'signed',
cid: m.newCopayer cid: m.newCopayer
}; };
@ -187,29 +187,54 @@ Wallet.prototype._getKeyMap = function(txp) {
}; };
Wallet.prototype._checkSentTx = function(ntxid, cb) {
var txp = this.txProposals.get(ntxid);
var tx = txp.builder.build();
this.blockchain.checkSentTx(tx, function(err, txid) {
var ret = false;
if (txid) {
txp.setSent(txid);
ret = txid;
}
return cb(ret);
});
};
Wallet.prototype._handleTxProposal = function(senderId, data) { Wallet.prototype._handleTxProposal = function(senderId, data) {
var self = this;
this.log('RECV TXPROPOSAL: ', data); this.log('RECV TXPROPOSAL: ', data);
var m; var m;
try { try {
m = this.txProposals.merge(data.txProposal, Wallet.builderOpts); m = this.txProposals.merge(data.txProposal, Wallet.builderOpts);
var keyMap = this._getKeyMap(m.txp); var keyMap = this._getKeyMap(m.txp);
ret.newCopayer = m.txp.setCopayers(senderId, keyMap); ret.newCopayer = m.txp.setCopayers(senderId, keyMap);
} catch (e) { } catch (e) {
this.log('Corrupt TX proposal received from:', senderId, e); this.log('Corrupt TX proposal received from:', senderId, e);
} }
if (m) { if (m) {
if (m.hasChanged) {
this.sendSeen(m.ntxid);
var tx = m.txp.builder.build();
if (tx.isComplete()) {
this._checkSentTx(m.ntxid, function(ret) {
if (ret) {
self.emit('txProposalsUpdated');
self.store();
}
});
} else {
this.sendTxProposal(m.ntxid);
}
}
this.emit('txProposalsUpdated'); this.emit('txProposalsUpdated');
this.store(); this.store();
this.sendSeen(m.ntxid);
if (m.hasChanged)
this.sendTxProposal(m.ntxid);
} }
this._processProposalEvents(senderId, m); this._processProposalEvents(senderId, m);
}; };
@ -218,7 +243,7 @@ Wallet.prototype._handleReject = function(senderId, data, isInbound) {
preconditions.checkState(data.ntxid); preconditions.checkState(data.ntxid);
this.log('RECV REJECT:', data); this.log('RECV REJECT:', data);
var txp = this.txProposals.txps[data.ntxid]; var txp = this.txProposals.get(data.ntxid);
if (!txp) if (!txp)
throw new Error('Received Reject for an unknown TX from:' + senderId); throw new Error('Received Reject for an unknown TX from:' + senderId);
@ -241,11 +266,7 @@ Wallet.prototype._handleSeen = function(senderId, data, isInbound) {
preconditions.checkState(data.ntxid); preconditions.checkState(data.ntxid);
this.log('RECV SEEN:', data); this.log('RECV SEEN:', data);
var txp = this.txProposals.txps[data.ntxid]; var txp = this.txProposals.get(data.ntxid);
if (!txp)
throw new Error('Received Reject for an unknown TX from:' + senderId);
txp.setSeen(senderId); txp.setSeen(senderId);
this.store(); this.store();
this.emit('txProposalsUpdated'); this.emit('txProposalsUpdated');
@ -524,12 +545,11 @@ Wallet.prototype.sendAllTxProposals = function(recipients) {
Wallet.prototype.sendTxProposal = function(ntxid, recipients) { Wallet.prototype.sendTxProposal = function(ntxid, recipients) {
preconditions.checkArgument(ntxid); preconditions.checkArgument(ntxid);
preconditions.checkState(this.txProposals.txps[ntxid]);
this.log('### SENDING txProposal ' + ntxid + ' TO:', recipients || 'All', this.txProposals); this.log('### SENDING txProposal ' + ntxid + ' TO:', recipients || 'All', this.txProposals);
this.send(recipients, { this.send(recipients, {
type: 'txProposal', type: 'txProposal',
txProposal: this.txProposals.txps[ntxid].toObjTrim(), txProposal: this.txProposals.get(ntxid).toObjTrim(),
walletId: this.id, walletId: this.id,
}); });
}; };
@ -644,7 +664,7 @@ Wallet.prototype.getTxProposals = function() {
Wallet.prototype.reject = function(ntxid) { Wallet.prototype.reject = function(ntxid) {
var txp = this.txProposals.reject(ntxid, this.getMyCopayerId()) ; var txp = this.txProposals.reject(ntxid, this.getMyCopayerId());
this.sendReject(ntxid); this.sendReject(ntxid);
this.store(); this.store();
this.emit('txProposalsUpdated'); this.emit('txProposalsUpdated');
@ -655,7 +675,7 @@ Wallet.prototype.sign = function(ntxid, cb) {
var self = this; var self = this;
setTimeout(function() { setTimeout(function() {
var myId = self.getMyCopayerId(); var myId = self.getMyCopayerId();
var txp = self.txProposals.txps[ntxid]; var txp = self.txProposals.get(ntxid);
// if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) { // if (!txp || txp.rejectedBy[myId] || txp.signedBy[myId]) {
// if (cb) cb(false); // if (cb) cb(false);
// } // }
@ -678,14 +698,13 @@ Wallet.prototype.sign = function(ntxid, cb) {
}, 10); }, 10);
}; };
Wallet.prototype.sendTx = function(ntxid, cb) { Wallet.prototype.sendTx = function(ntxid, cb) {
var txp = this.txProposals.txps[ntxid]; var txp = this.txProposals.get(ntxid);
if (!txp) return;
var tx = txp.builder.build(); var tx = txp.builder.build();
if (!tx.isComplete()) return; if (!tx.isComplete())
throw new Error('Tx is not complete. Can not broadcast');
this.log('Broadcasting Transaction'); this.log('Broadcasting Transaction');
var scriptSig = tx.ins[0].getScript(); var scriptSig = tx.ins[0].getScript();
var size = scriptSig.serialize().length; var size = scriptSig.serialize().length;
@ -696,28 +715,23 @@ Wallet.prototype.sendTx = function(ntxid, cb) {
this.blockchain.sendRawTransaction(txHex, function(txid) { this.blockchain.sendRawTransaction(txHex, function(txid) {
self.log('BITCOIND txid:', txid); self.log('BITCOIND txid:', txid);
if (txid) { if (txid) {
self.txProposals.txps[ntxid].setSent(txid); self.txProposals.get(ntxid).setSent(txid);
self.sendTxProposal(ntxid); self.sendTxProposal(ntxid);
self.store(); self.store();
return cb(txid);
} else {
self.log('Sent failed. Checking is the TX was sent already');
self._checkSentTx(ntxid, function(txid) {
console.log('[Wallet.js.730:txid:]', txid); //TODO
if (txid)
self.store();
return cb(txid);
});
} }
return cb(txid);
}); });
}; };
// Wallet.prototype.addSeenToTxProposals = function() {
// var ret = false;
// var myId = this.getMyCopayerId();
//
// for (var k in this.txProposals.txps) {
// var txp = this.txProposals.txps[k];
// if (!txp.seenBy[myId]) {
//
// txp.seenBy[myId] = Date.now();
// ret = true;
// }
// }
// return ret;
// };
// TODO: remove this method and use getAddressesInfo everywhere // TODO: remove this method and use getAddressesInfo everywhere
Wallet.prototype.getAddresses = function(opts) { Wallet.prototype.getAddresses = function(opts) {
@ -840,7 +854,7 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
preconditions.checkArgument(new Address(toAddress).network().name === this.getNetworkName(), 'networkname mismatch'); preconditions.checkArgument(new Address(toAddress).network().name === this.getNetworkName(), 'networkname mismatch');
preconditions.checkState(pkr.isComplete(), 'pubkey ring incomplete'); preconditions.checkState(pkr.isComplete(), 'pubkey ring incomplete');
preconditions.checkState(priv,'no private key'); preconditions.checkState(priv, 'no private key');
if (comment) preconditions.checkArgument(comment.length <= 100); if (comment) preconditions.checkArgument(comment.length <= 100);
if (!opts.remainderOut) { if (!opts.remainderOut) {

View file

@ -678,10 +678,10 @@ describe('Wallet model', function() {
w.blockchain.fixUnspent(utxo); w.blockchain.fixUnspent(utxo);
w.createTx(toAddress, amountSatStr, null, function(ntxid) { w.createTx(toAddress, amountSatStr, null, function(ntxid) {
var s = sinon.stub(w, 'getMyCopayerId').returns('213'); var s = sinon.stub(w, 'getMyCopayerId').returns('213');
Object.keys(w.txProposals._getTxp(ntxid).rejectedBy).length.should.equal(0); Object.keys(w.txProposals.get(ntxid).rejectedBy).length.should.equal(0);
w.reject(ntxid); w.reject(ntxid);
Object.keys(w.txProposals._getTxp(ntxid).rejectedBy).length.should.equal(1); Object.keys(w.txProposals.get(ntxid).rejectedBy).length.should.equal(1);
w.txProposals._getTxp(ntxid).rejectedBy['213'].should.gt(1); w.txProposals.get(ntxid).rejectedBy['213'].should.gt(1);
s.restore(); s.restore();
done(); done();
}); });
@ -1169,7 +1169,7 @@ describe('Wallet model', function() {
w._handleReject(1, { w._handleReject(1, {
ntxid: 1 ntxid: 1
}, 1); }, 1);
}).should.throw('unknown TX'); }).should.throw('Unknown TXP');
}); });
it('should fail to reject a signed tx', function() { it('should fail to reject a signed tx', function() {
var w = cachedCreateW(); var w = cachedCreateW();
@ -1223,7 +1223,7 @@ describe('Wallet model', function() {
w._handleReject(1, { w._handleReject(1, {
ntxid: 1 ntxid: 1
}, 1); }, 1);
}).should.throw('unknown TX'); }).should.throw('Unknown TXP');
}); });
it('should set seen a tx', function() { it('should set seen a tx', function() {
var w = cachedCreateW(); var w = cachedCreateW();

View file

@ -82,9 +82,9 @@ describe('Insight model', function() {
sinon sinon
.stub(http, 'request') .stub(http, 'request')
.returns(req) .returns(req)
.yields(request); .yields(request);
i.getUnspent(['2MuD5LnZSViZZYwZbpVsagwrH8WWvCztdmV', '2NBSLoMvsHsf2Uv3LA17zV4beH6Gze6RovA'], function(e, ret) { i.getUnspent(['2MuD5LnZSViZZYwZbpVsagwrH8WWvCztdmV', '2NBSLoMvsHsf2Uv3LA17zV4beH6Gze6RovA'], function(e, ret) {
should.not.exist(e); should.not.exist(e);
@ -113,9 +113,9 @@ describe('Insight model', function() {
req.end = function() {}; req.end = function() {};
sinon sinon
.stub(http, 'request') .stub(http, 'request')
.returns(req) .returns(req)
.yields(request); .yields(request);
i.sendRawTransaction(rawtx, function(a) { i.sendRawTransaction(rawtx, function(a) {
should.exist(a); should.exist(a);
@ -200,5 +200,36 @@ describe('Insight model', function() {
}); });
}); });
describe("#checkSentTx", function() {
it('should return true if Tx is found', function(done) {
var w = new Insight();
w._request = sinon.stub().yields(null, {
txid: "414142",
});
var tx = function() {};
tx.prototype.getHash = function(){return new Buffer('BAA')};
w.checkSentTx(new tx(), function(err, ret) {
should.not.exist(err);
ret.should.equal('414142');
done();
});
});
it('should return false if Tx is not found', function(done) {
var w = new Insight();
w._request = sinon.stub().yields(null, {
txid: "414142",
});
var tx = function() {};
tx.prototype.getHash = function(){return new Buffer('ABC')};
w.checkSentTx(new tx(), function(err, ret) {
should.not.exist(err);
ret.should.equal(false);
done();
});
});
});
}); });