diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index c4bb8083..06590aed 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -848,6 +848,34 @@ ChainDB.prototype.hasCoins = function hasCoins(hash) { return this.db.has(layout.c(hash)); }; +/** + * Get coin viewpoint. + * @param {TX} tx + * @returns {Promise} - Returns {@link CoinView}. + */ + +ChainDB.prototype.getCoinView = co(function* getCoinView(tx, callback) { + var view = new CoinView(); + var prevout = tx.getPrevout(); + var i, prev, coins; + + for (i = 0; i < prevout.length; i++) { + prev = prevout[i]; + coins = yield this.getCoins(prev); + + if (!coins) { + coins = new Coins(); + coins.hash = prev.hash; + view.add(coins); + continue; + } + + view.add(coins); + } + + return view; +}); + /** * Get coins necessary to be resurrected during a reorg. * @param {Hash} hash diff --git a/lib/blockchain/coins.js b/lib/blockchain/coins.js index 317080ed..e4f9b98d 100644 --- a/lib/blockchain/coins.js +++ b/lib/blockchain/coins.js @@ -90,20 +90,39 @@ Coins.fromOptions = function fromOptions(options) { }; /** - * Add a single output to the collection. + * Add a single entry to the collection. * @param {Number} index - * @param {Output} output + * @param {CoinEntry} entry */ -Coins.prototype.add = function add(index, output) { - assert(!output.script.isUnspendable()); - +Coins.prototype.add = function add(index, entry) { while (this.outputs.length <= index) this.outputs.push(null); assert(!this.outputs[index]); - this.outputs[index] = CoinEntry.fromOutput(output); + this.outputs[index] = entry; +}; + +/** + * Add a single output to the collection. + * @param {Number} index + * @param {Output} output + */ + +Coins.prototype.addOutput = function addOutput(index, output) { + assert(!output.script.isUnspendable()); + this.add(index, CoinEntry.fromOutput(output)); +}; + +/** + * Add a single coin to the collection. + * @param {Coin} coin + */ + +Coins.prototype.addCoin = function addCoin(coin) { + assert(!coin.script.isUnspendable()); + this.add(coin.index, CoinEntry.fromCoin(coin)); }; /** @@ -132,6 +151,21 @@ Coins.prototype.get = function get(index) { return this.outputs[index]; }; +/** + * Get an output. + * @param {Number} index + * @returns {Output} + */ + +Coins.prototype.getOutput = function getOutput(index) { + var entry = this.get(index); + + if (!entry) + return; + + return entry.toOutput(); +}; + /** * Get a coin. * @param {Number} index @@ -426,6 +460,7 @@ Coins.fromRaw = function fromRaw(data, hash) { * Inject properties from tx. * @private * @param {TX} tx + * @param {Number} height */ Coins.prototype.fromTX = function fromTX(tx, height) { @@ -457,6 +492,7 @@ Coins.prototype.fromTX = function fromTX(tx, height) { /** * Instantiate a coins object from a transaction. * @param {TX} tx + * @param {Number} height * @returns {Coins} */ @@ -586,7 +622,7 @@ CoinEntry.fromReader = function fromReader(br) { }; /** - * Instantiate compressed coin from coin. + * Instantiate compressed coin from output. * @param {Output} output * @returns {CoinEntry} */ @@ -597,6 +633,21 @@ CoinEntry.fromOutput = function fromOutput(output) { return entry; }; +/** + * Instantiate compressed coin from coin. + * @param {Coin} coin + * @returns {CoinEntry} + */ + +CoinEntry.fromCoin = function fromCoin(coin) { + var entry = new CoinEntry(); + var output = new Output(); + output.value = coin.value; + output.script = coin.script; + entry.output = output; + return entry; +}; + /* * Expose */ diff --git a/lib/blockchain/coinview.js b/lib/blockchain/coinview.js index 01efda57..215e28fd 100644 --- a/lib/blockchain/coinview.js +++ b/lib/blockchain/coinview.js @@ -26,12 +26,22 @@ function CoinView() { this.undo = new UndoCoins(); } +/** + * Get coins. + * @param {Hash} hash + * @returns {Coins} coins + */ + +CoinView.prototype.get = function get(hash) { + return this.unspent[hash]; +}; + /** * Add coins to the collection. * @param {Coins} coins */ -CoinView.prototype.addCoins = function addCoins(coins) { +CoinView.prototype.add = function add(coins) { this.unspent[coins.hash] = coins; return coins; }; @@ -39,76 +49,24 @@ CoinView.prototype.addCoins = function addCoins(coins) { /** * Add a tx to the collection. * @param {TX} tx + * @param {Number} height */ CoinView.prototype.addTX = function addTX(tx, height) { var coins = Coins.fromTX(tx, height); - return this.addCoins(coins); + return this.add(coins); }; /** * Remove a tx from the collection. * @param {TX} tx + * @param {Number} height */ CoinView.prototype.removeTX = function removeTX(tx, height) { var coins = Coins.fromTX(tx, height); coins.outputs.length = 0; - return this.addCoins(coins); -}; - -/** - * Get a coin. - * @param {Hash} hash - * @param {Number} index - * @returns {Coin} - */ - -CoinView.prototype.get = function get(hash, index) { - var coins = this.unspent[hash]; - var entry; - - if (!coins) - return; - - entry = coins.get(index); - - if (!entry) - return; - - return entry.toCoin(coins, index); -}; - -/** - * Test whether the collection has a coin. - * @param {Hash} hash - * @param {Number} index - * @returns {Boolean} - */ - -CoinView.prototype.has = function has(hash, index) { - var coins = this.unspent[hash]; - - if (!coins) - return false; - - return coins.has(index); -}; - -/** - * Remove a coin and return it. - * @param {Hash} hash - * @param {Number} index - * @returns {Coin} - */ - -CoinView.prototype.spend = function spend(hash, index) { - var coins = this.unspent[hash]; - - if (!coins) - return null; - - return this.spendFrom(coins, index); + return this.add(coins); }; /** @@ -158,31 +116,6 @@ CoinView.prototype.getCoins = co(function* getCoins(db, hash) { return coins; }); -/** - * Test whether all inputs are available. - * @param {ChainDB} db - * @param {TX} tx - * @returns {Boolean} True if all inputs are available. - */ - -CoinView.prototype.hasInputs = co(function* hasInputs(db, tx) { - var i, input, prevout, coins; - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - prevout = input.prevout; - coins = yield this.getCoins(db, prevout.hash); - - if (!coins) - return false; - - if (!coins.has(prevout.index)) - return false; - } - - return true; -}); - /** * Read all input coins into unspent map. * @param {ChainDB} db @@ -198,28 +131,6 @@ CoinView.prototype.ensureInputs = co(function* ensureInputs(db, tx) { } }); -/** - * Spend coins for transaction. - * @param {TX} tx - * @returns {Boolean} - */ - -CoinView.prototype.spendCoins = function spendCoins(tx) { - var i, input, prevout; - - for (i = 0; i < tx.inputs.length; i++) { - input = tx.inputs[i]; - prevout = input.prevout; - - input.coin = this.spend(prevout.hash, prevout.index); - - if (!input.coin) - return false; - } - - return true; -}; - /** * Spend coins for transaction. * @param {TX} tx diff --git a/lib/blockchain/undocoins.js b/lib/blockchain/undocoins.js index 183eee0b..6114878b 100644 --- a/lib/blockchain/undocoins.js +++ b/lib/blockchain/undocoins.js @@ -148,7 +148,7 @@ UndoCoins.prototype.apply = function apply(view, outpoint) { assert(coins); } - coins.add(index, undo.toOutput()); + coins.addOutput(index, undo.toOutput()); assert(coins.has(index)); diff --git a/lib/mempool/mempool.js b/lib/mempool/mempool.js index 33c51da3..d413ea41 100644 --- a/lib/mempool/mempool.js +++ b/lib/mempool/mempool.js @@ -1574,6 +1574,32 @@ Mempool.prototype.fillAllCoins = co(function* fillAllCoins(tx) { return found; }); +/** + * Get coin viewpoint. + * @param {TX} tx + * @returns {Promise} - Returns {@link CoinView}. + */ + +Mempool.prototype.getCoinView = co(function* getCoinView(tx) { + var view = yield this.chain.db.getCoinView(tx); + var entries = view.toArray(); + var i, coins, entry; + + for (i = 0; i < entries.length; i++) { + coins = entries[i]; + + if (!coins.isEmpty()) + continue; + + entry = this.getEntry(coins.hash); + + if (!entry) + continue; + + view.addTX(entry.tx, entry.height); + } +}); + /** * Get a snapshot of all transaction hashes in the mempool. Used * for generating INV packets in response to MEMPOOL packets. diff --git a/migrate/chaindb1to2.js b/migrate/chaindb1to2.js index 152b7a8c..82a38a8b 100644 --- a/migrate/chaindb1to2.js +++ b/migrate/chaindb1to2.js @@ -152,7 +152,7 @@ var reserializeCoins = co(function* reserializeCoins() { output.value = coin.value; if (!output.script.isUnspendable()) - coins.add(coin.index, output); + coins.addOutput(coin.index, output); } coins.cleanup();