From de18e92117e370ac62a70b701b676d58fb27b123 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Thu, 20 Oct 2016 07:32:52 -0700 Subject: [PATCH] coin: safely handle coin.fromTX. --- lib/chain/chaindb.js | 30 ++-- lib/mempool/mempool.js | 9 +- lib/primitives/tx.js | 12 +- lib/wallet/txdb.js | 306 ++++++++++++++++++++--------------------- 4 files changed, 185 insertions(+), 172 deletions(-) diff --git a/lib/chain/chaindb.js b/lib/chain/chaindb.js index 59367f8d..b29a2ce5 100644 --- a/lib/chain/chaindb.js +++ b/lib/chain/chaindb.js @@ -836,7 +836,7 @@ ChainDB.prototype.fillCoins = co(function* fillCoins(tx) { var i, input, prevout, coin; if (tx.isCoinbase()) - return tx; + return; for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; @@ -847,11 +847,11 @@ ChainDB.prototype.fillCoins = co(function* fillCoins(tx) { coin = yield this.getCoin(prevout.hash, prevout.index); - if (coin) - input.coin = coin; - } + if (!coin) + continue; - return tx; + input.coin = coin; + } }); /** @@ -861,27 +861,31 @@ ChainDB.prototype.fillCoins = co(function* fillCoins(tx) { */ ChainDB.prototype.fillHistory = co(function* fillHistory(tx) { - var i, input, ptx; + var i, input, prevout, prev; if (!this.options.indexTX) - return tx; + return; if (tx.isCoinbase()) - return tx; + return; for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; + prevout = input.prevout; if (input.coin) continue; - ptx = yield this.getTX(input.prevout.hash); + prev = yield this.getTX(prevout.hash); - if (ptx) - input.coin = Coin.fromTX(ptx, input.prevout.index); + if (!prev) + continue; + + if (prevout.index >= prev.outputs.length) + continue; + + input.coin = Coin.fromTX(prev, prevout.index); } - - return tx; }); /** diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index f40c7837..d7e30e26 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -458,16 +458,19 @@ Mempool.prototype.fillHistory = function fillHistory(tx) { for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; + prevout = input.prevout; if (input.coin) continue; - prevout = input.prevout; prev = this.getTX(prevout.hash); if (!prev) continue; + if (prevout.index >= prev.outputs.length) + continue; + input.coin = Coin.fromTX(prev, prevout.index); } }; @@ -486,11 +489,11 @@ Mempool.prototype.fillCoins = function fillCoins(tx) { for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; + prevout = input.prevout; if (input.coin) continue; - prevout = input.prevout; coin = this.getCoin(prevout.hash, prevout.index); if (!coin) @@ -1597,6 +1600,7 @@ Mempool.prototype.trackEntry = function trackEntry(entry) { continue; coin = Coin.fromTX(tx, i); + this.coinIndex.addCoin(coin); } } @@ -1641,6 +1645,7 @@ Mempool.prototype.untrackEntry = function untrackEntry(entry) { continue; coin = Coin.fromTX(tx, i); + this.coinIndex.removeCoin(coin); } } diff --git a/lib/primitives/tx.js b/lib/primitives/tx.js index 6e904152..114239b0 100644 --- a/lib/primitives/tx.js +++ b/lib/primitives/tx.js @@ -1076,7 +1076,7 @@ TX.prototype.hasCoins = function hasCoins() { TX.prototype.fillCoins = function fillCoins(coins) { var result = true; - var i, input, hash, index, map, coin; + var i, input, hash, index, map, tx, coin; if ((coins instanceof Coin) || (coins instanceof TX)) { @@ -1105,10 +1105,14 @@ TX.prototype.fillCoins = function fillCoins(coins) { if (input.coin) continue; - coin = coins[hash]; + tx = coins[hash]; - if (coin) { - input.coin = Coin.fromTX(coin, index); + if (tx) { + if (index < tx.outputs.length) { + input.coin = Coin.fromTX(tx, index); + continue; + } + result = false; continue; } diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index 079b8cf4..8e118b70 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -2049,25 +2049,6 @@ TXDB.prototype.getPending = co(function* getPending(account) { return txs; }); -/** - * Get coins. - * @param {Number?} account - * @returns {Promise} - Returns {@link Coin}[]. - */ - -TXDB.prototype.getCoins = co(function* getCoins(account, all) { - var credits = yield this.getCredits(account, all); - var coins = []; - var i, credit; - - for (i = 0; i < credits.length; i++) { - credit = credits[i]; - coins.push(credit.coin); - } - - return coins; -}); - /** * Get coins. * @param {Number?} account @@ -2113,25 +2094,6 @@ TXDB.prototype.getCredits = co(function* getCredits(account, all) { return unspent; }); -/** - * Get coins by account. - * @param {Number} account - * @returns {Promise} - Returns {@link Coin}[]. - */ - -TXDB.prototype.getAccountCoins = co(function* getAccountCoins(account, all) { - var credits = yield this.getAccountCredits(account, all); - var coins = []; - var i, credit; - - for (i = 0; i < credits.length; i++) { - credit = credits[i]; - coins.push(credit.coin); - } - - return coins; -}); - /** * Get coins by account. * @param {Number} account @@ -2161,36 +2123,6 @@ TXDB.prototype.getAccountCredits = co(function* getAccountCredits(account, all) return credits; }); -/** - * Fill a transaction with coins (all historical coins). - * @param {TX} tx - * @returns {Promise} - Returns {@link TX}. - */ - -TXDB.prototype.fillHistory = co(function* fillHistory(tx) { - var coins = []; - var i, input, credits, credit; - - if (tx.isCoinbase()) - return coins; - - credits = yield this.getSpentCredits(tx); - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - credit = credits[i]; - - if (!credit) - continue; - - input.coin = credit.coin; - - coins.push(credit.coin); - } - - return coins; -}); - /** * Fill a transaction with coins (all historical coins). * @param {TX} tx @@ -2226,6 +2158,74 @@ TXDB.prototype.getSpentCredits = co(function* getSpentCredits(tx) { return credits; }); +/** + * Get coins. + * @param {Number?} account + * @returns {Promise} - Returns {@link Coin}[]. + */ + +TXDB.prototype.getCoins = co(function* getCoins(account, all) { + var credits = yield this.getCredits(account, all); + var coins = []; + var i, credit; + + for (i = 0; i < credits.length; i++) { + credit = credits[i]; + coins.push(credit.coin); + } + + return coins; +}); + +/** + * Get coins by account. + * @param {Number} account + * @returns {Promise} - Returns {@link Coin}[]. + */ + +TXDB.prototype.getAccountCoins = co(function* getAccountCoins(account, all) { + var credits = yield this.getAccountCredits(account, all); + var coins = []; + var i, credit; + + for (i = 0; i < credits.length; i++) { + credit = credits[i]; + coins.push(credit.coin); + } + + return coins; +}); + +/** + * Fill a transaction with coins (all historical coins). + * @param {TX} tx + * @returns {Promise} - Returns {@link TX}. + */ + +TXDB.prototype.fillHistory = co(function* fillHistory(tx) { + var coins = []; + var i, input, credits, credit; + + if (tx.isCoinbase()) + return coins; + + credits = yield this.getSpentCredits(tx); + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + credit = credits[i]; + + if (!credit) + continue; + + input.coin = credit.coin; + + coins.push(credit.coin); + } + + return coins; +}); + /** * Fill a transaction with coins. * @param {TX} tx @@ -2236,7 +2236,7 @@ TXDB.prototype.fillCoins = co(function* fillCoins(tx) { var i, input, prevout, credit; if (tx.isCoinbase()) - return tx; + return; for (i = 0; i < tx.inputs.length; i++) { input = tx.inputs[i]; @@ -2255,8 +2255,6 @@ TXDB.prototype.fillCoins = co(function* fillCoins(tx) { input.coin = credit.coin; } - - return tx; }); /** @@ -2368,8 +2366,10 @@ TXDB.prototype.hasTX = function hasTX(hash) { TXDB.prototype.getCoin = co(function* getCoin(hash, index) { var credit = yield this.getCredit(hash, index); + if (!credit) return; + return credit.coin; }); @@ -2786,6 +2786,88 @@ TXDBState.prototype.inspect = function inspect() { return this.toJSON(); }; +/** + * Credit (wrapped coin) + * @constructor + * @param {Coin} coin + * @param {Boolean?} spent + * @property {Coin} coin + * @property {Boolean} spent + */ + +function Credit(coin, spent) { + if (!(this instanceof Credit)) + return new Credit(coin, spent); + + this.coin = coin || new Coin(); + this.spent = spent || false; +} + +/** + * Inject properties from serialized data. + * @private + * @param {Buffer} data + */ + +Credit.prototype.fromRaw = function fromRaw(data) { + var p = BufferReader(data); + this.coin.fromRaw(p); + this.spent = p.readU8() === 1; + return this; +}; + +/** + * Instantiate credit from serialized data. + * @param {Buffer} data + * @returns {Credit} + */ + +Credit.fromRaw = function fromRaw(data) { + return new Credit().fromRaw(data); +}; + +/** + * Serialize credit. + * @returns {Buffer} + */ + +Credit.prototype.toRaw = function toRaw(writer) { + var p = BufferWriter(writer); + + this.coin.toRaw(p); + p.writeU8(this.spent ? 1 : 0); + + if (!writer) + p = p.render(); + + return p; +}; + +/** + * Inject properties from tx object. + * @private + * @param {TX} tx + * @param {Number} i + * @returns {Credit} + */ + +Credit.prototype.fromTX = function fromTX(tx, i) { + this.coin.fromTX(tx, i); + this.spent = false; + return this; +}; + +/** + * Instantiate credit from transaction. + * @param {TX} tx + * @param {Number} i + * @returns {Credit} + */ + +Credit.fromTX = function fromTX(tx, i) { + return new Credit().fromTX(tx, i); +}; + /** * Transaction Details * @constructor @@ -2971,88 +3053,6 @@ DetailsMember.prototype.toJSON = function toJSON(network) { }; }; -/** - * Credit (wrapped coin) - * @constructor - * @param {Coin} coin - * @param {Boolean?} spent - * @property {Coin} coin - * @property {Boolean} spent - */ - -function Credit(coin, spent) { - if (!(this instanceof Credit)) - return new Credit(coin, spent); - - this.coin = coin || new Coin(); - this.spent = spent || false; -} - -/** - * Inject properties from serialized data. - * @private - * @param {Buffer} data - */ - -Credit.prototype.fromRaw = function fromRaw(data) { - var p = BufferReader(data); - this.coin.fromRaw(p); - this.spent = p.readU8() === 1; - return this; -}; - -/** - * Instantiate credit from serialized data. - * @param {Buffer} data - * @returns {Credit} - */ - -Credit.fromRaw = function fromRaw(data) { - return new Credit().fromRaw(data); -}; - -/** - * Serialize credit. - * @returns {Buffer} - */ - -Credit.prototype.toRaw = function toRaw(writer) { - var p = BufferWriter(writer); - - this.coin.toRaw(p); - p.writeU8(this.spent ? 1 : 0); - - if (!writer) - p = p.render(); - - return p; -}; - -/** - * Inject properties from tx object. - * @private - * @param {TX} tx - * @param {Number} i - * @returns {Credit} - */ - -Credit.prototype.fromTX = function fromTX(tx, i) { - this.coin.fromTX(tx, i); - this.spent = false; - return this; -}; - -/** - * Instantiate credit from transaction. - * @param {TX} tx - * @param {Number} i - * @returns {Credit} - */ - -Credit.fromTX = function fromTX(tx, i) { - return new Credit().fromTX(tx, i); -}; - /* * Helpers */