|
|
@ -818,12 +818,13 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C |
|
|
|
bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee, |
|
|
|
bool* pfMissingInputs, bool fOverrideMempoolLimit, bool fRejectAbsurdFee, |
|
|
|
std::vector<uint256>& vHashTxnToUncache) |
|
|
|
std::vector<uint256>& vHashTxnToUncache) |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
const uint256 hash = tx.GetHash(); |
|
|
|
AssertLockHeld(cs_main); |
|
|
|
AssertLockHeld(cs_main); |
|
|
|
if (pfMissingInputs) |
|
|
|
if (pfMissingInputs) |
|
|
|
*pfMissingInputs = false; |
|
|
|
*pfMissingInputs = false; |
|
|
|
|
|
|
|
|
|
|
|
if (!CheckTransaction(tx, state)) |
|
|
|
if (!CheckTransaction(tx, state)) |
|
|
|
return false; |
|
|
|
return error("%s: CheckTransaction: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); |
|
|
|
|
|
|
|
|
|
|
|
// Coinbase is only valid in a block, not as a loose transaction
|
|
|
|
// Coinbase is only valid in a block, not as a loose transaction
|
|
|
|
if (tx.IsCoinBase()) |
|
|
|
if (tx.IsCoinBase()) |
|
|
@ -841,7 +842,6 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C |
|
|
|
return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); |
|
|
|
return state.DoS(0, false, REJECT_NONSTANDARD, "non-final"); |
|
|
|
|
|
|
|
|
|
|
|
// is it already in the memory pool?
|
|
|
|
// is it already in the memory pool?
|
|
|
|
uint256 hash = tx.GetHash(); |
|
|
|
|
|
|
|
if (pool.exists(hash)) |
|
|
|
if (pool.exists(hash)) |
|
|
|
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); |
|
|
|
return state.Invalid(false, REJECT_ALREADY_KNOWN, "txn-already-in-mempool"); |
|
|
|
|
|
|
|
|
|
|
@ -1177,7 +1177,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C |
|
|
|
// Check against previous transactions
|
|
|
|
// Check against previous transactions
|
|
|
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
|
|
|
// This is done last to help prevent CPU exhaustion denial-of-service attacks.
|
|
|
|
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) |
|
|
|
if (!CheckInputs(tx, state, view, true, STANDARD_SCRIPT_VERIFY_FLAGS, true)) |
|
|
|
return false; |
|
|
|
return error("%s: CheckInputs: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); |
|
|
|
|
|
|
|
|
|
|
|
// Check again against just the consensus-critical mandatory script
|
|
|
|
// Check again against just the consensus-critical mandatory script
|
|
|
|
// verification flags, in case of bugs in the standard flags that cause
|
|
|
|
// verification flags, in case of bugs in the standard flags that cause
|
|
|
@ -1966,7 +1966,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin |
|
|
|
|
|
|
|
|
|
|
|
// Check it again in case a previous version let a bad block in
|
|
|
|
// Check it again in case a previous version let a bad block in
|
|
|
|
if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) |
|
|
|
if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) |
|
|
|
return false; |
|
|
|
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); |
|
|
|
|
|
|
|
|
|
|
|
// verify that the view's current state corresponds to the previous block
|
|
|
|
// verify that the view's current state corresponds to the previous block
|
|
|
|
uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); |
|
|
|
uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); |
|
|
@ -2911,13 +2911,11 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f |
|
|
|
{ |
|
|
|
{ |
|
|
|
// Check proof of work matches claimed amount
|
|
|
|
// Check proof of work matches claimed amount
|
|
|
|
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) |
|
|
|
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits, Params().GetConsensus())) |
|
|
|
return state.DoS(50, error("CheckBlockHeader(): proof of work failed"), |
|
|
|
return state.DoS(50, false, REJECT_INVALID, "high-hash", false, "proof of work failed"); |
|
|
|
REJECT_INVALID, "high-hash"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check timestamp
|
|
|
|
// Check timestamp
|
|
|
|
if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) |
|
|
|
if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) |
|
|
|
return state.Invalid(error("CheckBlockHeader(): block timestamp too far in the future"), |
|
|
|
return state.Invalid(false, REJECT_INVALID, "time-too-new", "block timestamp too far in the future"); |
|
|
|
REJECT_INVALID, "time-too-new"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
@ -2939,15 +2937,13 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo |
|
|
|
bool mutated; |
|
|
|
bool mutated; |
|
|
|
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); |
|
|
|
uint256 hashMerkleRoot2 = BlockMerkleRoot(block, &mutated); |
|
|
|
if (block.hashMerkleRoot != hashMerkleRoot2) |
|
|
|
if (block.hashMerkleRoot != hashMerkleRoot2) |
|
|
|
return state.DoS(100, error("CheckBlock(): hashMerkleRoot mismatch"), |
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-txnmrklroot", true, "hashMerkleRoot mismatch"); |
|
|
|
REJECT_INVALID, "bad-txnmrklroot", true); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check for merkle tree malleability (CVE-2012-2459): repeating sequences
|
|
|
|
// Check for merkle tree malleability (CVE-2012-2459): repeating sequences
|
|
|
|
// of transactions in a block without affecting the merkle root of a block,
|
|
|
|
// of transactions in a block without affecting the merkle root of a block,
|
|
|
|
// while still invalidating it.
|
|
|
|
// while still invalidating it.
|
|
|
|
if (mutated) |
|
|
|
if (mutated) |
|
|
|
return state.DoS(100, error("CheckBlock(): duplicate transaction"), |
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-txns-duplicate", true, "duplicate transaction"); |
|
|
|
REJECT_INVALID, "bad-txns-duplicate", true); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// All potential-corruption validation must be done before we do any
|
|
|
|
// All potential-corruption validation must be done before we do any
|
|
|
@ -2956,24 +2952,20 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo |
|
|
|
|
|
|
|
|
|
|
|
// Size limits
|
|
|
|
// Size limits
|
|
|
|
if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) |
|
|
|
if (block.vtx.empty() || block.vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(block, SER_NETWORK, PROTOCOL_VERSION) > MAX_BLOCK_SIZE) |
|
|
|
return state.DoS(100, error("CheckBlock(): size limits failed"), |
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-blk-length", false, "size limits failed"); |
|
|
|
REJECT_INVALID, "bad-blk-length"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// First transaction must be coinbase, the rest must not be
|
|
|
|
// First transaction must be coinbase, the rest must not be
|
|
|
|
if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) |
|
|
|
if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) |
|
|
|
return state.DoS(100, error("CheckBlock(): first tx is not coinbase"), |
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-cb-missing", false, "first tx is not coinbase"); |
|
|
|
REJECT_INVALID, "bad-cb-missing"); |
|
|
|
|
|
|
|
for (unsigned int i = 1; i < block.vtx.size(); i++) |
|
|
|
for (unsigned int i = 1; i < block.vtx.size(); i++) |
|
|
|
if (block.vtx[i].IsCoinBase()) |
|
|
|
if (block.vtx[i].IsCoinBase()) |
|
|
|
return state.DoS(100, error("CheckBlock(): more than one coinbase"), |
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-cb-multiple", false, "more than one coinbase"); |
|
|
|
REJECT_INVALID, "bad-cb-multiple"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check transactions
|
|
|
|
// Check transactions
|
|
|
|
BOOST_FOREACH(const CTransaction& tx, block.vtx) |
|
|
|
BOOST_FOREACH(const CTransaction& tx, block.vtx) |
|
|
|
if (!CheckTransaction(tx, state)) |
|
|
|
if (!CheckTransaction(tx, state)) |
|
|
|
return error("CheckBlock(): CheckTransaction of %s failed with %s", |
|
|
|
return state.Invalid(false, state.GetRejectCode(), state.GetRejectReason(), |
|
|
|
tx.GetHash().ToString(), |
|
|
|
strprintf("Transaction check failed (tx hash %s) %s", tx.GetHash().ToString(), state.GetDebugMessage())); |
|
|
|
FormatStateMessage(state)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int nSigOps = 0; |
|
|
|
unsigned int nSigOps = 0; |
|
|
|
BOOST_FOREACH(const CTransaction& tx, block.vtx) |
|
|
|
BOOST_FOREACH(const CTransaction& tx, block.vtx) |
|
|
@ -2981,8 +2973,7 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo |
|
|
|
nSigOps += GetLegacySigOpCount(tx); |
|
|
|
nSigOps += GetLegacySigOpCount(tx); |
|
|
|
} |
|
|
|
} |
|
|
|
if (nSigOps > MAX_BLOCK_SIGOPS) |
|
|
|
if (nSigOps > MAX_BLOCK_SIGOPS) |
|
|
|
return state.DoS(100, error("CheckBlock(): out-of-bounds SigOpCount"), |
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-blk-sigops", false, "out-of-bounds SigOpCount"); |
|
|
|
REJECT_INVALID, "bad-blk-sigops"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (fCheckPOW && fCheckMerkleRoot) |
|
|
|
if (fCheckPOW && fCheckMerkleRoot) |
|
|
|
block.fChecked = true; |
|
|
|
block.fChecked = true; |
|
|
@ -3009,28 +3000,17 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta |
|
|
|
const Consensus::Params& consensusParams = Params().GetConsensus(); |
|
|
|
const Consensus::Params& consensusParams = Params().GetConsensus(); |
|
|
|
// Check proof of work
|
|
|
|
// Check proof of work
|
|
|
|
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) |
|
|
|
if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) |
|
|
|
return state.DoS(100, error("%s: incorrect proof of work", __func__), |
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-diffbits", false, "incorrect proof of work"); |
|
|
|
REJECT_INVALID, "bad-diffbits"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Check timestamp against prev
|
|
|
|
// Check timestamp against prev
|
|
|
|
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) |
|
|
|
if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) |
|
|
|
return state.Invalid(error("%s: block's timestamp is too early", __func__), |
|
|
|
return state.Invalid(false, REJECT_INVALID, "time-too-old", "block's timestamp is too early"); |
|
|
|
REJECT_INVALID, "time-too-old"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded:
|
|
|
|
|
|
|
|
if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) |
|
|
|
|
|
|
|
return state.Invalid(error("%s: rejected nVersion=1 block", __func__), |
|
|
|
|
|
|
|
REJECT_OBSOLETE, "bad-version"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reject block.nVersion=2 blocks when 95% (75% on testnet) of the network has upgraded:
|
|
|
|
// Reject outdated version blocks when 95% (75% on testnet) of the network has upgraded:
|
|
|
|
if (block.nVersion < 3 && IsSuperMajority(3, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) |
|
|
|
for (int32_t version = 2; version < 5; ++version) // check for version 2, 3 and 4 upgrades
|
|
|
|
return state.Invalid(error("%s: rejected nVersion=2 block", __func__), |
|
|
|
if (block.nVersion < version && IsSuperMajority(version, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) |
|
|
|
REJECT_OBSOLETE, "bad-version"); |
|
|
|
return state.Invalid(false, REJECT_OBSOLETE, strprintf("bad-version(v%d)", version - 1), |
|
|
|
|
|
|
|
strprintf("rejected nVersion=%d block", version - 1)); |
|
|
|
// Reject block.nVersion=3 blocks when 95% (75% on testnet) of the network has upgraded:
|
|
|
|
|
|
|
|
if (block.nVersion < 4 && IsSuperMajority(4, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) |
|
|
|
|
|
|
|
return state.Invalid(error("%s : rejected nVersion=3 block", __func__), |
|
|
|
|
|
|
|
REJECT_OBSOLETE, "bad-version"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
@ -3047,7 +3027,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn |
|
|
|
? pindexPrev->GetMedianTimePast() |
|
|
|
? pindexPrev->GetMedianTimePast() |
|
|
|
: block.GetBlockTime(); |
|
|
|
: block.GetBlockTime(); |
|
|
|
if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { |
|
|
|
if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) { |
|
|
|
return state.DoS(10, error("%s: contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); |
|
|
|
return state.DoS(10, false, REJECT_INVALID, "bad-txns-nonfinal", false, "non-final transaction"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -3058,7 +3038,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn |
|
|
|
CScript expect = CScript() << nHeight; |
|
|
|
CScript expect = CScript() << nHeight; |
|
|
|
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || |
|
|
|
if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || |
|
|
|
!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { |
|
|
|
!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { |
|
|
|
return state.DoS(100, error("%s: block height mismatch in coinbase", __func__), REJECT_INVALID, "bad-cb-height"); |
|
|
|
return state.DoS(100, false, REJECT_INVALID, "bad-cb-height", false, "block height mismatch in coinbase"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -3085,7 +3065,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!CheckBlockHeader(block, state)) |
|
|
|
if (!CheckBlockHeader(block, state)) |
|
|
|
return false; |
|
|
|
return error("%s: Consensus::CheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); |
|
|
|
|
|
|
|
|
|
|
|
// Get prev block index
|
|
|
|
// Get prev block index
|
|
|
|
CBlockIndex* pindexPrev = NULL; |
|
|
|
CBlockIndex* pindexPrev = NULL; |
|
|
@ -3101,7 +3081,7 @@ static bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state |
|
|
|
return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); |
|
|
|
return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); |
|
|
|
|
|
|
|
|
|
|
|
if (!ContextualCheckBlockHeader(block, state, pindexPrev)) |
|
|
|
if (!ContextualCheckBlockHeader(block, state, pindexPrev)) |
|
|
|
return false; |
|
|
|
return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); |
|
|
|
} |
|
|
|
} |
|
|
|
if (pindex == NULL) |
|
|
|
if (pindex == NULL) |
|
|
|
pindex = AddToBlockIndex(block); |
|
|
|
pindex = AddToBlockIndex(block); |
|
|
@ -3148,7 +3128,7 @@ static bool AcceptBlock(const CBlock& block, CValidationState& state, const CCha |
|
|
|
pindex->nStatus |= BLOCK_FAILED_VALID; |
|
|
|
pindex->nStatus |= BLOCK_FAILED_VALID; |
|
|
|
setDirtyBlockIndex.insert(pindex); |
|
|
|
setDirtyBlockIndex.insert(pindex); |
|
|
|
} |
|
|
|
} |
|
|
|
return false; |
|
|
|
return error("%s: %s", __func__, FormatStateMessage(state)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int nHeight = pindex->nHeight; |
|
|
|
int nHeight = pindex->nHeight; |
|
|
@ -3199,7 +3179,7 @@ bool ProcessNewBlock(CValidationState& state, const CChainParams& chainparams, c |
|
|
|
bool fRequested = MarkBlockAsReceived(pblock->GetHash()); |
|
|
|
bool fRequested = MarkBlockAsReceived(pblock->GetHash()); |
|
|
|
fRequested |= fForceProcessing; |
|
|
|
fRequested |= fForceProcessing; |
|
|
|
if (!checked) { |
|
|
|
if (!checked) { |
|
|
|
return error("%s: CheckBlock FAILED", __func__); |
|
|
|
return error("%s: CheckBlock FAILED %s", __func__, FormatStateMessage(state)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Store to disk
|
|
|
|
// Store to disk
|
|
|
@ -3233,11 +3213,11 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, |
|
|
|
|
|
|
|
|
|
|
|
// NOTE: CheckBlockHeader is called by CheckBlock
|
|
|
|
// NOTE: CheckBlockHeader is called by CheckBlock
|
|
|
|
if (!ContextualCheckBlockHeader(block, state, pindexPrev)) |
|
|
|
if (!ContextualCheckBlockHeader(block, state, pindexPrev)) |
|
|
|
return false; |
|
|
|
return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state)); |
|
|
|
if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot)) |
|
|
|
if (!CheckBlock(block, state, fCheckPOW, fCheckMerkleRoot)) |
|
|
|
return false; |
|
|
|
return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); |
|
|
|
if (!ContextualCheckBlock(block, state, pindexPrev)) |
|
|
|
if (!ContextualCheckBlock(block, state, pindexPrev)) |
|
|
|
return false; |
|
|
|
return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); |
|
|
|
if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) |
|
|
|
if (!ConnectBlock(block, state, &indexDummy, viewNew, true)) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
assert(state.IsValid()); |
|
|
|
assert(state.IsValid()); |
|
|
@ -3567,7 +3547,8 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, |
|
|
|
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); |
|
|
|
return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); |
|
|
|
// check level 1: verify block validity
|
|
|
|
// check level 1: verify block validity
|
|
|
|
if (nCheckLevel >= 1 && !CheckBlock(block, state)) |
|
|
|
if (nCheckLevel >= 1 && !CheckBlock(block, state)) |
|
|
|
return error("VerifyDB(): *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString()); |
|
|
|
return error("%s: *** found bad block at %d, hash=%s (%s)\n", __func__, |
|
|
|
|
|
|
|
pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); |
|
|
|
// check level 2: verify undo validity
|
|
|
|
// check level 2: verify undo validity
|
|
|
|
if (nCheckLevel >= 2 && pindex) { |
|
|
|
if (nCheckLevel >= 2 && pindex) { |
|
|
|
CBlockUndo undo; |
|
|
|
CBlockUndo undo; |
|
|
|