Split AcceptBlockHeader from AcceptBlock.
Also modify some connection logic to deal with non-full blocks in the index.
This commit is contained in:
parent
f457347053
commit
942b33a19d
2 changed files with 162 additions and 64 deletions
200
src/main.cpp
200
src/main.cpp
|
@ -1849,8 +1849,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;
|
||||
|
@ -1864,7 +1869,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))
|
||||
|
@ -2058,10 +2063,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);
|
||||
|
@ -2135,12 +2141,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);
|
||||
|
@ -2157,14 +2165,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"));
|
||||
|
@ -2292,14 +2324,35 @@ bool CheckBlockHeader(const CBlockHeader& block, CValidationState& state, bool f
|
|||
{
|
||||
// Check proof of work matches claimed amount
|
||||
if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits))
|
||||
return state.DoS(50, error("CheckBlock() : proof of work failed"),
|
||||
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("CheckBlock() : block timestamp too far in the future"),
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -2362,13 +2415,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;
|
||||
|
@ -2390,12 +2448,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),
|
||||
|
@ -2416,18 +2468,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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2443,8 +2534,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());
|
||||
}
|
||||
|
@ -2514,30 +2605,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());
|
||||
|
||||
|
@ -2562,7 +2632,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
|
||||
|
@ -2583,7 +2655,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;
|
||||
|
@ -2846,7 +2919,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;
|
||||
|
@ -2993,7 +3066,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());
|
||||
|
|
26
src/main.h
26
src/main.h
|
@ -608,7 +608,8 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW = t
|
|||
|
||||
// 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);
|
||||
|
||||
|
||||
|
||||
|
@ -866,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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue