chain: refactor undo coins.
This commit is contained in:
parent
873475b835
commit
3d876858f9
@ -856,27 +856,6 @@ ChainDB.prototype.hasCoins = function hasCoins(hash) {
|
||||
return this.db.has(layout.c(hash));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a view of the existing coins necessary to verify a block.
|
||||
* @param {Block} block
|
||||
* @returns {Promise} - Returns {@link CoinView}.
|
||||
*/
|
||||
|
||||
ChainDB.prototype.getCoinView = co(function* getCoinView(block, callback) {
|
||||
var view = new CoinView();
|
||||
var prevout = block.getPrevout();
|
||||
var i, prev, coins;
|
||||
|
||||
for (i = 0; i < prevout.length; i++) {
|
||||
prev = prevout[i];
|
||||
coins = yield this.getCoins(prev);
|
||||
if (coins)
|
||||
view.add(coins);
|
||||
}
|
||||
|
||||
return view;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get coins necessary to be resurrected during a reorg.
|
||||
* @param {Hash} hash
|
||||
@ -890,32 +869,6 @@ ChainDB.prototype.getUndoCoins = co(function* getUndoCoins(hash) {
|
||||
return UndoCoins.fromRaw(data);
|
||||
});
|
||||
|
||||
/**
|
||||
* Get a coin view containing unspent coins as
|
||||
* well as the coins to be resurrected for a reorg.
|
||||
* (Note: fills block with undo coins).
|
||||
* @param {Block} block
|
||||
* @returns {Promise} - Returns {@link CoinView}.
|
||||
*/
|
||||
|
||||
ChainDB.prototype.getUndoView = co(function* getUndoView(block) {
|
||||
var view = yield this.getCoinView(block);
|
||||
var undo = yield this.getUndoCoins(block.hash());
|
||||
var index = 0;
|
||||
var i, j, tx, input;
|
||||
|
||||
for (i = 1; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
for (j = 0; j < tx.inputs.length; j++) {
|
||||
input = tx.inputs[j];
|
||||
input.coin = undo.apply(index++, view, input.prevout);
|
||||
assert(input.coin);
|
||||
}
|
||||
}
|
||||
|
||||
return view;
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve a block from the database (not filled with coins).
|
||||
* @param {Hash} hash
|
||||
@ -972,11 +925,24 @@ ChainDB.prototype.getRawBlock = co(function* getRawBlock(block) {
|
||||
|
||||
ChainDB.prototype.getFullBlock = co(function* getFullBlock(hash) {
|
||||
var block = yield this.getBlock(hash);
|
||||
var i, j, view, undo, tx, input;
|
||||
|
||||
if (!block)
|
||||
return;
|
||||
|
||||
yield this.getUndoView(block);
|
||||
view = new CoinView();
|
||||
undo = yield this.getUndoCoins(block.hash());
|
||||
|
||||
for (i = block.txs.length - 1; i > 0; i--) {
|
||||
tx = block.txs[i];
|
||||
for (j = tx.inputs.length - 1; j >= 0; j--) {
|
||||
input = tx.inputs[j];
|
||||
input.coin = undo.apply(view, input.prevout);
|
||||
}
|
||||
}
|
||||
|
||||
// Undo coins should be empty.
|
||||
assert(undo.items.length === 0, 'Undo coins data inconsistency.');
|
||||
|
||||
return block;
|
||||
});
|
||||
@ -1709,6 +1675,7 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
|
||||
if (this.chain.isGenesis(block))
|
||||
return;
|
||||
|
||||
// Update chain state value.
|
||||
for (i = 0; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
|
||||
@ -1740,6 +1707,7 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
|
||||
if (!view.undo.isEmpty())
|
||||
this.put(layout.u(block.hash()), view.undo.toRaw());
|
||||
|
||||
// Prune height-288 if pruning is enabled.
|
||||
yield this.pruneBlock(block);
|
||||
});
|
||||
|
||||
@ -1750,27 +1718,21 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
|
||||
*/
|
||||
|
||||
ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(block) {
|
||||
var i, j, view, tx, input, output;
|
||||
var i, j, view, undo, tx, input, output;
|
||||
|
||||
if (this.options.spv)
|
||||
return;
|
||||
|
||||
view = yield this.getUndoView(block);
|
||||
view = new CoinView();
|
||||
undo = yield this.getUndoCoins(block.hash());
|
||||
|
||||
this.pending.disconnect(block);
|
||||
|
||||
// Disconnect all transactions.
|
||||
for (i = block.txs.length - 1; i >= 0; i--) {
|
||||
tx = block.txs[i];
|
||||
|
||||
if (i > 0) {
|
||||
for (j = 0; j < tx.inputs.length; j++) {
|
||||
input = tx.inputs[j];
|
||||
assert(input.coin);
|
||||
this.pending.add(input.coin);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < tx.outputs.length; j++) {
|
||||
for (j = tx.outputs.length - 1; j >= 0; j--) {
|
||||
output = tx.outputs[j];
|
||||
|
||||
if (output.script.isUnspendable())
|
||||
@ -1782,10 +1744,23 @@ ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(block) {
|
||||
// Remove any created coins.
|
||||
view.removeTX(tx);
|
||||
|
||||
if (i > 0) {
|
||||
yield view.ensureInputs(this, tx);
|
||||
|
||||
for (j = tx.inputs.length - 1; j >= 0; j--) {
|
||||
input = tx.inputs[j];
|
||||
input.coin = undo.apply(view, input.prevout);
|
||||
this.pending.add(input.coin);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from transaction index.
|
||||
this.unindexTX(tx);
|
||||
}
|
||||
|
||||
// Undo coins should be empty.
|
||||
assert(undo.items.length === 0, 'Undo coins data inconsistency.');
|
||||
|
||||
// Commit new coin state.
|
||||
this.saveView(view);
|
||||
|
||||
|
||||
@ -19,11 +19,11 @@ var UndoCoins = require('./undocoins');
|
||||
* @property {Object} coins
|
||||
*/
|
||||
|
||||
function CoinView(coins) {
|
||||
function CoinView() {
|
||||
if (!(this instanceof CoinView))
|
||||
return new CoinView(coins);
|
||||
return new CoinView();
|
||||
|
||||
this.coins = coins || {};
|
||||
this.unspent = {};
|
||||
this.undo = new UndoCoins();
|
||||
}
|
||||
|
||||
@ -32,8 +32,8 @@ function CoinView(coins) {
|
||||
* @param {Coins} coins
|
||||
*/
|
||||
|
||||
CoinView.prototype.add = function add(coins) {
|
||||
this.coins[coins.hash] = coins;
|
||||
CoinView.prototype.addCoins = function addCoins(coins) {
|
||||
this.unspent[coins.hash] = coins;
|
||||
return coins;
|
||||
};
|
||||
|
||||
@ -43,7 +43,8 @@ CoinView.prototype.add = function add(coins) {
|
||||
*/
|
||||
|
||||
CoinView.prototype.addTX = function addTX(tx) {
|
||||
return this.add(Coins.fromTX(tx));
|
||||
var coins = Coins.fromTX(tx);
|
||||
return this.addCoins(coins);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -52,9 +53,9 @@ CoinView.prototype.addTX = function addTX(tx) {
|
||||
*/
|
||||
|
||||
CoinView.prototype.removeTX = function removeTX(tx) {
|
||||
var coins = this.addTX(tx);
|
||||
var coins = Coins.fromTX(tx);
|
||||
coins.outputs.length = 0;
|
||||
return coins;
|
||||
return this.addCoins(coins);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -65,7 +66,7 @@ CoinView.prototype.removeTX = function removeTX(tx) {
|
||||
*/
|
||||
|
||||
CoinView.prototype.get = function get(hash, index) {
|
||||
var coins = this.coins[hash];
|
||||
var coins = this.unspent[hash];
|
||||
var entry;
|
||||
|
||||
if (!coins)
|
||||
@ -87,7 +88,7 @@ CoinView.prototype.get = function get(hash, index) {
|
||||
*/
|
||||
|
||||
CoinView.prototype.has = function has(hash, index) {
|
||||
var coins = this.coins[hash];
|
||||
var coins = this.unspent[hash];
|
||||
|
||||
if (!coins)
|
||||
return false;
|
||||
@ -103,7 +104,7 @@ CoinView.prototype.has = function has(hash, index) {
|
||||
*/
|
||||
|
||||
CoinView.prototype.spend = function spend(hash, index) {
|
||||
var coins = this.coins[hash];
|
||||
var coins = this.unspent[hash];
|
||||
var entry, undo;
|
||||
|
||||
if (!coins)
|
||||
@ -133,7 +134,7 @@ CoinView.prototype.spend = function spend(hash, index) {
|
||||
*/
|
||||
|
||||
CoinView.prototype.getCoins = co(function* getCoins(db, hash) {
|
||||
var coins = this.coins[hash];
|
||||
var coins = this.unspent[hash];
|
||||
|
||||
if (!coins) {
|
||||
coins = yield db.getCoins(hash);
|
||||
@ -141,7 +142,7 @@ CoinView.prototype.getCoins = co(function* getCoins(db, hash) {
|
||||
if (!coins)
|
||||
return;
|
||||
|
||||
this.coins[hash] = coins;
|
||||
this.unspent[hash] = coins;
|
||||
}
|
||||
|
||||
return coins;
|
||||
@ -172,6 +173,21 @@ CoinView.prototype.hasInputs = co(function* hasInputs(db, tx) {
|
||||
return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* Read all input coins into unspent map.
|
||||
* @param {ChainDB} db
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
CoinView.prototype.ensureInputs = co(function* ensureInputs(db, tx) {
|
||||
var i, input;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
yield this.getCoins(db, input.prevout.hash);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Spend coins for transaction.
|
||||
* @param {TX} tx
|
||||
@ -195,13 +211,13 @@ CoinView.prototype.spendCoins = function spendCoins(tx) {
|
||||
*/
|
||||
|
||||
CoinView.prototype.toArray = function toArray() {
|
||||
var keys = Object.keys(this.coins);
|
||||
var keys = Object.keys(this.unspent);
|
||||
var out = [];
|
||||
var i, hash;
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
hash = keys[i];
|
||||
out.push(this.coins[hash]);
|
||||
out.push(this.unspent[hash]);
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
@ -26,6 +26,9 @@ var decompress = compressor.decompress;
|
||||
*/
|
||||
|
||||
function UndoCoins() {
|
||||
if (!(this instanceof UndoCoins))
|
||||
return new UndoCoins();
|
||||
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
@ -107,14 +110,13 @@ UndoCoins.prototype.top = function top() {
|
||||
|
||||
/**
|
||||
* Re-apply undo coins to a view, effectively unspending them.
|
||||
* @param {Number} i
|
||||
* @param {CoinView} view
|
||||
* @param {Outpoint} outpoint
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
UndoCoins.prototype.apply = function apply(i, view, outpoint) {
|
||||
var undo = this.items[i];
|
||||
UndoCoins.prototype.apply = function apply(view, outpoint) {
|
||||
var undo = this.items.pop();
|
||||
var hash = outpoint.hash;
|
||||
var index = outpoint.index;
|
||||
var coins;
|
||||
@ -124,19 +126,21 @@ UndoCoins.prototype.apply = function apply(i, view, outpoint) {
|
||||
if (undo.height !== -1) {
|
||||
coins = new Coins();
|
||||
|
||||
assert(!view.coins[hash]);
|
||||
view.coins[hash] = coins;
|
||||
assert(!view.unspent[hash]);
|
||||
view.unspent[hash] = coins;
|
||||
|
||||
coins.coinbase = undo.coinbase;
|
||||
coins.height = undo.height;
|
||||
coins.version = undo.version;
|
||||
} else {
|
||||
coins = view.coins[hash];
|
||||
coins = view.unspent[hash];
|
||||
assert(coins);
|
||||
}
|
||||
|
||||
coins.add(index, undo.toOutput());
|
||||
|
||||
assert(coins.has(index));
|
||||
|
||||
return coins.getCoin(index);
|
||||
};
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user