diff --git a/lib/bcoin/tx-pool.js b/lib/bcoin/tx-pool.js index e794ebe8..df894b4b 100644 --- a/lib/bcoin/tx-pool.js +++ b/lib/bcoin/tx-pool.js @@ -70,18 +70,26 @@ TXPool.prototype.add = function add(tx, noWrite) { if (unspent) { // Add TX to inputs and spend money - tx.input(unspent.tx, unspent.index); + var index = tx._input(unspent.tx, unspent.index); + + // Skip invalid transactions + if (!tx.verify(index)) + return; + delete this._unspent[key]; updated = true; continue; } - // Double-spend?! - if (!own || this._orphans[key]) + if (!own) continue; // Add orphan, if no parent transaction is yet known - this._orphans[key] = { tx: tx, index: input.out.index }; + var orphan = { tx: tx, index: input.out.index }; + if (this._orphans[key]) + this._orphans[key].push(orphan); + else + this._orphans[key] = [orphan]; } if (!own) { @@ -99,27 +107,28 @@ TXPool.prototype.add = function add(tx, noWrite) { var out = tx.outputs[i]; var key = hash + '/' + i; - var orphan = this._orphans[key]; + var orphans = this._orphans[key]; + // Add input to orphan - if (orphan) { - orphan.tx.input(tx, orphan.index); - var index = orphan.tx.inputIndex(tx, orphan.index); + if (orphans) { + var some = orphans.some(function(orphan) { + var index = orphan.tx._input(tx, orphan.index); - // Verify that input script is correct, if not - add output to unspent - // and remove orphan from storage - if (!orphan.tx.verify(orphan, index)) { - orphan = null; - if (this._storage) + // Verify that input script is correct, if not - add output to unspent + // and remove orphan from storage + if (!orphan.tx.verify(index)) { this._removeTX(orphan.tx); - } + return false; + } + return true; + }, this); + if (!some) + orphans = null; } - if (!orphan) { - this._unspent[key] = { tx: tx, index: i }; - updated = true; - continue; - } delete this._orphans[key]; + if (!orphans) + this._unspent[key] = { tx: tx, index: i }; } this._lastTs = Math.max(tx.ts, this._lastTs); @@ -143,6 +152,11 @@ TXPool.prototype._storeTX = function _storeTX(hash, tx) { }; TXPool.prototype._removeTX = function _removeTX(tx) { + for (var i = 0; i < tx.outputs.length; i++) + delete this._unspent[tx.hash('hex') + '/' + i]; + + if (!this._storage) + return; var self = this; this._storage.del(this._prefix + tx.hash('hex'), function(err) { if (err) diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index febba665..f60f341e 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -51,7 +51,7 @@ TX.prototype.render = function render() { return bcoin.protocol.framer.tx(this); }; -TX.prototype.input = function input(i, index) { +TX.prototype._input = function _input(i, index) { if (i instanceof TX) i = { tx: i, index: index }; else if (typeof i === 'string' || Array.isArray(i)) @@ -79,7 +79,7 @@ TX.prototype.input = function input(i, index) { }; // Try modifying existing input first - var index = this.inputIndex(hash, index); + var index = this._inputIndex(hash, index); if (index !== -1) { var ex = this.inputs[index]; @@ -90,12 +90,13 @@ TX.prototype.input = function input(i, index) { this.inputs.push(input); if (input.out.tx) this.funds.iadd(input.out.tx.outputs[input.out.index].value); + index = this.inputs.length - 1; } - return this; + return index; }; -TX.prototype.inputIndex = function inputIndex(hash, index) { +TX.prototype._inputIndex = function _inputIndex(hash, index) { if (hash instanceof TX) hash = hash.hash('hex'); for (var i = 0; i < this.inputs.length; i++) { @@ -107,6 +108,11 @@ TX.prototype.inputIndex = function inputIndex(hash, index) { return -1; }; +TX.prototype.input = function input(i, index) { + this._input(i, index); + return this; +}; + TX.prototype.out = function out(output, value) { if (output instanceof bcoin.wallet) output = output.getAddress(); @@ -175,9 +181,9 @@ TX.prototype.verify = function verify(index, force) { if (!force && this.ts !== 0) return true; - return this.inputs.filter(function(input, i) { + return this.inputs.every(function(input, i) { if (index !== undefined && index !== i) - return false; + return true; if (!input.out.tx) return false; diff --git a/test/wallet-test.js b/test/wallet-test.js index 0a98c467..f84b32bc 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -79,24 +79,25 @@ describe('Wallet', function() { // Coinbase var t1 = bcoin.tx().out(w, 50000).out(w, 1000); - var t2 = bcoin.tx().input(t1.hash(), 0) + w.sign(t1); + var t2 = bcoin.tx().input(t1, 0) .out(w, 24000) .out(w, 24000); - var t3 = bcoin.tx().input(t1.hash(), 1) - .input(t2.hash(), 0) + w.sign(t2); + var t3 = bcoin.tx().input(t1, 1) + .input(t2, 0) .out(w, 23000); - var t4 = bcoin.tx().input(t2.hash(), 1) - .input(t3.hash(), 0) + w.sign(t3); + var t4 = bcoin.tx().input(t2, 1) + .input(t3, 0) .out(w, 11000) .out(w, 11000); - var f1 = bcoin.tx().input(t4.hash(), 1) - .out(f, 10000); - var fake = bcoin.tx().input(t1.hash(), 1); - w.sign(t1); - w.sign(t2); - w.sign(t3); w.sign(t4); + var f1 = bcoin.tx().input(t4, 1) + .out(f, 10000); w.sign(f1); + var fake = bcoin.tx().input(t1, 1) + .out(w, 500); // Just for debugging t1.hint = 't1'; @@ -104,10 +105,13 @@ describe('Wallet', function() { t3.hint = 't3'; t4.hint = 't4'; f1.hint = 'f1'; + fake.hint = 'fake'; + // Fake TX should temporarly change output w.addTX(fake); + w.addTX(t4); - assert.equal(w.balance().toString(10), '22000'); + assert.equal(w.balance().toString(10), '22500'); w.addTX(t1); assert.equal(w.balance().toString(10), '73000'); w.addTX(t2);