Merge pull request #3884

942b33a Split AcceptBlockHeader from AcceptBlock. (Pieter Wuille)
f457347 Split up CheckBlock in a block and header version (Pieter Wuille)
This commit is contained in:
Wladimir J. van der Laan 2014-05-09 16:00:32 +02:00
commit d54985f3f1
No known key found for this signature in database
GPG key ID: 74810B012346C9A6
2 changed files with 179 additions and 72 deletions

View file

@ -1850,8 +1850,13 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
if (fJustCheck)
return true;
// Correct transaction counts.
pindex->nTx = block.vtx.size();
if (pindex->pprev)
pindex->nChainTx = pindex->pprev->nChainTx + block.vtx.size();
// Write undo information to disk
if (pindex->GetUndoPos().IsNull() || (pindex->nStatus & BLOCK_VALID_MASK) < BLOCK_VALID_SCRIPTS)
if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS))
{
if (pindex->GetUndoPos().IsNull()) {
CDiskBlockPos pos;
@ -1865,7 +1870,7 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
pindex->nStatus |= BLOCK_HAVE_UNDO;
}
pindex->nStatus = (pindex->nStatus & ~BLOCK_VALID_MASK) | BLOCK_VALID_SCRIPTS;
pindex->RaiseValidity(BLOCK_VALID_SCRIPTS);
CDiskBlockIndex blockindex(pindex);
if (!pblocktree->WriteBlockIndex(blockindex))
@ -2059,10 +2064,11 @@ void static FindMostWorkChain() {
CBlockIndex *pindexTest = pindexNew;
bool fInvalidAncestor = false;
while (pindexTest && !chainActive.Contains(pindexTest)) {
if (pindexTest->nStatus & BLOCK_FAILED_MASK) {
if (!pindexTest->IsValid(BLOCK_VALID_TRANSACTIONS) || !(pindexTest->nStatus & BLOCK_HAVE_DATA)) {
// Candidate has an invalid ancestor, remove entire chain from the set.
if (pindexBestInvalid == NULL || pindexNew->nChainWork > pindexBestInvalid->nChainWork)
pindexBestInvalid = pindexNew; CBlockIndex *pindexFailed = pindexNew;
pindexBestInvalid = pindexNew;
CBlockIndex *pindexFailed = pindexNew;
while (pindexTest != pindexFailed) {
pindexFailed->nStatus |= BLOCK_FAILED_CHILD;
setBlockIndexValid.erase(pindexFailed);
@ -2136,12 +2142,14 @@ bool ActivateBestChain(CValidationState &state) {
return true;
}
bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos)
CBlockIndex* AddToBlockIndex(CBlockHeader& block)
{
// Check for duplicate
uint256 hash = block.GetHash();
if (mapBlockIndex.count(hash))
return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString()), 0, "duplicate");
std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(hash);
if (it != mapBlockIndex.end())
return it->second;
// Construct new block index object
CBlockIndex* pindexNew = new CBlockIndex(block);
@ -2158,14 +2166,38 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos
pindexNew->pprev = (*miPrev).second;
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
}
pindexNew->nTx = block.vtx.size();
pindexNew->nChainWork = (pindexNew->pprev ? pindexNew->pprev->nChainWork : 0) + pindexNew->GetBlockWork().getuint256();
pindexNew->nChainTx = (pindexNew->pprev ? pindexNew->pprev->nChainTx : 0) + pindexNew->nTx;
pindexNew->RaiseValidity(BLOCK_VALID_TREE);
return pindexNew;
}
// Mark a block as having its data received and checked (up to BLOCK_VALID_TRANSACTIONS).
bool ReceivedBlockTransactions(const CBlock &block, CValidationState& state, CBlockIndex *pindexNew, const CDiskBlockPos& pos)
{
pindexNew->nTx = block.vtx.size();
if (pindexNew->pprev) {
// Not the genesis block.
if (pindexNew->pprev->nChainTx) {
// This parent's block's total number transactions is known, so compute outs.
pindexNew->nChainTx = pindexNew->pprev->nChainTx + pindexNew->nTx;
} else {
// The total number of transactions isn't known yet.
// We will compute it when the block is connected.
pindexNew->nChainTx = 0;
}
} else {
// Genesis block.
pindexNew->nChainTx = pindexNew->nTx;
}
pindexNew->nFile = pos.nFile;
pindexNew->nDataPos = pos.nPos;
pindexNew->nUndoPos = 0;
pindexNew->nStatus = BLOCK_VALID_TRANSACTIONS | BLOCK_HAVE_DATA;
setBlockIndexValid.insert(pindexNew);
pindexNew->nStatus |= BLOCK_HAVE_DATA;
if (pindexNew->RaiseValidity(BLOCK_VALID_TRANSACTIONS))
setBlockIndexValid.insert(pindexNew);
if (!pblocktree->WriteBlockIndex(CDiskBlockIndex(pindexNew)))
return state.Abort(_("Failed to write block index"));
@ -2289,26 +2321,55 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne
}
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW)
{
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits))
return state.DoS(50, error("CheckBlockHeader() : proof of work failed"),
REJECT_INVALID, "high-hash");
// Check timestamp
if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
return state.Invalid(error("CheckBlockHeader() : block timestamp too far in the future"),
REJECT_INVALID, "time-too-new");
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
if (pcheckpoint && block.hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0)))
{
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
int64_t deltaTime = block.GetBlockTime() - pcheckpoint->nTime;
if (deltaTime < 0)
{
return state.DoS(100, error("CheckBlockHeader() : block with timestamp before last checkpoint"),
REJECT_CHECKPOINT, "time-too-old");
}
CBigNum bnNewBlock;
bnNewBlock.SetCompact(block.nBits);
CBigNum bnRequired;
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
if (bnNewBlock > bnRequired)
{
return state.DoS(100, error("CheckBlockHeader() : block with too little proof-of-work"),
REJECT_INVALID, "bad-diffbits");
}
}
return true;
}
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bool fCheckMerkleRoot)
{
// These are checks that are independent of context
// that can be verified before saving an orphan block.
if (!CheckBlockHeader(block, state, fCheckPOW))
return false;
// Size limits
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"),
REJECT_INVALID, "bad-blk-length");
// Check proof of work matches claimed amount
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits))
return state.DoS(50, error("CheckBlock() : proof of work failed"),
REJECT_INVALID, "high-hash");
// Check timestamp
if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60)
return state.Invalid(error("CheckBlock() : block timestamp too far in the future"),
REJECT_INVALID, "time-too-new");
// First transaction must be coinbase, the rest must not be
if (block.vtx.empty() || !block.vtx[0].IsCoinBase())
return state.DoS(100, error("CheckBlock() : first tx is not coinbase"),
@ -2355,13 +2416,18 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo
return true;
}
bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex** ppindex)
{
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash();
if (mapBlockIndex.count(hash))
return state.Invalid(error("AcceptBlock() : block already in mapBlockIndex"), 0, "duplicate");
std::map<uint256, CBlockIndex*>::iterator miSelf = mapBlockIndex.find(hash);
CBlockIndex *pindex = NULL;
if (miSelf != mapBlockIndex.end()) {
pindex = miSelf->second;
if (pindex->nStatus & BLOCK_FAILED_MASK)
return state.Invalid(error("AcceptBlock() : block is marked invalid"), 0, "duplicate");
}
// Get prev block index
CBlockIndex* pindexPrev = NULL;
@ -2383,12 +2449,6 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
return state.Invalid(error("AcceptBlock() : block's timestamp is too early"),
REJECT_INVALID, "time-too-old");
// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!IsFinalTx(tx, nHeight, block.GetBlockTime()))
return state.DoS(10, error("AcceptBlock() : contains a non-final transaction"),
REJECT_INVALID, "bad-txns-nonfinal");
// Check that the block chain matches the known block chain up to a checkpoint
if (!Checkpoints::CheckBlock(nHeight, hash))
return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight),
@ -2409,18 +2469,57 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
REJECT_OBSOLETE, "bad-version");
}
}
// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
if (block.nVersion >= 2)
}
if (pindex == NULL)
pindex = AddToBlockIndex(block);
if (ppindex)
*ppindex = pindex;
return true;
}
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex** ppindex, CDiskBlockPos* dbp)
{
AssertLockHeld(cs_main);
CBlockIndex *&pindex = *ppindex;
if (!AcceptBlockHeader(block, state, &pindex))
return false;
if (!CheckBlock(block, state)) {
if (state.Invalid() && !state.CorruptionPossible()) {
pindex->nStatus |= BLOCK_FAILED_VALID;
}
return false;
}
int nHeight = pindex->nHeight;
uint256 hash = pindex->GetBlockHash();
// 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 (block.nVersion >= 2)
{
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 750, 1000)) ||
(TestNet() && CBlockIndex::IsSuperMajority(2, pindex->pprev, 51, 100)))
{
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 750, 1000)) ||
(TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100)))
{
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("AcceptBlock() : block height mismatch in coinbase"),
REJECT_INVALID, "bad-cb-height");
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");
}
}
}
@ -2436,8 +2535,8 @@ bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp)
if (dbp == NULL)
if (!WriteBlockToDisk(block, blockPos))
return state.Abort(_("Failed to write block"));
if (!AddToBlockIndex(block, state, blockPos))
return error("AcceptBlock() : AddToBlockIndex failed");
if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
return error("AcceptBlock() : ReceivedBlockTransactions failed");
} catch(std::runtime_error &e) {
return state.Abort(_("System error: ") + e.what());
}
@ -2507,30 +2606,9 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl
if (!CheckBlock(*pblock, state))
return error("ProcessBlock() : CheckBlock FAILED");
CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
if (pcheckpoint && pblock->hashPrevBlock != (chainActive.Tip() ? chainActive.Tip()->GetBlockHash() : uint256(0)))
{
// Extra checks to prevent "fill up memory by spamming with bogus blocks"
int64_t deltaTime = pblock->GetBlockTime() - pcheckpoint->nTime;
if (deltaTime < 0)
{
return state.DoS(100, error("ProcessBlock() : block with timestamp before last checkpoint"),
REJECT_CHECKPOINT, "time-too-old");
}
CBigNum bnNewBlock;
bnNewBlock.SetCompact(pblock->nBits);
CBigNum bnRequired;
bnRequired.SetCompact(ComputeMinWork(pcheckpoint->nBits, deltaTime));
if (bnNewBlock > bnRequired)
{
return state.DoS(100, error("ProcessBlock() : block with too little proof-of-work"),
REJECT_INVALID, "bad-diffbits");
}
}
// If we don't already have its previous block, shunt it off to holding area until we get it
if (pblock->hashPrevBlock != 0 && !mapBlockIndex.count(pblock->hashPrevBlock))
// If we don't already have its previous block (with full data), shunt it off to holding area until we get it
std::map<uint256, CBlockIndex*>::iterator it = mapBlockIndex.find(pblock->hashPrevBlock);
if (pblock->hashPrevBlock != 0 && (it == mapBlockIndex.end() || !(it->second->nStatus & BLOCK_HAVE_DATA)))
{
LogPrintf("ProcessBlock: ORPHAN BLOCK %lu, prev=%s\n", (unsigned long)mapOrphanBlocks.size(), pblock->hashPrevBlock.ToString());
@ -2555,7 +2633,9 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl
}
// Store to disk
if (!AcceptBlock(*pblock, state, dbp))
CBlockIndex *pindex = NULL;
bool ret = AcceptBlock(*pblock, state, &pindex, dbp);
if (!ret)
return error("ProcessBlock() : AcceptBlock FAILED");
// Recursively process any orphan blocks that depended on this one
@ -2576,7 +2656,8 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl
block.BuildMerkleTree();
// Use a dummy CValidationState so someone can't setup nodes to counter-DoS based on orphan resolution (that is, feeding people an invalid block based on LegitBlockX in order to get anyone relaying LegitBlockX banned)
CValidationState stateDummy;
if (AcceptBlock(block, stateDummy))
CBlockIndex *pindexChild = NULL;
if (AcceptBlock(block, stateDummy, &pindexChild))
vWorkQueue.push_back(mi->second->hashBlock);
mapOrphanBlocks.erase(mi->second->hashBlock);
delete mi->second;
@ -2839,7 +2920,7 @@ bool static LoadBlockIndexDB()
CBlockIndex* pindex = item.second;
pindex->nChainWork = (pindex->pprev ? pindex->pprev->nChainWork : 0) + pindex->GetBlockWork().getuint256();
pindex->nChainTx = (pindex->pprev ? pindex->pprev->nChainTx : 0) + pindex->nTx;
if ((pindex->nStatus & BLOCK_VALID_MASK) >= BLOCK_VALID_TRANSACTIONS && !(pindex->nStatus & BLOCK_FAILED_MASK))
if (pindex->IsValid(BLOCK_VALID_TRANSACTIONS))
setBlockIndexValid.insert(pindex);
if (pindex->nStatus & BLOCK_FAILED_MASK && (!pindexBestInvalid || pindex->nChainWork > pindexBestInvalid->nChainWork))
pindexBestInvalid = pindex;
@ -2987,7 +3068,8 @@ bool InitBlockIndex() {
return error("LoadBlockIndex() : FindBlockPos failed");
if (!WriteBlockToDisk(block, blockPos))
return error("LoadBlockIndex() : writing genesis block to disk failed");
if (!AddToBlockIndex(block, state, blockPos))
CBlockIndex *pindex = AddToBlockIndex(block);
if (!ReceivedBlockTransactions(block, state, pindex, blockPos))
return error("LoadBlockIndex() : genesis block not accepted");
} catch(std::runtime_error &e) {
return error("LoadBlockIndex() : failed to initialize block database: %s", e.what());

View file

@ -603,11 +603,13 @@ bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, C
bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos);
// Context-independent validity checks
bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool fCheckPOW = true);
bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = true, bool fCheckMerkleRoot = true);
// Store block on disk
// if dbp is provided, the file is known to already reside on disk
bool AcceptBlock(CBlock& block, CValidationState& state, CDiskBlockPos* dbp = NULL);
bool AcceptBlock(CBlock& block, CValidationState& state, CBlockIndex **pindex, CDiskBlockPos* dbp = NULL);
bool AcceptBlockHeader(CBlockHeader& block, CValidationState& state, CBlockIndex **ppindex= NULL);
@ -865,6 +867,29 @@ public:
{
LogPrintf("%s\n", ToString().c_str());
}
// Check whether this block index entry is valid up to the passed validity level.
bool IsValid(enum BlockStatus nUpTo = BLOCK_VALID_TRANSACTIONS) const
{
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK)
return false;
return ((nStatus & BLOCK_VALID_MASK) >= nUpTo);
}
// Raise the validity level of this block index entry.
// Returns true if the validity was changed.
bool RaiseValidity(enum BlockStatus nUpTo)
{
assert(!(nUpTo & ~BLOCK_VALID_MASK)); // Only validity flags allowed.
if (nStatus & BLOCK_FAILED_MASK)
return false;
if ((nStatus & BLOCK_VALID_MASK) < nUpTo) {
nStatus = (nStatus & ~BLOCK_VALID_MASK) | nUpTo;
return true;
}
return false;
}
};