diff --git a/lib/bcoin/tx-pool.js b/lib/bcoin/tx-pool.js index d4906f69..f5a208a5 100644 --- a/lib/bcoin/tx-pool.js +++ b/lib/bcoin/tx-pool.js @@ -53,13 +53,14 @@ TXPool.prototype.add = function add(tx, noWrite) { // Transaction was confirmed, update it in storage if (tx.ts !== 0 && this._all[hash].ts === 0) { this._all[hash].ts = tx.ts; - this._storeTX(hash, tx); + if (this._storage) + this._storeTX(hash, tx); } return false; } this._all[hash] = tx; - var own = this._wallet.own(tx); + var own = this._wallet.ownOutput(tx); var update = false; // Consume unspent money or add orphans @@ -87,6 +88,10 @@ TXPool.prototype.add = function add(tx, noWrite) { if (!own) { if (updated) this.emit('update', this._lastTs); + + // Save spending TXs without adding unspents + if (this._storage && this._wallet.ownInput(tx)) + this._storeTX(hash, tx); return; } @@ -130,8 +135,9 @@ TXPool.prototype._storeTX = function _storeTX(hash, tx) { TXPool.prototype.all = function all() { return Object.keys(this._all).map(function(key) { return this._all[key]; - }, this).filter(function(item) { - return this._wallet.own(item.tx, item.index); + }, this).filter(function(tx) { + return this._wallet.ownOutput(tx) || + this._wallet.ownInput(tx); }, this); }; @@ -139,7 +145,7 @@ TXPool.prototype.unspent = function unspent() { return Object.keys(this._unspent).map(function(key) { return this._unspent[key]; }, this).filter(function(item) { - return this._wallet.own(item.tx, item.index); + return this._wallet.ownOutput(item.tx, item.index); }, this); }; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 586e1fdf..9f2c918c 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -163,7 +163,9 @@ TX.prototype.subscriptHash = function subscriptHash(index, s, type) { TX.prototype.verify = function verify() { return this.inputs.every(function(input, i) { - assert(input.out.tx); + if (!input.out.tx) + return false; + assert(input.out.tx.outputs.length > input.out.index); var subscript = input.out.tx.getSubscript(input.out.index); diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index bb41f2a8..42904a2c 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -142,7 +142,7 @@ Wallet.prototype.validateAddress = function validateAddress(addr) { }; Wallet.validateAddress = Wallet.prototype.validateAddress; -Wallet.prototype.own = function own(tx, index) { +Wallet.prototype.ownOutput = function ownOutput(tx, index) { var hash = this.getHash(); var key = this.getPublicKey(); var outputs = tx.outputs.filter(function(output, i) { @@ -165,6 +165,32 @@ Wallet.prototype.own = function own(tx, index) { return outputs; }; +Wallet.prototype.ownInput = function ownInput(tx, index) { + var hash = this.getHash(); + var key = this.getPublicKey(); + + var inputs = tx.inputs.filter(function(input, i) { + if (index !== undefined && index !== i) + return false; + + if (!input.out.tx) + return false; + + var s = input.out.tx.outputs[input.out.index].script; + if (bcoin.script.isPubkeyhash(s, hash)) + return true; + + if (bcoin.script.isMultisig(s, key)) + return true; + + return false; + }, this); + if (inputs.length === 0) + return false; + + return inputs; +}; + Wallet.prototype.sign = function sign(tx, type) { if (!type) type = 'all'; @@ -172,7 +198,7 @@ Wallet.prototype.sign = function sign(tx, type) { // Filter inputs that this wallet own var inputs = tx.inputs.filter(function(input) { - return input.out.tx && this.own(input.out.tx); + return input.out.tx && this.ownOutput(input.out.tx); }, this); var pub = this.getPublicKey(); diff --git a/test/wallet-test.js b/test/wallet-test.js index 170bcf11..c1327d51 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -31,8 +31,8 @@ describe('Wallet', function() { address: w.getAddress() + 'x' }] }); - assert(w.own(src)); - assert.equal(w.own(src).reduce(function(acc, out) { + assert(w.ownOutput(src)); + assert.equal(w.ownOutput(src).reduce(function(acc, out) { return acc.iadd(out.value); }, new bn(0)).toString(10), 5460 * 2); @@ -58,8 +58,8 @@ describe('Wallet', function() { address: w.getAddress() + 'x' }] }); - assert(w.own(src)); - assert.equal(w.own(src).reduce(function(acc, out) { + assert(w.ownOutput(src)); + assert.equal(w.ownOutput(src).reduce(function(acc, out) { return acc.iadd(out.value); }, new bn(0)).toString(10), 5460 * 2); @@ -75,6 +75,7 @@ describe('Wallet', function() { it('should have TX pool and be serializable', function() { var w = bcoin.wallet(); + var f = bcoin.wallet(); // Coinbase var t1 = bcoin.tx().out(w, 50000).out(w, 1000); @@ -86,13 +87,22 @@ describe('Wallet', function() { .out(w, 23000); var t4 = bcoin.tx().input(t2.hash(), 1) .input(t3.hash(), 0) - .out(w, 22000); + .out(w, 11000) + .out(w, 11000); + var f1 = bcoin.tx().input(t4.hash(), 1) + .out(f, 10000); + w.sign(t1); + w.sign(t2); + w.sign(t3); + w.sign(t4); + w.sign(f1); // Just for debugging t1.hint = 't1'; t2.hint = 't2'; t3.hint = 't3'; t4.hint = 't4'; + f1.hint = 'f1'; w.addTX(t4); assert.equal(w.balance().toString(10), '22000'); @@ -102,8 +112,16 @@ describe('Wallet', function() { assert.equal(w.balance().toString(10), '47000'); w.addTX(t3); assert.equal(w.balance().toString(10), '22000'); + w.addTX(f1); + assert.equal(w.balance().toString(10), '11000'); + assert(w.all().some(function(tx) { + return tx.hash('hex') === f1.hash('hex'); + })); var w2 = bcoin.wallet.fromJSON(w.toJSON()); - assert.equal(w2.balance().toString(10), '22000'); + assert.equal(w2.balance().toString(10), '11000'); + assert(w2.all().some(function(tx) { + return tx.hash('hex') === f1.hash('hex'); + })); }); });