chain: refactor checkpoints handling. other misc refactoring.

This commit is contained in:
Christopher Jeffrey 2017-09-06 14:17:39 -07:00
parent 5f82c0d8c2
commit eb5469088c
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 84 additions and 80 deletions

View File

@ -164,8 +164,23 @@ Chain.prototype.verifyContext = async function verifyContext(block, prev, flags)
// Initial non-contextual verification.
const state = await this.verify(block, prev, flags);
// Skip everything if we're in SPV mode.
if (this.options.spv) {
const view = new CoinView();
return [view, state];
}
// Skip everything if we're using checkpoints.
if (this.isHistorical(prev)) {
const view = await this.updateInputs(block, prev);
return [view, state];
}
// BIP30 - Verify there are no duplicate txids.
await this.verifyDuplicates(block, prev, state);
// Note that BIP34 made it impossible to create
// duplicate txids.
if (!state.hasBIP34())
await this.verifyDuplicates(block, prev);
// Verify scripts, spend and add coins.
const view = await this.verifyInputs(block, prev, state);
@ -202,16 +217,6 @@ Chain.prototype._verifyBlock = async function _verifyBlock(block) {
return await this.verifyContext(block, this.tip, flags);
};
/**
* Test whether a block is the genesis block.
* @param {Block} block
* @returns {Boolean}
*/
Chain.prototype.isGenesis = function isGenesis(block) {
return block.hash('hex') === this.network.genesis.hash;
};
/**
* Test whether the hash is in the main chain.
* @param {Hash} hash
@ -348,11 +353,6 @@ Chain.prototype.isHistorical = function isHistorical(prev) {
*/
Chain.prototype.verify = async function verify(block, prev, flags) {
const deployments = this.network.deployments;
const hash = block.hash('hex');
const now = this.network.now();
const height = prev.height + 1;
assert(typeof flags === 'number');
// Extra sanity check.
@ -360,6 +360,7 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
throw new VerifyError(block, 'invalid', 'bad-prevblk', 0);
// Verify a checkpoint if there is one.
const hash = block.hash('hex');
if (!this.verifyCheckpoint(prev, hash)) {
throw new VerifyError(block,
'checkpoint',
@ -373,7 +374,7 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
// validated outside in the header chain.
if (this.isHistorical(prev)) {
if (this.options.spv)
return new DeploymentState();
return this.state;
// Once segwit is active, we will still
// need to check for block mutability.
@ -391,10 +392,6 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
throw new VerifyError(block, 'invalid', reason, score, true);
}
// Skip all blocks in spv mode.
if (this.options.spv)
return this.state;
// Ensure the POW is what we expect.
const bits = await this.getTarget(block.time, prev);
@ -405,6 +402,11 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
100);
}
// Skip all blocks in spv mode once
// we've verified the network target.
if (this.options.spv)
return this.state;
// Ensure the timestamp is correct.
const mtp = await this.getMedianTime(prev);
@ -418,7 +420,7 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
// Check timestamp against adj-time+2hours.
// If this fails we may be able to accept
// the block later.
if (block.time > now + 2 * 60 * 60) {
if (block.time > this.network.now() + 2 * 60 * 60) {
throw new VerifyError(block,
'invalid',
'time-too-new',
@ -426,6 +428,9 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
true);
}
// Calculate height of current block.
const height = prev.height + 1;
// Only allow version 2 blocks (coinbase height)
// once the majority of blocks are using it.
if (block.version < 2 && height >= this.network.block.bip34height)
@ -446,7 +451,8 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
// Enforce BIP91/BIP148.
if (state.hasBIP91() || state.hasBIP148()) {
if (!consensus.hasBit(block.version, deployments.segwit.bit))
const {segwit} = this.network.deployments;
if (!consensus.hasBit(block.version, segwit.bit))
throw new VerifyError(block, 'invalid', 'bad-no-segwit', 0);
}
@ -476,7 +482,7 @@ Chain.prototype.verify = async function verify(block, prev, flags) {
}
// Check the commitment hash for segwit.
let commit;
let commit = null;
if (state.hasWitness()) {
commit = block.getCommitmentHash();
if (commit) {
@ -658,38 +664,49 @@ Chain.prototype.setDeploymentState = function setDeploymentState(state) {
* @returns {Promise}
*/
Chain.prototype.verifyDuplicates = async function verifyDuplicates(block, prev, state) {
if (this.options.spv)
return;
if (this.isHistorical(prev))
return;
// BIP34 made it impossible to
// create duplicate txids.
if (state.hasBIP34())
return;
// Check all transactions.
Chain.prototype.verifyDuplicates = async function verifyDuplicates(block, prev) {
for (const tx of block.txs) {
const result = await this.db.hasCoins(tx);
if (!await this.hasCoins(tx))
continue;
if (result) {
const height = prev.height + 1;
// Blocks 91842 and 91880 created duplicate
// txids by using the same exact output script
// and extraNonce.
if (this.network.bip30[height]) {
if (block.hash('hex') === this.network.bip30[height])
continue;
}
const height = prev.height + 1;
const hash = this.network.bip30[height];
// Blocks 91842 and 91880 created duplicate
// txids by using the same exact output script
// and extraNonce.
if (!hash || block.hash('hex') !== hash)
throw new VerifyError(block, 'invalid', 'bad-txns-BIP30', 100);
}
}
};
/**
* Spend and update inputs (checkpoints only).
* @private
* @param {Block} block
* @param {ChainEntry} prev
* @returns {Promise} - Returns {@link CoinView}.
*/
Chain.prototype.updateInputs = async function updateInputs(block, prev) {
const view = new CoinView();
const height = prev.height + 1;
const cb = block.txs[0];
view.addTX(cb, height);
for (let i = 1; i < block.txs.length; i++) {
const tx = block.txs[i];
assert(await view.spendInputs(this.db, tx),
'BUG: Spent inputs in historical data!');
view.addTX(tx, height);
}
return view;
};
/**
* Check block transactions for all things pertaining
* to inputs. This function is important because it is
@ -710,13 +727,8 @@ Chain.prototype.verifyDuplicates = async function verifyDuplicates(block, prev,
Chain.prototype.verifyInputs = async function verifyInputs(block, prev, state) {
const view = new CoinView();
if (this.options.spv)
return view;
const interval = this.network.halvingInterval;
const height = prev.height + 1;
const historical = this.isHistorical(prev);
const interval = this.network.halvingInterval;
let sigops = 0;
let reward = 0;
@ -728,7 +740,6 @@ Chain.prototype.verifyInputs = async function verifyInputs(block, prev, state) {
// Ensure tx is not double spending an output.
if (i > 0) {
if (!await view.spendInputs(this.db, tx)) {
assert(!historical, 'BUG: Spent inputs in historical data!');
throw new VerifyError(block,
'invalid',
'bad-txns-inputs-missingorspent',
@ -736,13 +747,6 @@ Chain.prototype.verifyInputs = async function verifyInputs(block, prev, state) {
}
}
// Skip everything if we're
// using checkpoints.
if (historical) {
view.addTX(tx, height);
continue;
}
// Verify sequence locks.
if (i > 0 && tx.version >= 2) {
const valid = await this.verifyLocks(prev, tx, view, state.lockFlags);
@ -790,10 +794,6 @@ Chain.prototype.verifyInputs = async function verifyInputs(block, prev, state) {
view.addTX(tx, height);
}
// Skip script verification.
if (historical)
return view;
// Make sure the miner isn't trying to conjure more coins.
reward += consensus.getReward(height, interval);
@ -967,7 +967,7 @@ Chain.prototype.reorganizeSPV = async function reorganizeSPV(competitor) {
*/
Chain.prototype.disconnect = async function disconnect(entry) {
let block = await this.db.getBlock(entry.hash);
let block = await this.getBlock(entry.hash);
if (!block) {
if (!this.options.spv)
@ -1001,7 +1001,7 @@ Chain.prototype.disconnect = async function disconnect(entry) {
Chain.prototype.reconnect = async function reconnect(entry) {
const flags = common.flags.VERIFY_NONE;
let block = await this.db.getBlock(entry.hash);
let block = await this.getBlock(entry.hash);
if (!block) {
if (!this.options.spv)
@ -1444,7 +1444,7 @@ Chain.prototype.connect = async function connect(prev, block, flags) {
// chainwork is less than or equal to
// our tip's. Add the block but do _not_
// connect the inputs.
if (entry.chainwork.cmp(this.tip.chainwork) <= 0) {
if (entry.chainwork.lte(this.tip.chainwork)) {
// Save block to an alternate chain.
await this.saveAlternate(entry, block, prev, flags);
} else {
@ -1725,7 +1725,7 @@ Chain.prototype.purgeOrphans = function purgeOrphans() {
Chain.prototype.limitOrphans = function limitOrphans() {
const now = util.now();
let oldest;
let oldest = null;
for (const orphan of this.orphanMap.values()) {
if (now < orphan.time + 60 * 60) {
if (!oldest || orphan.time < oldest.time)
@ -2294,12 +2294,11 @@ Chain.prototype.getTarget = async function getTarget(time, prev) {
&& prev.bits === pow.bits) {
const cache = this.getPrevCache(prev);
if (cache) {
if (cache)
prev = cache;
continue;
}
else
prev = await this.getPrevious(prev);
prev = await this.getPrevious(prev);
assert(prev);
}
}
@ -2392,9 +2391,10 @@ Chain.prototype.isActive = async function isActive(prev, deployment) {
*/
Chain.prototype.getState = async function getState(prev, deployment) {
const bit = deployment.bit;
let window = this.network.minerWindow;
let threshold = this.network.activationThreshold;
const bit = deployment.bit;
if (deployment.threshold !== -1)
threshold = deployment.threshold;
@ -2404,6 +2404,7 @@ Chain.prototype.getState = async function getState(prev, deployment) {
if (((prev.height + 1) % window) !== 0) {
const height = prev.height - ((prev.height + 1) % window);
prev = await this.getAncestor(prev, height);
if (!prev)
@ -2415,6 +2416,7 @@ Chain.prototype.getState = async function getState(prev, deployment) {
let entry = prev;
let state = thresholdStates.DEFINED;
const compute = [];
while (entry) {
@ -2436,6 +2438,7 @@ Chain.prototype.getState = async function getState(prev, deployment) {
compute.push(entry);
const height = entry.height - window;
entry = await this.getAncestor(entry, height);
}
@ -2460,14 +2463,15 @@ Chain.prototype.getState = async function getState(prev, deployment) {
}
case thresholdStates.STARTED: {
const time = await this.getMedianTime(entry);
let block = entry;
let count = 0;
if (time >= deployment.timeout) {
state = thresholdStates.FAILED;
break;
}
let block = entry;
let count = 0;
for (let i = 0; i < window; i++) {
if (block.hasBit(bit))
count++;

View File

@ -800,7 +800,7 @@ Mempool.prototype.insertTX = async function insertTX(tx, id) {
// We can test whether this is an
// non-fully-spent transaction on
// the chain.
if (await this.chain.db.hasCoins(tx)) {
if (await this.chain.hasCoins(tx)) {
throw new VerifyError(tx,
'alreadyknown',
'txn-already-known',
@ -1657,7 +1657,7 @@ Mempool.prototype.getCoinView = async function getCoinView(tx) {
continue;
}
const coin = await this.chain.db.readCoin(prevout);
const coin = await this.chain.readCoin(prevout);
if (!coin) {
const coins = new Coins();