diff --git a/lib/bcoin/tx-pool.js b/lib/bcoin/tx-pool.js index 75f85ce1..a0549436 100644 --- a/lib/bcoin/tx-pool.js +++ b/lib/bcoin/tx-pool.js @@ -37,16 +37,6 @@ function TXPool(wallet) { this._received = new bn(0); this._balance = new bn(0); - this._wallet.on('remove address', function(address) { - address = self._addresses[address.getAddress()]; - if (address) { - self._balance.isub(address.balance); - self._sent.isub(address.sent); - self._received.isub(address.received); - delete self._addresses[address]; - } - }); - // Load TXs from storage this._init(); } @@ -130,18 +120,16 @@ TXPool.prototype.add = function add(tx, noWrite, strict) { if (unspent) { // Add TX to inputs and spend money - index = tx._inputIndex(unspent.hash, unspent.index); - assert(index !== -1); - assert(tx.inputs[index] === input); - assert(tx.inputs[index].prevout.hash === unspent.hash); - assert(tx.inputs[index].prevout.index === unspent.index); input.output = unspent; + assert(input.prevout.hash === unspent.hash); + assert(input.prevout.index === unspent.index); + // Skip invalid transactions - if (!tx.verify(index)) + if (!tx.verify(i)) return; - this._addInput(tx, index); + this._addInput(tx, i); delete this._unspent[key]; updated = true; @@ -153,7 +141,7 @@ TXPool.prototype.add = function add(tx, noWrite, strict) { continue; // Add orphan, if no parent transaction is yet known - orphan = { tx: tx, index: input.prevout.index }; + orphan = { tx: tx, index: i }; if (this._orphans[key]) this._orphans[key].push(orphan); else @@ -161,20 +149,19 @@ TXPool.prototype.add = function add(tx, noWrite, strict) { } function checkOrphan(orphan) { - var index = orphan.tx._inputIndex(tx.hash('hex'), orphan.index); - assert(index !== -1); - assert(orphan.tx.inputs[index].prevout.hash === tx.hash('hex')); - assert(orphan.tx.inputs[index].prevout.index === i); - orphan.tx.inputs[index].output = coin; + orphan.tx.inputs[orphan.index].output = coin; + + assert(orphan.tx.inputs[orphan.index].prevout.hash === hash); + assert(orphan.tx.inputs[orphan.index].prevout.index === i); // Verify that input script is correct, if not - add output to unspent // and remove orphan from storage - if (!orphan.tx.verify(index)) { + if (!orphan.tx.verify(orphan.index)) { this._removeTX(orphan.tx, noWrite); return false; } - this._addInput(orphan.tx, index); + this._addInput(orphan.tx, orphan.index); return true; } @@ -226,21 +213,69 @@ TXPool.prototype.getTX = function getTX(hash) { return this._all[hash]; }; -TXPool.prototype.getUnspent = function getUnspent(hash, index) { +TXPool.prototype.getCoin = function getCoin(hash, index) { return this._unspent[hash + '/' + index]; }; -TXPool.prototype.addUnspent = function addUnspent(coin) { +TXPool.prototype.addCoin = function addCoin(coin, noWrite) { var id = coin.hash + '/' + coin.index; - if (!this._unspent[id]) { - this._unspent[id] = coin; - this._addOutput(coin); - this._lastHeight = Math.max(coin.height, this._lastHeight); - this.emit('update', this._lastTs, this._lastHeight); - // Weird workaround to get addresses to update - if (coin.height !== -1) - this.emit('confirmed', coin); + + // Do not add TX two times + if (this._unspent[id]) { + // Transaction was confirmed, update it in storage + if (coin.height !== -1 && this._unspent[id].height === -1) { + this._unspent[key].height = coin.height; + this._lastHeight = Math.max(tx.height, this._lastHeight); + this.emit('update', this._lastTs, this._lastHeight, tx); + this.emit('confirmed', tx); + // this._storeCoin(coin, noWrite); + } + return; } + + // Problem here: could in some cases add + // an unspent that was already redeemed. + if (this._all[coin.hash]) + return; + + this._unspent[id] = coin; + this._addOutput(coin); + this._lastHeight = Math.max(coin.height, this._lastHeight); + this.emit('update', this._lastTs, this._lastHeight); + // Weird workaround to get addresses to update + if (coin.height !== -1) + this.emit('confirmed', coin); + // this._storeCoin(coin, noWrite); +}; + +TXPool.prototype._storeCoin = function _storeCoin(id, coin, noWrite) { + var self = this; + + if (!this._storage || noWrite) + return; + + this._storage.put(this._prefix + id, coin.toJSON(), function(err) { + if (err) + self.emit('error', err); + }); +}; + +TXPool.prototype._removeCoin = function _removeCoin(id, noWrite) { + var self = this; + var key; + + if (this._unspent[id]) { + this._removeOutput(this._unspent[id]); + delete this._unspent[id]; + } + + if (!this._storage || noWrite) + return; + + this._storage.del(this._prefix + id, function(err) { + if (err) + self.emit('error', err); + }); }; TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) { @@ -276,19 +311,6 @@ TXPool.prototype._removeTX = function _removeTX(tx, noWrite) { }); }; -TXPool.prototype.prune = function prune(pruneOrphans) { - var unspent = Object.keys(this._unspent).reduce(function(key) { - out[key.split('/')[0]] = true; - return out; - }, {}); - Object.keys(this._all).forEach(function(key) { - if (!unspent[key]) - delete this._all[key]; - }); - if (pruneOrphans) - this._orphans = {}; -}; - TXPool.prototype.getAll = function getAll(address) { if (!address) address = this._wallet; @@ -302,17 +324,15 @@ TXPool.prototype.getAll = function getAll(address) { }; TXPool.prototype._addOutput = function _addOutput(tx, i, remove) { - if ((tx instanceof bcoin.output) || (tx instanceof bcoin.coin)) { - var output = tx; - if (!this._wallet.ownOutput(output)) - return; - } else { - var output = tx.outputs[i]; - var address; + var output, address; - if (!this._wallet.ownOutput(tx, i)) - return; - } + if ((tx instanceof bcoin.output) || (tx instanceof bcoin.coin)) + output = tx; + else + output = tx.outputs[i]; + + if (!this._wallet.ownOutput(output)) + return; address = output.getAddress(); @@ -346,8 +366,12 @@ TXPool.prototype._removeOutput = function _removeOutput(tx, i) { }; TXPool.prototype._addInput = function _addInput(tx, i, remove) { - var input = tx.inputs[i]; - var prev, address; + var input, prev, address; + + if (tx instanceof bcoin.input) + input = tx; + else + input = tx.inputs[i]; assert(input.output); @@ -418,8 +442,8 @@ TXPool.prototype.getBalance = function getBalance(address) { if (unspent.length === 0) return acc; - return unspent.reduce(function(acc, item) { - return acc.iadd(item.tx.outputs[item.index].value); + return unspent.reduce(function(acc, coin) { + return acc.iadd(coin.value); }, acc); }; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 036afe93..78a8e09e 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -159,7 +159,7 @@ TX.prototype._addInput = function _addInput(options, index) { } else if (options.tx) { coin = bcoin.coin(options.tx, options.index); options = { - prevout: { hash: coin.hash, index: coin.index }, + prevout: { hash: options.tx.hash('hex'), index: options.index }, output: coin, script: options.script, sequence: options.sequence diff --git a/lib/bcoin/utils.js b/lib/bcoin/utils.js index 96eaf68b..4e83fa9d 100644 --- a/lib/bcoin/utils.js +++ b/lib/bcoin/utils.js @@ -1333,3 +1333,36 @@ utils.sizeIntv = function sizeIntv(num) { return 9; }; + +utils.cmp = function(a, b) { + var len = Math.min(a.length, b.length); + + for (i = 0; i < len; i++) { + if (a[i] < b[i]) + return -1; + if (a[i] > b[i]) + return 1; + } + + if (a.length < b.length) + return -1; + + if (a.length > b.length) + return 1; + + return 0; +}; + +// https://cryptocoding.net/index.php/Coding_rules +// memcmp in constant time (can only return true or false) +// $ man 3 memcmp (see NetBSD's consttime_memequal) +utils.ccmp = function(a, b) { + var res, i; + + assert(a.length === b.length); + + for (i = 0; i < a.length; i++) + res = a[i] ^ b[i]; + + return res === 0; +}; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 77021b37..bff268b0 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -646,8 +646,7 @@ Wallet.prototype.addAddress = function addAddress(address) { if (this._addressIndex(address) !== -1) return; - if (address._wallet) - address._wallet.removeAddress(address); + assert(!address._wallet); address._wallet = this;