diff --git a/src/main.cpp b/src/main.cpp index 82d52913a..2781c6f3f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2334,6 +2334,73 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return true; } +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) +{ + uint256 hash = block.GetHash(); + if (hash == Params().HashGenesisBlock()) + return true; + + assert(pindexPrev); + + int nHeight = pindexPrev->nHeight+1; + + // Check proof of work + if ((!Params().SkipProofOfWorkCheck()) && + (block.nBits != GetNextWorkRequired(pindexPrev, &block))) + return state.DoS(100, error("%s : incorrect proof of work", __func__), + REJECT_INVALID, "bad-diffbits"); + + // Check timestamp against prev + if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) + return state.Invalid(error("%s : block's timestamp is too early", __func__), + REJECT_INVALID, "time-too-old"); + + // Check that the block chain matches the known block chain up to a checkpoint + if (!Checkpoints::CheckBlock(nHeight, hash)) + return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight), + REJECT_CHECKPOINT, "checkpoint mismatch"); + + // Don't accept any forks from the main chain prior to last checkpoint + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) + return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight)); + + // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: + if (block.nVersion < 2 && + CBlockIndex::IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority())) + { + return state.Invalid(error("%s : rejected nVersion=1 block", __func__), + REJECT_OBSOLETE, "bad-version"); + } + + return true; +} + +bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex * const pindexPrev) +{ + const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1; + + // Check that all transactions are finalized + BOOST_FOREACH(const CTransaction& tx, block.vtx) + if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) { + return state.DoS(10, error("%s : contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal"); + } + + // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height + // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): + if (block.nVersion >= 2 && + CBlockIndex::IsSuperMajority(2, pindexPrev, Params().EnforceBlockUpgradeMajority())) + { + CScript expect = CScript() << nHeight; + if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || + !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 true; +} + bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex) { AssertLockHeld(cs_main); @@ -2353,44 +2420,16 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc // Get prev block index CBlockIndex* pindexPrev = NULL; - int nHeight = 0; if (hash != Params().HashGenesisBlock()) { BlockMap::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) return state.DoS(10, error("%s : prev block not found", __func__), 0, "bad-prevblk"); pindexPrev = (*mi).second; - nHeight = pindexPrev->nHeight+1; - - // Check proof of work - if ((!Params().SkipProofOfWorkCheck()) && - (block.nBits != GetNextWorkRequired(pindexPrev, &block))) - return state.DoS(100, error("%s : incorrect proof of work", __func__), - REJECT_INVALID, "bad-diffbits"); - - // Check timestamp against prev - if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) - return state.Invalid(error("%s : block's timestamp is too early", __func__), - REJECT_INVALID, "time-too-old"); - - // Check that the block chain matches the known block chain up to a checkpoint - if (!Checkpoints::CheckBlock(nHeight, hash)) - return state.DoS(100, error("%s : rejected by checkpoint lock-in at %d", __func__, nHeight), - REJECT_CHECKPOINT, "checkpoint mismatch"); - - // Don't accept any forks from the main chain prior to last checkpoint - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(100, error("%s : forked chain older than last checkpoint (height %d)", __func__, nHeight)); - - // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (block.nVersion < 2 && - CBlockIndex::IsSuperMajority(2, pindexPrev, Params().RejectBlockOutdatedMajority())) - { - return state.Invalid(error("%s : rejected nVersion=1 block", __func__), - REJECT_OBSOLETE, "bad-version"); - } } + if (!ContextualCheckBlockHeader(block, state, pindexPrev)) + return false; + if (pindex == NULL) pindex = AddToBlockIndex(block); @@ -2415,7 +2454,7 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, return true; } - if (!CheckBlock(block, state)) { + if ((!CheckBlock(block, state)) || !ContextualCheckBlock(block, state, pindex->pprev)) { if (state.IsInvalid() && !state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; } @@ -2424,27 +2463,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, int nHeight = pindex->nHeight; - // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, block.vtx) - if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) { - pindex->nStatus |= BLOCK_FAILED_VALID; - return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"), - REJECT_INVALID, "bad-txns-nonfinal"); - } - - // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - // if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet): - if (block.nVersion >= 2 && - CBlockIndex::IsSuperMajority(2, pindex->pprev, Params().EnforceBlockUpgradeMajority())) - { - CScript expect = CScript() << nHeight; - if (block.vtx[0].vin[0].scriptSig.size() < expect.size() || - !std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) { - pindex->nStatus |= BLOCK_FAILED_VALID; - return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase"), REJECT_INVALID, "bad-cb-height"); - } - } - // Write block to history file try { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); diff --git a/src/main.h b/src/main.h index 1bb091981..6e684be8c 100644 --- a/src/main.h +++ b/src/main.h @@ -463,6 +463,10 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true); bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true); +// Context-dependent validity checks +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex *pindexPrev); +bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIndex *pindexPrev); + // Store block on disk // if dbp is provided, the file is known to already reside on disk bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, CDiskBlockPos* dbp = NULL);