chain: refactor tx indexing.
This commit is contained in:
parent
7157d06464
commit
aad1691f7e
@ -639,7 +639,7 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
||||
tx = block.txs[i];
|
||||
|
||||
// Ensure tx is not double spending an output.
|
||||
if (!tx.isCoinbase()) {
|
||||
if (i > 0) {
|
||||
if (!view.fillCoins(tx)) {
|
||||
assert(!historical, 'BUG: Spent inputs in historical data!');
|
||||
throw new VerifyError(block,
|
||||
@ -677,7 +677,7 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
||||
}
|
||||
|
||||
// Contextual sanity checks.
|
||||
if (!tx.isCoinbase()) {
|
||||
if (i > 0) {
|
||||
if (!tx.checkInputs(height, ret)) {
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
|
||||
@ -1665,80 +1665,14 @@ ChainDB.prototype.removeBlock = co(function* removeBlock(hash) {
|
||||
});
|
||||
|
||||
/**
|
||||
* Connect block inputs.
|
||||
* @param {Block} block
|
||||
* @returns {Promise} - Returns {@link Block}.
|
||||
* Commit coin view to database.
|
||||
* @private
|
||||
* @param {CoinView} view
|
||||
*/
|
||||
|
||||
ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
|
||||
var i, j, tx, input, output, prev;
|
||||
var hashes, address, hash, coins, raw;
|
||||
ChainDB.prototype.saveView = function saveView(view) {
|
||||
var i, coins, raw;
|
||||
|
||||
if (this.options.spv)
|
||||
return;
|
||||
|
||||
// Genesis block's coinbase is unspendable.
|
||||
if (this.chain.isGenesis(block)) {
|
||||
this.pending.connect(block);
|
||||
return;
|
||||
}
|
||||
|
||||
this.pending.connect(block);
|
||||
|
||||
for (i = 0; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
hash = tx.hash();
|
||||
|
||||
if (this.options.indexTX) {
|
||||
this.put(layout.t(hash), tx.toExtended());
|
||||
if (this.options.indexAddress) {
|
||||
hashes = tx.getHashes();
|
||||
for (j = 0; j < hashes.length; j++) {
|
||||
address = hashes[j];
|
||||
this.put(layout.T(address, hash), DUMMY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!tx.isCoinbase()) {
|
||||
for (j = 0; j < tx.inputs.length; j++) {
|
||||
input = tx.inputs[j];
|
||||
|
||||
assert(input.coin);
|
||||
|
||||
if (this.options.indexAddress) {
|
||||
address = input.getHash();
|
||||
if (address) {
|
||||
prev = input.prevout;
|
||||
this.del(layout.C(address, prev.hash, prev.index));
|
||||
}
|
||||
}
|
||||
|
||||
this.pending.spend(input.coin);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < tx.outputs.length; j++) {
|
||||
output = tx.outputs[j];
|
||||
|
||||
if (output.script.isUnspendable())
|
||||
continue;
|
||||
|
||||
if (this.options.indexAddress) {
|
||||
address = output.getHash();
|
||||
if (address)
|
||||
this.put(layout.C(address, hash, j), DUMMY);
|
||||
}
|
||||
|
||||
this.pending.add(output);
|
||||
}
|
||||
}
|
||||
|
||||
// Write undo coins (if there are any).
|
||||
if (!view.undo.isEmpty())
|
||||
this.put(layout.u(block.hash()), view.undo.toRaw());
|
||||
|
||||
// Commit new coin state.
|
||||
view = view.toArray();
|
||||
|
||||
for (i = 0; i < view.length; i++) {
|
||||
@ -1752,6 +1686,56 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
|
||||
this.coinCache.push(coins.hash, raw);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect block inputs.
|
||||
* @param {Block} block
|
||||
* @returns {Promise} - Returns {@link Block}.
|
||||
*/
|
||||
|
||||
ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
|
||||
var i, j, tx, input, output;
|
||||
|
||||
if (this.options.spv)
|
||||
return;
|
||||
|
||||
this.pending.connect(block);
|
||||
|
||||
// Genesis block's coinbase is unspendable.
|
||||
if (this.chain.isGenesis(block))
|
||||
return;
|
||||
|
||||
for (i = 0; i < block.txs.length; 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.spend(input.coin);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < tx.outputs.length; j++) {
|
||||
output = tx.outputs[j];
|
||||
|
||||
if (output.script.isUnspendable())
|
||||
continue;
|
||||
|
||||
this.pending.add(output);
|
||||
}
|
||||
|
||||
// Index the transaction if enabled.
|
||||
this.indexTX(tx);
|
||||
}
|
||||
|
||||
// Commit new coin state.
|
||||
this.saveView(view);
|
||||
|
||||
// Write undo coins (if there are any).
|
||||
if (!view.undo.isEmpty())
|
||||
this.put(layout.u(block.hash()), view.undo.toRaw());
|
||||
|
||||
yield this.pruneBlock(block);
|
||||
});
|
||||
@ -1763,8 +1747,7 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
|
||||
*/
|
||||
|
||||
ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(block) {
|
||||
var i, j, tx, input, output, prev, view;
|
||||
var hashes, address, hash, coins, raw;
|
||||
var i, j, view, tx, hash, input, output;
|
||||
|
||||
if (this.options.spv)
|
||||
return;
|
||||
@ -1777,31 +1760,10 @@ ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(block) {
|
||||
tx = block.txs[i];
|
||||
hash = tx.hash('hex');
|
||||
|
||||
if (this.options.indexTX) {
|
||||
this.del(layout.t(hash));
|
||||
if (this.options.indexAddress) {
|
||||
hashes = tx.getHashes();
|
||||
for (j = 0; j < hashes.length; j++) {
|
||||
address = hashes[j];
|
||||
this.del(layout.T(address, hash));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!tx.isCoinbase()) {
|
||||
if (i > 0) {
|
||||
for (j = 0; j < tx.inputs.length; j++) {
|
||||
input = tx.inputs[j];
|
||||
|
||||
assert(input.coin);
|
||||
|
||||
if (this.options.indexAddress) {
|
||||
address = input.getHash();
|
||||
if (address) {
|
||||
prev = input.prevout;
|
||||
this.put(layout.C(address, prev.hash, prev.index), DUMMY);
|
||||
}
|
||||
}
|
||||
|
||||
this.pending.add(input.coin);
|
||||
}
|
||||
}
|
||||
@ -1817,36 +1779,21 @@ ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(block) {
|
||||
if (output.script.isUnspendable())
|
||||
continue;
|
||||
|
||||
if (this.options.indexAddress) {
|
||||
address = output.getHash();
|
||||
if (address)
|
||||
this.del(layout.C(address, hash, j));
|
||||
}
|
||||
|
||||
// Spend added coin.
|
||||
view.spend(hash, j);
|
||||
|
||||
this.pending.spend(output);
|
||||
}
|
||||
|
||||
// Remove from transaction index.
|
||||
this.unindexTX(tx);
|
||||
}
|
||||
|
||||
// Commit new coin state.
|
||||
this.saveView(view);
|
||||
|
||||
// Remove undo coins.
|
||||
this.del(layout.u(block.hash()));
|
||||
|
||||
// Commit new coin state.
|
||||
view = view.toArray();
|
||||
|
||||
for (i = 0; i < view.length; i++) {
|
||||
coins = view[i];
|
||||
if (coins.isEmpty()) {
|
||||
this.del(layout.c(coins.hash));
|
||||
this.coinCache.unpush(coins.hash);
|
||||
} else {
|
||||
raw = coins.toRaw();
|
||||
this.put(layout.c(coins.hash), raw);
|
||||
this.coinCache.push(coins.hash, raw);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1889,6 +1836,108 @@ ChainDB.prototype.saveOptions = function saveOptions() {
|
||||
return this.db.put(layout.O, this.options.toRaw());
|
||||
};
|
||||
|
||||
/**
|
||||
* Index a transaction by txid and address.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
ChainDB.prototype.indexTX = function indexTX(tx) {
|
||||
var hash = tx.hash();
|
||||
var i, input, output, prevout;
|
||||
var hashes, addr;
|
||||
|
||||
if (this.options.indexTX) {
|
||||
this.put(layout.t(hash), tx.toExtended());
|
||||
if (this.options.indexAddress) {
|
||||
hashes = tx.getHashes();
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
addr = hashes[i];
|
||||
this.put(layout.T(addr, hash), DUMMY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.options.indexAddress)
|
||||
return;
|
||||
|
||||
if (!tx.isCoinbase()) {
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prevout = input.prevout;
|
||||
addr = input.getHash();
|
||||
|
||||
assert(input.coin);
|
||||
|
||||
if (!addr)
|
||||
continue;
|
||||
|
||||
this.del(layout.C(addr, prevout.hash, prevout.index));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
output = tx.outputs[i];
|
||||
addr = output.getHash();
|
||||
|
||||
if (!addr)
|
||||
continue;
|
||||
|
||||
this.put(layout.C(addr, hash, i), DUMMY);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove transaction from index.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
ChainDB.prototype.unindexTX = function unindexTX(tx) {
|
||||
var hash = tx.hash();
|
||||
var i, input, output, prevout;
|
||||
var hashes, addr;
|
||||
|
||||
if (this.options.indexTX) {
|
||||
this.del(layout.t(hash));
|
||||
if (this.options.indexAddress) {
|
||||
hashes = tx.getHashes();
|
||||
for (i = 0; i < hashes.length; i++) {
|
||||
addr = hashes[i];
|
||||
this.del(layout.T(addr, hash));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.options.indexAddress)
|
||||
return;
|
||||
|
||||
if (!tx.isCoinbase()) {
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prevout = input.prevout;
|
||||
addr = input.getHash();
|
||||
|
||||
assert(input.coin);
|
||||
|
||||
if (!addr)
|
||||
continue;
|
||||
|
||||
this.put(layout.C(addr, prevout.hash, prevout.index), DUMMY);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
output = tx.outputs[i];
|
||||
addr = output.getHash();
|
||||
|
||||
if (!addr)
|
||||
continue;
|
||||
|
||||
this.del(layout.C(addr, hash, i));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Chain Options
|
||||
* @constructor
|
||||
|
||||
Loading…
Reference in New Issue
Block a user