add rebroadcast test
This commit is contained in:
parent
91829f8410
commit
5a2dfe690d
5 changed files with 131 additions and 66 deletions
|
|
@ -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;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -138,10 +138,10 @@ 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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue