aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2014-05-09 16:00:32 +0200
committerWladimir J. van der Laan <laanwj@gmail.com>2014-05-09 16:00:41 +0200
commitd54985f3f1385481696dbc53bce4aa3c0205a597 (patch)
tree12a7546b0c70170b77a4e0d4d831e92197d113a6
parentaaab675a78378c8f087639b24feba10f0e25630f (diff)
parent942b33a19d3aa96326acc044c1834c48beff777c (diff)
Merge pull request #3884
942b33a Split AcceptBlockHeader from AcceptBlock. (Pieter Wuille) f457347 Split up CheckBlock in a block and header version (Pieter Wuille)
-rw-r--r--src/main.cpp224
-rw-r--r--src/main.h27
2 files changed, 179 insertions, 72 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 7b9ca2878c..83a640d7f3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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());
diff --git a/src/main.h b/src/main.h
index 825e577d1e..f63f4d5f11 100644
--- a/src/main.h
+++ b/src/main.h
@@ -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;
+ }
};