chain: refactor input spending.

This commit is contained in:
Christopher Jeffrey 2016-12-01 23:37:57 -08:00
parent 967449b0d5
commit b37b99a86e
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 61 additions and 10 deletions

View File

@ -619,12 +619,12 @@ Chain.prototype.verifyDuplicates = co(function* verifyDuplicates(block, prev, st
*/
Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
var ret = new VerifyResult();
var view = new CoinView();
var height = prev.height + 1;
var historical = prev.isHistorical();
var sigops = 0;
var jobs = [];
var ret = new VerifyResult();
var view = new CoinView();
var i, tx, valid;
if (this.options.spv)
@ -639,15 +639,13 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
// Ensure tx is not double spending an output.
if (i > 0) {
if (!(yield view.hasInputs(this.db, tx))) {
if (!(yield view.spendInputs(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

View File

@ -1708,7 +1708,7 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
// Write undo coins (if there are any).
if (!view.undo.isEmpty())
this.put(layout.u(block.hash()), view.undo.toRaw());
this.put(layout.u(block.hash()), view.undo.commit());
// Prune height-288 if pruning is enabled.
yield this.pruneBlock(block);

View File

@ -105,12 +105,23 @@ CoinView.prototype.has = function has(hash, index) {
CoinView.prototype.spend = function spend(hash, index) {
var coins = this.unspent[hash];
var entry, undo;
if (!coins)
return null;
entry = coins.spend(index);
return this.spendFrom(coins, index);
};
/**
* Remove a coin and return it.
* @param {Coins} coins
* @param {Number} index
* @returns {Coin}
*/
CoinView.prototype.spendFrom = function spendFrom(coins, index) {
var entry = coins.spend(index);
var undo;
if (!entry)
return null;
@ -191,7 +202,7 @@ CoinView.prototype.ensureInputs = co(function* ensureInputs(db, tx) {
/**
* Spend coins for transaction.
* @param {TX} tx
* @throws on missing coin
* @returns {Boolean}
*/
CoinView.prototype.spendCoins = function spendCoins(tx) {
@ -200,11 +211,42 @@ CoinView.prototype.spendCoins = function spendCoins(tx) {
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
prevout = input.prevout;
input.coin = this.spend(prevout.hash, prevout.index);
assert(input.coin, 'Not all coins available.');
if (!input.coin)
return false;
}
return true;
};
/**
* Spend coins for transaction.
* @param {TX} tx
* @returns {Boolean}
*/
CoinView.prototype.spendInputs = co(function* spendInputs(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;
input.coin = this.spendFrom(coins, prevout.index);
if (!input.coin)
return false;
}
return true;
});
/**
* Convert collection to an array.
* @returns {Coins[]}

View File

@ -99,6 +99,17 @@ UndoCoins.prototype.isEmpty = function isEmpty() {
return this.items.length === 0;
};
/**
* Render the undo coins.
* @returns {Buffer}
*/
UndoCoins.prototype.commit = function commit() {
var raw = this.toRaw();
this.items.length = 0;
return raw;
};
/**
* Retrieve the last undo coin.
* @returns {UndoCoin}