Changeset View
Standalone View
src/validation.cpp
// Copyright (c) 2009-2010 Satoshi Nakamoto | // Copyright (c) 2009-2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2016 The Bitcoin Core developers | // Copyright (c) 2009-2016 The Bitcoin Core developers | ||||
// Copyright (c) 2017 The Bitcoin developers | // Copyright (c) 2017-2018 The Bitcoin developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include "validation.h" | #include "validation.h" | ||||
#include "arith_uint256.h" | #include "arith_uint256.h" | ||||
#include "chainparams.h" | #include "chainparams.h" | ||||
#include "checkpoints.h" | #include "checkpoints.h" | ||||
▲ Show 20 Lines • Show All 178 Lines • ▼ Show 20 Lines | |||||
static bool FlushStateToDisk(const CChainParams &chainParams, | static bool FlushStateToDisk(const CChainParams &chainParams, | ||||
CValidationState &state, FlushStateMode mode, | CValidationState &state, FlushStateMode mode, | ||||
int nManualPruneHeight = 0); | int nManualPruneHeight = 0); | ||||
static void FindFilesToPruneManual(std::set<int> &setFilesToPrune, | static void FindFilesToPruneManual(std::set<int> &setFilesToPrune, | ||||
int nManualPruneHeight); | int nManualPruneHeight); | ||||
static void FindFilesToPrune(std::set<int> &setFilesToPrune, | static void FindFilesToPrune(std::set<int> &setFilesToPrune, | ||||
uint64_t nPruneAfterHeight); | uint64_t nPruneAfterHeight); | ||||
static FILE *OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); | static FILE *OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly = false); | ||||
static uint32_t GetBlockScriptFlags(const CBlockIndex *pindex, | uint32_t GetMempoolScriptFlags(const Config &config, const CBlockIndex *pindex); | ||||
const Config &config); | |||||
static bool IsFinalTx(const CTransaction &tx, int nBlockHeight, | static bool IsFinalTx(const CTransaction &tx, int nBlockHeight, | ||||
int64_t nBlockTime) { | int64_t nBlockTime) { | ||||
if (tx.nLockTime == 0) { | if (tx.nLockTime == 0) { | ||||
return true; | return true; | ||||
} | } | ||||
int64_t lockTime = tx.nLockTime; | int64_t lockTime = tx.nLockTime; | ||||
▲ Show 20 Lines • Show All 749 Lines • ▼ Show 20 Lines | // Check for conflicts with in-memory transactions | ||||
std::string errString; | std::string errString; | ||||
if (!pool.CalculateMemPoolAncestors( | if (!pool.CalculateMemPoolAncestors( | ||||
entry, setAncestors, nLimitAncestors, nLimitAncestorSize, | entry, setAncestors, nLimitAncestors, nLimitAncestorSize, | ||||
nLimitDescendants, nLimitDescendantSize, errString)) { | nLimitDescendants, nLimitDescendantSize, errString)) { | ||||
return state.DoS(0, false, REJECT_NONSTANDARD, | return state.DoS(0, false, REJECT_NONSTANDARD, | ||||
"too-long-mempool-chain", false, errString); | "too-long-mempool-chain", false, errString); | ||||
} | } | ||||
uint32_t currentBlockScriptVerifyFlags = | |||||
deadalnix: there is no reason that this would use a different function than the one connecting blocks. | |||||
movrcxUnsubmitted Not Done Inline ActionsNot sure what I'm supposed to do here... 🤔 movrcx: Not sure what I'm supposed to do here... 🤔 | |||||
GetMempoolScriptFlags(config, chainActive.Tip()); | |||||
uint32_t scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; | uint32_t scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; | ||||
if (!config.GetChainParams().RequireStandard()) { | if (!config.GetChainParams().RequireStandard()) { | ||||
scriptVerifyFlags = | scriptVerifyFlags = | ||||
SCRIPT_ENABLE_SIGHASH_FORKID | | SCRIPT_ENABLE_SIGHASH_FORKID | | ||||
gArgs.GetArg("-promiscuousmempoolflags", scriptVerifyFlags); | gArgs.GetArg("-promiscuousmempoolflags", scriptVerifyFlags); | ||||
} | } | ||||
if ((currentBlockScriptVerifyFlags & SCRIPT_ENABLE_OPCODES_MONOLITH) != | |||||
0) { | |||||
scriptVerifyFlags |= SCRIPT_ENABLE_OPCODES_MONOLITH; | |||||
schancelUnsubmitted Not Done Inline ActionsCan we drop the branch and just use: scriptVerifyFlags |= currentBlockScriptVerifyFlags Would this change behavior? schancel: Can we drop the branch and just use:
`scriptVerifyFlags |= currentBlockScriptVerifyFlags`… | |||||
movrcxUnsubmitted Not Done Inline ActionsWorks fine. This change will be submitted in the next diff. user@debian:~/code/bitcoin-abc$ test/functional/test_runner.py monolith-opcodes.py Temporary test directory at /tmp/bitcoin_test_runner_20180328_185347 ........ monolith-opcodes.py passed, Duration: 4 s TEST | STATUS | DURATION monolith-opcodes.py | ✓ Passed | 4 s ALL | ✓ Passed | 4 s (accumulated) Runtime: 4 s user@debian:~/code/bitcoin-abc$ src/test/test_bitcoin Running 294 test cases... *** No errors detected user@debian:~/code/bitcoin-abc$ movrcx: Works fine. This change will be submitted in the next diff.
```
user@debian:~/code/bitcoin… | |||||
} | |||||
// Check against previous transactions. This is done last to help | // Check against previous transactions. This is done last to help | ||||
// prevent CPU exhaustion denial-of-service attacks. | // prevent CPU exhaustion denial-of-service attacks. | ||||
PrecomputedTransactionData txdata(tx); | PrecomputedTransactionData txdata(tx); | ||||
if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, | if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, | ||||
txdata)) { | txdata)) { | ||||
// State filled in by CheckInputs. | // State filled in by CheckInputs. | ||||
return false; | return false; | ||||
Show All 9 Lines | // Check for conflicts with in-memory transactions | ||||
// This is also useful in case of bugs in the standard flags that cause | // This is also useful in case of bugs in the standard flags that cause | ||||
// transactions to pass as valid when they're actually invalid. For | // transactions to pass as valid when they're actually invalid. For | ||||
// instance the STRICTENC flag was incorrectly allowing certain CHECKSIG | // instance the STRICTENC flag was incorrectly allowing certain CHECKSIG | ||||
// NOT scripts to pass, even though they were invalid. | // NOT scripts to pass, even though they were invalid. | ||||
// | // | ||||
// There is a similar check in CreateNewBlock() to prevent creating | // There is a similar check in CreateNewBlock() to prevent creating | ||||
// invalid blocks (using TestBlockValidity), however allowing such | // invalid blocks (using TestBlockValidity), however allowing such | ||||
// transactions into the mempool can be exploited as a DoS attack. | // transactions into the mempool can be exploited as a DoS attack. | ||||
uint32_t currentBlockScriptVerifyFlags = | |||||
GetBlockScriptFlags(chainActive.Tip(), config); | |||||
if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, | if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, | ||||
currentBlockScriptVerifyFlags, true, | currentBlockScriptVerifyFlags, true, | ||||
txdata)) { | txdata)) { | ||||
// If we're using promiscuousmempoolflags, we may hit this normally. | // If we're using promiscuousmempoolflags, we may hit this normally. | ||||
// Check if current block has some flags that scriptVerifyFlags does | // Check if current block has some flags that scriptVerifyFlags does | ||||
// not before printing an ominous warning. | // not before printing an ominous warning. | ||||
if (!(~scriptVerifyFlags & currentBlockScriptVerifyFlags)) { | if (!(~scriptVerifyFlags & currentBlockScriptVerifyFlags)) { | ||||
return error( | return error( | ||||
▲ Show 20 Lines • Show All 838 Lines • ▼ Show 20 Lines | bool Condition(const CBlockIndex *pindex, | ||||
((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0; | ((ComputeBlockVersion(pindex->pprev, params) >> bit) & 1) == 0; | ||||
} | } | ||||
}; | }; | ||||
// Protected by cs_main | // Protected by cs_main | ||||
static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; | static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; | ||||
// Returns the script flags which should be checked for a given block | // Returns the script flags which should be checked for a given block | ||||
static uint32_t GetBlockScriptFlags(const CBlockIndex *pindex, | static uint32_t GetScriptFlags(const Config &config, const CBlockIndex *pindex, | ||||
deadalnixAuthorUnsubmitted Not Done Inline ActionsThis is very much outside the scope of this diff. it is very risky, and overall not very much tested. deadalnix: This is very much outside the scope of this diff. it is very risky, and overall not very much… | |||||
const Config &config) { | int64_t blockTime) { | ||||
AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
const Consensus::Params &consensusparams = | const Consensus::Params &consensusparams = | ||||
config.GetChainParams().GetConsensus(); | config.GetChainParams().GetConsensus(); | ||||
// BIP16 didn't become active until Apr 1 2012 | // BIP16 didn't become active until Apr 1 2012 | ||||
int64_t nBIP16SwitchTime = 1333238400; | int64_t nBIP16SwitchTime = 1333238400; | ||||
bool fStrictPayToScriptHash = (pindex->GetBlockTime() >= nBIP16SwitchTime); | bool fStrictPayToScriptHash = (blockTime >= nBIP16SwitchTime); | ||||
uint32_t flags = | uint32_t flags = | ||||
fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; | fStrictPayToScriptHash ? SCRIPT_VERIFY_P2SH : SCRIPT_VERIFY_NONE; | ||||
// Start enforcing the DERSIG (BIP66) rule | // Start enforcing the DERSIG (BIP66) rule from the block before | ||||
if (pindex->nHeight >= consensusparams.BIP66Height) { | if (pindex->nHeight >= consensusparams.BIP66Height - 1) { | ||||
schancelUnsubmitted Not Done Inline ActionsI'm not sure about the -1's here. It should follow the same rule as mempool vs the other scriptflag fetching. Right? schancel: I'm not sure about the -1's here. It should follow the same rule as mempool vs the other… | |||||
flags |= SCRIPT_VERIFY_DERSIG; | flags |= SCRIPT_VERIFY_DERSIG; | ||||
} | } | ||||
// Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule | // Start enforcing CHECKLOCKTIMEVERIFY (BIP65) rule from the block before | ||||
if (pindex->nHeight >= consensusparams.BIP65Height) { | if (pindex->nHeight >= consensusparams.BIP65Height - 1) { | ||||
schancelUnsubmitted Not Done Inline ActionsDitto schancel: Ditto | |||||
flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; | flags |= SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY; | ||||
} | } | ||||
// Start enforcing BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic. | // Start enforcing BIP112 (CHECKSEQUENCEVERIFY) using versionbits logic. | ||||
if (VersionBitsState(pindex->pprev, consensusparams, | if (VersionBitsState(pindex, consensusparams, Consensus::DEPLOYMENT_CSV, | ||||
Consensus::DEPLOYMENT_CSV, | |||||
versionbitscache) == THRESHOLD_ACTIVE) { | versionbitscache) == THRESHOLD_ACTIVE) { | ||||
flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; | flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; | ||||
} | } | ||||
// If the UAHF is enabled, we start accepting replay protected txns | // If the UAHF is enabled, we start accepting replay protected txns | ||||
if (IsUAHFenabled(config, pindex->pprev)) { | if (IsUAHFenabled(config, pindex)) { | ||||
flags |= SCRIPT_VERIFY_STRICTENC; | flags |= SCRIPT_VERIFY_STRICTENC; | ||||
flags |= SCRIPT_ENABLE_SIGHASH_FORKID; | flags |= SCRIPT_ENABLE_SIGHASH_FORKID; | ||||
} | } | ||||
// If the DAA HF is enabled, we start rejecting transaction that use a high | // If the DAA HF is enabled, we start rejecting transaction that use a high | ||||
// s in their signature. We also make sure that signature that are supposed | // s in their signature. We also make sure that signature that are supposed | ||||
// to fail (for instance in multisig or other forms of smart contracts) are | // to fail (for instance in multisig or other forms of smart contracts) are | ||||
// null. | // null. | ||||
if (IsDAAEnabled(config, pindex->pprev)) { | if (IsDAAEnabled(config, pindex)) { | ||||
flags |= SCRIPT_VERIFY_LOW_S; | flags |= SCRIPT_VERIFY_LOW_S; | ||||
flags |= SCRIPT_VERIFY_NULLFAIL; | flags |= SCRIPT_VERIFY_NULLFAIL; | ||||
} | } | ||||
if (IsMonolithEnabled(config, pindex)) { | |||||
// When the May 15, 2018 HF is enabled, activate new opcodes. | |||||
flags |= SCRIPT_ENABLE_OPCODES_MONOLITH; | |||||
} | |||||
return flags; | return flags; | ||||
} | } | ||||
// Returns the script flags which should be checked for the mempool | |||||
uint32_t GetMempoolScriptFlags(const Config &config, | |||||
const CBlockIndex *pindex) { | |||||
return GetScriptFlags(config, pindex, pindex->GetBlockTime()); | |||||
schancelUnsubmitted Not Done Inline ActionsI don't understand why blocktine or height needs to vary differently than the block that was passed in. Can you provide the rationale? My understanding is:
If these are the rules, then nothing else should vary differently. This changes some behavior for old blocks, but the activation blocks should never have been able to be created with transactions that would fail such a rule above. So, we should try it and run an IBD. (It is okay to change the historic behavior as long as it won't invalidate the existing blocks) If this is true, the blocktime parameter should go away. schancel: I don't understand why blocktine or height needs to vary differently than the block that was… | |||||
danconnollyUnsubmitted Not Done Inline ActionsThere are several questions here about the fix to T288, but rather than address them directly, I think that we should split the fix to T288 out into its own diff, starting with a full set of unit tests for the (current) GetBlockScriptFlags() function, followed by the change to fix T288. The tests should include all "transition points" where the script flags will change and a test which demonstrates the bug. The problem with this approach is that it will take time. danconnolly: There are several questions here about the fix to T288, but rather than address them directly… | |||||
schancelUnsubmitted Not Done Inline ActionsThat sounds like a solid idea to me. schancel: That sounds like a solid idea to me. | |||||
} | |||||
// Returns the script flags which should be checked when adding a new block | |||||
uint32_t GetBlockScriptFlags(const Config &config, const CBlockIndex *pindex) { | |||||
return GetScriptFlags(config, pindex->pprev, pindex->GetBlockTime()); | |||||
} | |||||
static int64_t nTimeCheck = 0; | static int64_t nTimeCheck = 0; | ||||
static int64_t nTimeForks = 0; | static int64_t nTimeForks = 0; | ||||
static int64_t nTimeVerify = 0; | static int64_t nTimeVerify = 0; | ||||
static int64_t nTimeConnect = 0; | static int64_t nTimeConnect = 0; | ||||
static int64_t nTimeIndex = 0; | static int64_t nTimeIndex = 0; | ||||
static int64_t nTimeCallbacks = 0; | static int64_t nTimeCallbacks = 0; | ||||
static int64_t nTimeTotal = 0; | static int64_t nTimeTotal = 0; | ||||
▲ Show 20 Lines • Show All 132 Lines • ▼ Show 20 Lines | static bool ConnectBlock(const Config &config, const CBlock &block, | ||||
// Start enforcing BIP68 (sequence locks) using versionbits logic. | // Start enforcing BIP68 (sequence locks) using versionbits logic. | ||||
int nLockTimeFlags = 0; | int nLockTimeFlags = 0; | ||||
if (VersionBitsState(pindex->pprev, consensusParams, | if (VersionBitsState(pindex->pprev, consensusParams, | ||||
Consensus::DEPLOYMENT_CSV, | Consensus::DEPLOYMENT_CSV, | ||||
versionbitscache) == THRESHOLD_ACTIVE) { | versionbitscache) == THRESHOLD_ACTIVE) { | ||||
nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; | nLockTimeFlags |= LOCKTIME_VERIFY_SEQUENCE; | ||||
} | } | ||||
uint32_t flags = GetBlockScriptFlags(pindex, config); | uint32_t flags = GetBlockScriptFlags(config, pindex); | ||||
int64_t nTime2 = GetTimeMicros(); | int64_t nTime2 = GetTimeMicros(); | ||||
nTimeForks += nTime2 - nTime1; | nTimeForks += nTime2 - nTime1; | ||||
LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs]\n", | LogPrint(BCLog::BENCH, " - Fork checks: %.2fms [%.2fs]\n", | ||||
0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); | 0.001 * (nTime2 - nTime1), nTimeForks * 0.000001); | ||||
CBlockUndo blockundo; | CBlockUndo blockundo; | ||||
▲ Show 20 Lines • Show All 328 Lines • ▼ Show 20 Lines | |||||
void PruneAndFlush() { | void PruneAndFlush() { | ||||
CValidationState state; | CValidationState state; | ||||
fCheckForPruning = true; | fCheckForPruning = true; | ||||
const CChainParams &chainparams = Params(); | const CChainParams &chainparams = Params(); | ||||
FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE); | FlushStateToDisk(chainparams, state, FLUSH_STATE_NONE); | ||||
} | } | ||||
/** | /** | ||||
* Update chainActive and related internal data structures when adding a new block to the chain tip. | * Update chainActive and related internal data structures when adding a new | ||||
* block to the chain tip. | |||||
*/ | */ | ||||
static void UpdateTip(const Config &config, CBlockIndex *pindexNew) { | static void UpdateTip(const Config &config, CBlockIndex *pindexNew) { | ||||
const Consensus::Params &consensusParams = | const Consensus::Params &consensusParams = | ||||
config.GetChainParams().GetConsensus(); | config.GetChainParams().GetConsensus(); | ||||
chainActive.SetTip(pindexNew); | chainActive.SetTip(pindexNew); | ||||
// New best block | // New best block | ||||
▲ Show 20 Lines • Show All 114 Lines • ▼ Show 20 Lines | LogPrint(BCLog::BENCH, "- Disconnect block: %.2fms\n", | ||||
(GetTimeMicros() - nStart) * 0.001); | (GetTimeMicros() - nStart) * 0.001); | ||||
// Write the chain state to disk, if necessary. | // Write the chain state to disk, if necessary. | ||||
if (!FlushStateToDisk(config.GetChainParams(), state, | if (!FlushStateToDisk(config.GetChainParams(), state, | ||||
FLUSH_STATE_IF_NEEDED)) { | FLUSH_STATE_IF_NEEDED)) { | ||||
return false; | return false; | ||||
} | } | ||||
// If this block was activating the monolith opcodes, then we need to | |||||
// remove any transactions that use the monolith opcodes from the mempool. | |||||
// There is | |||||
// no easy way to do this so we'll just discard the whole mempool and then | |||||
// add the transaction of the block we just disconnected back. | |||||
if (IsMonolithEnabled(config, pindexDelete) && | |||||
!IsMonolithEnabled(config, pindexDelete->pprev)) { | |||||
mempool.clear(); | |||||
} | |||||
if (disconnectpool) { | if (disconnectpool) { | ||||
// Save transactions to re-add to mempool at end of reorg | // Save transactions to re-add to mempool at end of reorg | ||||
for (const auto &tx : boost::adaptors::reverse(block.vtx)) { | for (const auto &tx : boost::adaptors::reverse(block.vtx)) { | ||||
disconnectpool->addTransaction(tx); | disconnectpool->addTransaction(tx); | ||||
} | } | ||||
while (disconnectpool->DynamicMemoryUsage() > | while (disconnectpool->DynamicMemoryUsage() > | ||||
MAX_DISCONNECTED_TX_POOL_SIZE) { | MAX_DISCONNECTED_TX_POOL_SIZE) { | ||||
// Drop the earliest entry, and remove its children from the | // Drop the earliest entry, and remove its children from the | ||||
▲ Show 20 Lines • Show All 2,815 Lines • Show Last 20 Lines |
there is no reason that this would use a different function than the one connecting blocks.