diff --git a/lib/bcoin/tx-pool.js b/lib/bcoin/tx-pool.js index e69de29b..94c11a28 100644 --- a/lib/bcoin/tx-pool.js +++ b/lib/bcoin/tx-pool.js @@ -0,0 +1,76 @@ +var bn = require('bn.js'); + +function TXPool() { + if (!(this instanceof TXPool)) + return new TXPool(); + + this._all = {}; + this._unspent = {}; + this._orphans = {}; +} +module.exports = TXPool; + +TXPool.prototype.add = function add(tx) { + var hash = tx.hash('hex'); + + // Do not add TX two times + if (this._all[hash]) + return; + this._all[hash] = tx; + + // Consume unspent money or add orphans + for (var i = 0; i < tx.inputs.length; i++) { + var input = tx.inputs[i]; + var key = input.out.hash + '/' + input.out.index; + var unspent = this._unspent[key]; + + if (unspent) { + // Add TX to inputs and spend money + tx.input(unspent.tx, unspent.index); + delete this._unspent[key]; + continue; + } + + // Double-spend?! + if (this._orphans[key]) + continue; + + // Add orphan, if no parent transaction is yet known + this._orphans[key] = { tx: tx, index: i }; + } + + // Add unspent outputs or fullfill orphans + for (var i = 0; i < tx.outputs.length; i++) { + var out = tx.outputs[i]; + + var key = hash + '/' + i; + var orphan = this._orphans[key]; + if (!orphan) { + this._unspent[key] = { tx: tx, index: i }; + continue; + } + delete this._orphans[key]; + + // Add input to orphan + orphan.tx.input(tx, orphan.index); + } +}; + +TXPool.prototype.unspent = function unspent(wallet) { + return Object.keys(this._unspent).map(function(key) { + return this._unspent[key]; + }, this).filter(function(item) { + return wallet.own(item.tx, item.index); + }); +}; + +TXPool.prototype.balance = function balance(wallet) { + var acc = new bn(0); + var unspent = this.unspent(wallet); + if (unspent.length === 0) + return acc; + + return unspent.reduce(function(acc, item) { + return acc.iadd(item.tx.outputs[item.index].value); + }, acc); +}; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index aa7aeabc..54677afe 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -49,6 +49,8 @@ TX.prototype.render = function render() { TX.prototype.input = function input(i, index) { if (i instanceof TX) i = { tx: i, index: index }; + else if (typeof i === 'string' || Array.isArray(i)) + i = { hash: i, index: index }; var hash; if (i.tx) @@ -89,6 +91,8 @@ TX.prototype.input = function input(i, index) { }; TX.prototype.out = function out(output, value) { + if (output instanceof bcoin.wallet) + output = output.getAddress(); if (typeof output === 'string') { output = { address: output, diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 35a3becd..47a2d131 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -18,6 +18,7 @@ function Wallet(options, passphrase) { options = {}; this.compressed = true; + this.tx = new bcoin.txPool(); this.key = null; if (options.passphrase) { @@ -94,10 +95,13 @@ Wallet.prototype.validateAddress = function validateAddress(addr) { }; Wallet.validateAddress = Wallet.prototype.validateAddress; -Wallet.prototype.own = function own(tx) { +Wallet.prototype.own = function own(tx, index) { var hash = this.getHash(); var key = this.getPublicKey(); - var outputs = tx.outputs.filter(function(output) { + var outputs = tx.outputs.filter(function(output, i) { + if (index && index !== i) + return false; + var s = output.script; if (bcoin.script.isPubkeyhash(s, hash)) @@ -143,3 +147,15 @@ Wallet.prototype.sign = function sign(tx, type) { return inputs.length; }; + +Wallet.prototype.addTX = function addTX(tx) { + this.tx.add(tx); +}; + +Wallet.prototype.unspent = function unspent() { + return this.tx.unspent(this); +}; + +Wallet.prototype.balance = function balance() { + return this.tx.balance(this); +}; diff --git a/test/wallet-test.js b/test/wallet-test.js index c14a8138..79744b66 100644 --- a/test/wallet-test.js +++ b/test/wallet-test.js @@ -70,4 +70,28 @@ describe('Wallet', function() { w.sign(tx); assert(tx.verify()); }); + + it('should have TX pool', function() { + var w = bcoin.wallet(); + + // Coinbase + var t1 = bcoin.tx().out(w, 50000).out(w, 1000); + var t2 = bcoin.tx().input(t1.hash(), 0) + .out(w, 24000) + .out(w, 24000); + var t3 = bcoin.tx().input(t1.hash(), 1) + .input(t2.hash(), 0) + .out(w, 23000); + var t4 = bcoin.tx().input(t2.hash(), 1) + .input(t3.hash(), 0) + .out(w, 22000); + w.addTX(t4); + assert.equal(w.balance().toString(10), '22000'); + w.addTX(t1); + assert.equal(w.balance().toString(10), '73000'); + w.addTX(t2); + assert.equal(w.balance().toString(10), '47000'); + w.addTX(t3); + assert.equal(w.balance().toString(10), '22000'); + }); });