chain: refactor spending.
This commit is contained in:
parent
453eccbabd
commit
371f6b1fa0
@ -624,15 +624,14 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
||||
var sigops = 0;
|
||||
var jobs = [];
|
||||
var ret = new VerifyResult();
|
||||
var i, view, tx, valid;
|
||||
var view = new CoinView();
|
||||
var i, tx, valid;
|
||||
|
||||
if (this.options.spv)
|
||||
return new CoinView();
|
||||
return view;
|
||||
|
||||
if (this.isGenesis(block))
|
||||
return new CoinView();
|
||||
|
||||
view = yield this.db.getCoinView(block);
|
||||
return view;
|
||||
|
||||
// Check all transactions
|
||||
for (i = 0; i < block.txs.length; i++) {
|
||||
@ -640,13 +639,15 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
||||
|
||||
// Ensure tx is not double spending an output.
|
||||
if (i > 0) {
|
||||
if (!view.fillCoins(tx)) {
|
||||
if (!(yield view.hasInputs(this.db, tx))) {
|
||||
assert(!historical, 'BUG: Spent inputs in historical data!');
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
'bad-txns-inputs-missingorspent',
|
||||
100);
|
||||
}
|
||||
|
||||
view.spendCoins(tx);
|
||||
}
|
||||
|
||||
// Skip everything if we're
|
||||
|
||||
@ -1768,23 +1768,18 @@ ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(block) {
|
||||
}
|
||||
}
|
||||
|
||||
// Add all of the coins we are about to
|
||||
// remove. This is to ensure they appear
|
||||
// in the view array below.
|
||||
view.addTX(tx);
|
||||
|
||||
for (j = 0; j < tx.outputs.length; j++) {
|
||||
output = tx.outputs[j];
|
||||
|
||||
if (output.script.isUnspendable())
|
||||
continue;
|
||||
|
||||
// Spend added coin.
|
||||
view.spend(hash, j);
|
||||
|
||||
this.pending.spend(output);
|
||||
}
|
||||
|
||||
// Remove any created coins.
|
||||
view.removeTX(tx);
|
||||
|
||||
// Remove from transaction index.
|
||||
this.unindexTX(tx);
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var co = require('../utils/co');
|
||||
var Coins = require('./coins');
|
||||
var UndoCoins = require('./undocoins');
|
||||
|
||||
@ -32,6 +34,7 @@ function CoinView(coins) {
|
||||
|
||||
CoinView.prototype.add = function add(coins) {
|
||||
this.coins[coins.hash] = coins;
|
||||
return coins;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -40,7 +43,18 @@ CoinView.prototype.add = function add(coins) {
|
||||
*/
|
||||
|
||||
CoinView.prototype.addTX = function addTX(tx) {
|
||||
this.add(Coins.fromTX(tx));
|
||||
return this.add(Coins.fromTX(tx));
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a tx from the collection.
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
CoinView.prototype.removeTX = function removeTX(tx) {
|
||||
var coins = this.addTX(tx);
|
||||
coins.outputs.length = 0;
|
||||
return coins;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -93,12 +107,12 @@ CoinView.prototype.spend = function spend(hash, index) {
|
||||
var entry, undo;
|
||||
|
||||
if (!coins)
|
||||
return;
|
||||
return null;
|
||||
|
||||
entry = coins.spend(index);
|
||||
|
||||
if (!entry)
|
||||
return;
|
||||
return null;
|
||||
|
||||
this.undo.push(entry);
|
||||
|
||||
@ -113,23 +127,66 @@ CoinView.prototype.spend = function spend(hash, index) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill transaction(s) with coins.
|
||||
* Retrieve coins from database.
|
||||
* @param {TX} tx
|
||||
* @returns {Boolean} True if all inputs were filled.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
CoinView.prototype.fillCoins = function fillCoins(tx) {
|
||||
CoinView.prototype.getCoins = co(function* getCoins(db, hash) {
|
||||
var coins = this.coins[hash];
|
||||
|
||||
if (!coins) {
|
||||
coins = yield db.getCoins(hash);
|
||||
|
||||
if (!coins)
|
||||
return;
|
||||
|
||||
this.coins[hash] = coins;
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
/**
|
||||
* Spend coins for transaction.
|
||||
* @param {TX} tx
|
||||
* @throws on missing coin
|
||||
*/
|
||||
|
||||
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;
|
||||
assert(input.coin, 'Not all coins available.');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user