Skip to content

Commit e440ac7

Browse files
committed
Introduce assumevalid setting to skip presumed valid scripts.
This disentangles the script validation skipping from checkpoints. A new option is introduced "assumevalid" which specifies a block whos ancestors we assume all have valid scriptsigs and so we do not check them when they are also burried under the best header by two weeks worth of work. Unlike checkpoints this has no influence on consensus unless you set it to a block with an invalid history. Because of this it can be easily be updated without risk of influencing the network consensus. This results in a massive IBD speedup. This approach was independently recommended by Peter Todd and Luke-Jr since POW based signature skipping (see PR#9180) does not have the verifiable properties of a specific hash and may create bad incentives. The downside is that, like checkpoints, the defaults bitrot and older releases will sync slower. On the plus side users can provide their own value here, and if they set it to something crazy all that will happen is more time will be spend validating signatures. Checkblocks and checklevel are also moved to the hidden debug options: Especially now that checkblocks has a low default there is little need to change these settings, and users frequently misunderstand them as influencing security or IBD speed. By hiding them we offset the space added by this new option.
1 parent 02e5308 commit e440ac7

File tree

7 files changed

+75
-12
lines changed

7 files changed

+75
-12
lines changed

doc/release-notes.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ Low-level RPC changes
4949
than two arguments.
5050

5151
Removal of Priority Estimation
52-
------------------------------
52+
-------------------------------
5353

5454
- Estimation of "priority" needed for a transaction to be included within a target
5555
number of blocks has been removed. The rpc calls are deprecated and will either
@@ -71,6 +71,27 @@ P2P connection management
7171

7272
- New connections to manually added peers are much faster.
7373

74+
Introduction of assumed-valid blocks
75+
-------------------------------------
76+
77+
- A significant portion of the initial block download time is spent verifying
78+
scripts/signatures. Although the verification must pass to ensure the security
79+
of the system, no other result from this verification is needed: If the node
80+
knew the history of a given block were valid it could skip checking scripts
81+
for its ancestors.
82+
83+
- A new configuration option 'assumevalid' is provided to express this knowledge
84+
to the software. Unlike the 'checkpoints' in the past this setting does not
85+
force the use of a particular chain: chains that are consistent with it are
86+
processed quicker, but other chains are still accepted if they'd otherwise
87+
be chosen as best. Also unlike 'checkpoints' the user can configure which
88+
block history is assumed true, this means that even outdated software can
89+
sync more quickly if the setting is updated by the user.
90+
91+
- Because the validity of a chain history is a simple objective fact it is much
92+
easier to review this setting. As a result the software ships with a default
93+
value adjusted to match the current chain shortly before release. The use
94+
of this default value can be disabled by setting -assumevalid=0
7495

7596
0.14.0 Change log
7697
=================

doc/release-process.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ Before every minor and major release:
1313
* Update version in sources (see below)
1414
* Write release notes (see below)
1515
* Update `src/chainparams.cpp` nMinimumChainWork with information from the getblockchaininfo rpc.
16+
* Update `src/chainparams.cpp` defaultAssumeValid with information from the getblockhash rpc.
17+
- The selected value must not be orphaned so it may be useful to set the value two blocks back from the tip.
18+
- Testnet should be set some tens of thousands back from the tip due to reorgs there.
19+
- This update should be reviewed with a reindex-chainstate with assumevalid=0 to catch any defect
20+
that causes rejection of blocks in the past history.
1621

1722
Before every major release:
1823

src/chainparams.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ class CMainParams : public CChainParams {
9999
// The best chain should have at least this much work.
100100
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000002cb971dd56d1c583c20f90");
101101

102+
// By default assume that the signatures in ancestors of this block are valid.
103+
consensus.defaultAssumeValid = uint256S("0x0000000000000000030abc968e1bd635736e880b946085c93152969b9a81a6e2"); //447235
104+
102105
/**
103106
* The message start string is designed to be unlikely to occur in normal data.
104107
* The characters are rarely used upper ASCII, not valid as UTF-8, and produce
@@ -201,6 +204,9 @@ class CTestNetParams : public CChainParams {
201204
// The best chain should have at least this much work.
202205
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000000198b4def2baa9338d6");
203206

207+
// By default assume that the signatures in ancestors of this block are valid.
208+
consensus.defaultAssumeValid = uint256S("0x000000000871ee6842d3648317ccc8a435eb8cc3c2429aee94faff9ba26b05a0"); //1043841
209+
204210
pchMessageStart[0] = 0x0b;
205211
pchMessageStart[1] = 0x11;
206212
pchMessageStart[2] = 0x09;
@@ -283,6 +289,9 @@ class CRegTestParams : public CChainParams {
283289
// The best chain should have at least this much work.
284290
consensus.nMinimumChainWork = uint256S("0x00");
285291

292+
// By default assume that the signatures in ancestors of this block are valid.
293+
consensus.defaultAssumeValid = uint256S("0x00");
294+
286295
pchMessageStart[0] = 0xfa;
287296
pchMessageStart[1] = 0xbf;
288297
pchMessageStart[2] = 0xb5;

src/consensus/params.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ struct Params {
6262
int64_t nPowTargetTimespan;
6363
int64_t DifficultyAdjustmentInterval() const { return nPowTargetTimespan / nPowTargetSpacing; }
6464
uint256 nMinimumChainWork;
65+
uint256 defaultAssumeValid;
6566
};
6667
} // namespace Consensus
6768

src/init.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,7 @@ std::string HelpMessage(HelpMessageMode mode)
329329
strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)"));
330330
if (showDebug)
331331
strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY));
332-
strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS));
333-
strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL));
332+
strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), Params(CBaseChainParams::MAIN).GetConsensus().defaultAssumeValid.GetHex(), Params(CBaseChainParams::TESTNET).GetConsensus().defaultAssumeValid.GetHex()));
334333
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
335334
if (mode == HMM_BITCOIND)
336335
{
@@ -420,6 +419,8 @@ std::string HelpMessage(HelpMessageMode mode)
420419
strUsage += HelpMessageOpt("-uacomment=<cmt>", _("Append comment to the user agent string"));
421420
if (showDebug)
422421
{
422+
strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS));
423+
strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL));
423424
strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. Also sets -checkmempool (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks()));
424425
strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", Params(CBaseChainParams::MAIN).DefaultConsistencyChecks()));
425426
strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED));
@@ -920,6 +921,12 @@ bool AppInitParameterInteraction()
920921
fCheckBlockIndex = GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks());
921922
fCheckpointsEnabled = GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED);
922923

924+
hashAssumeValid = uint256S(GetArg("-assumevalid", chainparams.GetConsensus().defaultAssumeValid.GetHex()));
925+
if (!hashAssumeValid.IsNull())
926+
LogPrintf("Assuming ancestors of block %s have valid signatures.\n", hashAssumeValid.GetHex());
927+
else
928+
LogPrintf("Validating signatures for all blocks.\n");
929+
923930
// mempool limits
924931
int64_t nMempoolSizeMax = GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000;
925932
int64_t nMempoolSizeMin = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40;

src/validation.cpp

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ uint64_t nPruneTarget = 0;
7878
int64_t nMaxTipAge = DEFAULT_MAX_TIP_AGE;
7979
bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT;
8080

81+
uint256 hashAssumeValid;
8182

8283
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
8384
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
@@ -1389,11 +1390,10 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi
13891390
// Only if ALL inputs pass do we perform expensive ECDSA signature checks.
13901391
// Helps prevent CPU exhaustion attacks.
13911392

1392-
// Skip ECDSA signature verification when connecting blocks before the
1393-
// last block chain checkpoint. Assuming the checkpoints are valid this
1393+
// Skip script verification when connecting blocks under the
1394+
// assumedvalid block. Assuming the assumedvalid block is valid this
13941395
// is safe because block merkle hashes are still computed and checked,
1395-
// and any change will be caught at the next checkpoint. Of course, if
1396-
// the checkpoint is for a chain that's invalid due to false scriptSigs
1396+
// Of course, if an assumed valid block is invalid due to false scriptSigs
13971397
// this optimization would allow an invalid chain to be accepted.
13981398
if (fScriptChecks) {
13991399
for (unsigned int i = 0; i < tx.vin.size(); i++) {
@@ -1721,11 +1721,28 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
17211721
}
17221722

17231723
bool fScriptChecks = true;
1724-
if (fCheckpointsEnabled) {
1725-
CBlockIndex *pindexLastCheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints());
1726-
if (pindexLastCheckpoint && pindexLastCheckpoint->GetAncestor(pindex->nHeight) == pindex) {
1727-
// This block is an ancestor of a checkpoint: disable script checks
1728-
fScriptChecks = false;
1724+
if (!hashAssumeValid.IsNull()) {
1725+
// We've been configured with the hash of a block which has been externally verified to have a valid history.
1726+
// A suitable default value is included with the software and updated from time to time. Because validity
1727+
// relative to a piece of software is an objective fact these defaults can be easily reviewed.
1728+
// This setting doesn't force the selection of any particular chain but makes validating some faster by
1729+
// effectively caching the result of part of the verification.
1730+
BlockMap::const_iterator it = mapBlockIndex.find(hashAssumeValid);
1731+
if (it != mapBlockIndex.end()) {
1732+
if (it->second->GetAncestor(pindex->nHeight) == pindex &&
1733+
pindexBestHeader->GetAncestor(pindex->nHeight) == pindex &&
1734+
pindexBestHeader->nChainWork >= UintToArith256(chainparams.GetConsensus().nMinimumChainWork)) {
1735+
// This block is a member of the assumed verified chain and an ancestor of the best header.
1736+
// The equivalent time check discourages hashpower from extorting the network via DOS attack
1737+
// into accepting an invalid block through telling users they must manually set assumevalid.
1738+
// Requiring a software change or burying the invalid block, regardless of the setting, makes
1739+
// it hard to hide the implication of the demand. This also avoids having release candidates
1740+
// that are hardly doing any signature verification at all in testing without having to
1741+
// artificially set the default assumed verified block further back.
1742+
// The test against nMinimumChainWork prevents the skipping when denied access to any chain at
1743+
// least as good as the expected chain.
1744+
fScriptChecks = (GetBlockProofEquivalentTime(*pindexBestHeader, *pindex, *pindexBestHeader, chainparams.GetConsensus()) <= 60 * 60 * 24 * 7 * 2);
1745+
}
17291746
}
17301747
}
17311748

src/validation.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ extern CAmount maxTxFee;
186186
extern int64_t nMaxTipAge;
187187
extern bool fEnableReplacement;
188188

189+
/** Block hash whose ancestors we will assume to have valid scripts without checking them. */
190+
extern uint256 hashAssumeValid;
191+
189192
/** Best header we've seen so far (used for getheaders queries' starting points). */
190193
extern CBlockIndex *pindexBestHeader;
191194

0 commit comments

Comments
 (0)