Merge pull request #1270 from isocolsky/fix/remove_spent
Removed proposals with spent inputs
This commit is contained in:
commit
db3ca4ceef
6 changed files with 144 additions and 0 deletions
|
|
@ -44,6 +44,11 @@ TxProposals.prototype.getNtxids = function() {
|
||||||
return Object.keys(this.txps);
|
return Object.keys(this.txps);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
TxProposals.prototype.deleteOne = function(ntxid) {
|
||||||
|
preconditions.checkState(this.txps[ntxid], 'Unknown TXP: ' + ntxid);
|
||||||
|
delete this.txps[ntxid];
|
||||||
|
};
|
||||||
|
|
||||||
TxProposals.prototype.deleteAll = function() {
|
TxProposals.prototype.deleteAll = function() {
|
||||||
this.txps = {};
|
this.txps = {};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1534,6 +1534,52 @@ Wallet.prototype.getUnspent = function(cb) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Wallet.prototype.removeTxWithSpentInputs = function(cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
cb = cb || function () {};
|
||||||
|
|
||||||
|
var txps = [];
|
||||||
|
var maxRejectCount = this.maxRejectCount();
|
||||||
|
for (var ntxid in this.txProposals.txps) {
|
||||||
|
var txp = this.txProposals.txps[ntxid];
|
||||||
|
txp.ntxid = ntxid;
|
||||||
|
if (txp.isPending(maxRejectCount)) {
|
||||||
|
txps.push(txp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var inputs = [];
|
||||||
|
txps.forEach(function (txp) {
|
||||||
|
txp.builder.utxos.forEach(function (utxo) {
|
||||||
|
inputs.push({ ntxid: txp.ntxid, txid: utxo.txid, vout: utxo.vout });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (inputs.length === 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.blockchain.getUnspent(this.getAddressesStr(), function(err, unspentList) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
unspentList.forEach(function (unspent) {
|
||||||
|
inputs.forEach(function (input) {
|
||||||
|
input.unspent = input.unspent || (input.txid === unspent.txid && input.vout === unspent.vout);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
inputs.forEach(function (input) {
|
||||||
|
if (!input.unspent) {
|
||||||
|
self.txProposals.deleteOne(input.ntxid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.emit('txProposalsUpdated');
|
||||||
|
self.store();
|
||||||
|
|
||||||
|
cb(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
Wallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts, cb) {
|
Wallet.prototype.createTx = function(toAddress, amountSatStr, comment, opts, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,8 @@ angular.module('copayApp.services')
|
||||||
if (!w) return root.onErrorDigest();
|
if (!w) return root.onErrorDigest();
|
||||||
if (!w.isReady()) return;
|
if (!w.isReady()) return;
|
||||||
|
|
||||||
|
w.removeTxWithSpentInputs();
|
||||||
|
|
||||||
$rootScope.balanceByAddr = {};
|
$rootScope.balanceByAddr = {};
|
||||||
$rootScope.updatingBalance = true;
|
$rootScope.updatingBalance = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,9 @@ FakeWallet.prototype.getBalance = function(cb) {
|
||||||
return cb(null, this.balance, this.balanceByAddr, this.safeBalance);
|
return cb(null, this.balance, this.balanceByAddr, this.safeBalance);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
FakeWallet.prototype.removeTxWithSpentInputs = function (cb) {
|
||||||
|
};
|
||||||
|
|
||||||
FakeWallet.prototype.setEnc = function(enc) {
|
FakeWallet.prototype.setEnc = function(enc) {
|
||||||
this.enc = enc;
|
this.enc = enc;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,21 @@ describe('TxProposals', function() {
|
||||||
txps.getNtxids().should.deep.equal(['a','b']);
|
txps.getNtxids().should.deep.equal(['a','b']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('#deleteOne', function() {
|
||||||
|
it('should delete specified ntxid', function() {
|
||||||
|
var txps = new TxProposals();
|
||||||
|
txps.txps = {a:1, b:2};
|
||||||
|
txps.deleteOne('a');
|
||||||
|
txps.getNtxids().should.deep.equal(['b']);
|
||||||
|
});
|
||||||
|
it('should fail on non-existent ntxid', function() {
|
||||||
|
var txps = new TxProposals();
|
||||||
|
txps.txps = {a:1, b:2};
|
||||||
|
(function () {
|
||||||
|
txps.deleteOne('c');
|
||||||
|
}).should.throw('Unknown TXP: c');
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('#toObj', function() {
|
describe('#toObj', function() {
|
||||||
it('should an object', function() {
|
it('should an object', function() {
|
||||||
var txps = TxProposals.fromObj({
|
var txps = TxProposals.fromObj({
|
||||||
|
|
|
||||||
|
|
@ -809,6 +809,79 @@ describe('Wallet model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('removeTxWithSpentInputs', function () {
|
||||||
|
it('should remove pending TxProposal with spent inputs', function(done) {
|
||||||
|
var w = cachedCreateW2();
|
||||||
|
var utxo = createUTXO(w);
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(0);
|
||||||
|
w.blockchain.fixUnspent(utxo);
|
||||||
|
w.createTx(toAddress, amountSatStr, null, function(ntxid) {
|
||||||
|
w.sendTxProposal(ntxid);
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(1);
|
||||||
|
|
||||||
|
// Inputs are still available, txp still valid
|
||||||
|
w.removeTxWithSpentInputs();
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(1);
|
||||||
|
|
||||||
|
// Simulate input spent. txp should be removed from txps list
|
||||||
|
w.blockchain.fixUnspent([]);
|
||||||
|
w.removeTxWithSpentInputs();
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(0);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove pending TxProposal with at least 1 spent input', function(done) {
|
||||||
|
var w = cachedCreateW2();
|
||||||
|
var utxo = [createUTXO(w)[0], createUTXO(w)[0]];
|
||||||
|
utxo[0].amount = 80000;
|
||||||
|
utxo[1].amount = 80000;
|
||||||
|
utxo[1].vout = 1;
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(0);
|
||||||
|
w.blockchain.fixUnspent(utxo);
|
||||||
|
w.createTx(toAddress, '100000', null, function(ntxid) {
|
||||||
|
w.sendTxProposal(ntxid);
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(1);
|
||||||
|
|
||||||
|
// Inputs are still available, txp still valid
|
||||||
|
w.removeTxWithSpentInputs();
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(1);
|
||||||
|
|
||||||
|
// Simulate 1 input spent. txp should be removed from txps list
|
||||||
|
w.blockchain.fixUnspent([utxo[0]]);
|
||||||
|
w.removeTxWithSpentInputs();
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(0);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not remove complete TxProposal', function(done) {
|
||||||
|
var w = cachedCreateW2();
|
||||||
|
var utxo = createUTXO(w);
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(0);
|
||||||
|
w.blockchain.fixUnspent(utxo);
|
||||||
|
w.createTx(toAddress, amountSatStr, null, function(ntxid) {
|
||||||
|
w.sendTxProposal(ntxid);
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(1);
|
||||||
|
|
||||||
|
// Inputs are still available, txp still valid
|
||||||
|
w.removeTxWithSpentInputs();
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(1);
|
||||||
|
|
||||||
|
// Simulate input spent. txp should be removed from txps list
|
||||||
|
w.blockchain.fixUnspent([]);
|
||||||
|
var txp = w.txProposals.get(ntxid);
|
||||||
|
sinon.stub(txp, 'isPending', function () { return false; })
|
||||||
|
w.removeTxWithSpentInputs();
|
||||||
|
chai.expect(w.getTxProposals().length).to.equal(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('#send', function() {
|
describe('#send', function() {
|
||||||
it('should call this.network.send', function() {
|
it('should call this.network.send', function() {
|
||||||
var w = cachedCreateW2();
|
var w = cachedCreateW2();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue