chain: refactor checkpoints handling. other misc refactoring.
This commit is contained in:
parent
5f82c0d8c2
commit
eb5469088c
@ -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++;
|
||||
|
||||
@ -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();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user