chain: refactor input spending.
This commit is contained in:
parent
967449b0d5
commit
b37b99a86e
@ -619,12 +619,12 @@ Chain.prototype.verifyDuplicates = co(function* verifyDuplicates(block, prev, st
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
||||||
|
var ret = new VerifyResult();
|
||||||
|
var view = new CoinView();
|
||||||
var height = prev.height + 1;
|
var height = prev.height + 1;
|
||||||
var historical = prev.isHistorical();
|
var historical = prev.isHistorical();
|
||||||
var sigops = 0;
|
var sigops = 0;
|
||||||
var jobs = [];
|
var jobs = [];
|
||||||
var ret = new VerifyResult();
|
|
||||||
var view = new CoinView();
|
|
||||||
var i, tx, valid;
|
var i, tx, valid;
|
||||||
|
|
||||||
if (this.options.spv)
|
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.
|
// Ensure tx is not double spending an output.
|
||||||
if (i > 0) {
|
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!');
|
assert(!historical, 'BUG: Spent inputs in historical data!');
|
||||||
throw new VerifyError(block,
|
throw new VerifyError(block,
|
||||||
'invalid',
|
'invalid',
|
||||||
'bad-txns-inputs-missingorspent',
|
'bad-txns-inputs-missingorspent',
|
||||||
100);
|
100);
|
||||||
}
|
}
|
||||||
|
|
||||||
view.spendCoins(tx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip everything if we're
|
// Skip everything if we're
|
||||||
|
|||||||
@ -1708,7 +1708,7 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
|
|||||||
|
|
||||||
// Write undo coins (if there are any).
|
// Write undo coins (if there are any).
|
||||||
if (!view.undo.isEmpty())
|
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.
|
// Prune height-288 if pruning is enabled.
|
||||||
yield this.pruneBlock(block);
|
yield this.pruneBlock(block);
|
||||||
|
|||||||
@ -105,12 +105,23 @@ CoinView.prototype.has = function has(hash, index) {
|
|||||||
|
|
||||||
CoinView.prototype.spend = function spend(hash, index) {
|
CoinView.prototype.spend = function spend(hash, index) {
|
||||||
var coins = this.unspent[hash];
|
var coins = this.unspent[hash];
|
||||||
var entry, undo;
|
|
||||||
|
|
||||||
if (!coins)
|
if (!coins)
|
||||||
return null;
|
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)
|
if (!entry)
|
||||||
return null;
|
return null;
|
||||||
@ -191,7 +202,7 @@ CoinView.prototype.ensureInputs = co(function* ensureInputs(db, tx) {
|
|||||||
/**
|
/**
|
||||||
* Spend coins for transaction.
|
* Spend coins for transaction.
|
||||||
* @param {TX} tx
|
* @param {TX} tx
|
||||||
* @throws on missing coin
|
* @returns {Boolean}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CoinView.prototype.spendCoins = function spendCoins(tx) {
|
CoinView.prototype.spendCoins = function spendCoins(tx) {
|
||||||
@ -200,11 +211,42 @@ CoinView.prototype.spendCoins = function spendCoins(tx) {
|
|||||||
for (i = 0; i < tx.inputs.length; i++) {
|
for (i = 0; i < tx.inputs.length; i++) {
|
||||||
input = tx.inputs[i];
|
input = tx.inputs[i];
|
||||||
prevout = input.prevout;
|
prevout = input.prevout;
|
||||||
|
|
||||||
input.coin = this.spend(prevout.hash, prevout.index);
|
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.
|
* Convert collection to an array.
|
||||||
* @returns {Coins[]}
|
* @returns {Coins[]}
|
||||||
|
|||||||
@ -99,6 +99,17 @@ UndoCoins.prototype.isEmpty = function isEmpty() {
|
|||||||
return this.items.length === 0;
|
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.
|
* Retrieve the last undo coin.
|
||||||
* @returns {UndoCoin}
|
* @returns {UndoCoin}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user