diff --git a/src/core.h b/src/core.h index c568fd2ef..fce9ccc16 100644 --- a/src/core.h +++ b/src/core.h @@ -733,4 +733,127 @@ public: } }; + +class CBlock : public CBlockHeader +{ +public: + // network and disk + std::vector vtx; + + // memory only + mutable std::vector vMerkleTree; + + CBlock() + { + SetNull(); + } + + CBlock(const CBlockHeader &header) + { + SetNull(); + *((CBlockHeader*)this) = header; + } + + IMPLEMENT_SERIALIZE + ( + READWRITE(*(CBlockHeader*)this); + READWRITE(vtx); + ) + + void SetNull() + { + CBlockHeader::SetNull(); + vtx.clear(); + vMerkleTree.clear(); + } + + CBlockHeader GetBlockHeader() const + { + CBlockHeader block; + block.nVersion = nVersion; + block.hashPrevBlock = hashPrevBlock; + block.hashMerkleRoot = hashMerkleRoot; + block.nTime = nTime; + block.nBits = nBits; + block.nNonce = nNonce; + return block; + } + + uint256 BuildMerkleTree() const + { + vMerkleTree.clear(); + BOOST_FOREACH(const CTransaction& tx, vtx) + vMerkleTree.push_back(tx.GetHash()); + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + for (int i = 0; i < nSize; i += 2) + { + int i2 = std::min(i+1, nSize-1); + vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), + BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); + } + j += nSize; + } + return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); + } + + const uint256 &GetTxHash(unsigned int nIndex) const { + assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first + assert(nIndex < vtx.size()); + return vMerkleTree[nIndex]; + } + + std::vector GetMerkleBranch(int nIndex) const + { + if (vMerkleTree.empty()) + BuildMerkleTree(); + std::vector vMerkleBranch; + int j = 0; + for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + { + int i = std::min(nIndex^1, nSize-1); + vMerkleBranch.push_back(vMerkleTree[j+i]); + nIndex >>= 1; + j += nSize; + } + return vMerkleBranch; + } + + static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) + { + if (nIndex == -1) + return 0; + BOOST_FOREACH(const uint256& otherside, vMerkleBranch) + { + if (nIndex & 1) + hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); + else + hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); + nIndex >>= 1; + } + return hash; + } + + void print() const + { + printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n", + GetHash().ToString().c_str(), + nVersion, + hashPrevBlock.ToString().c_str(), + hashMerkleRoot.ToString().c_str(), + nTime, nBits, nNonce, + vtx.size()); + for (unsigned int i = 0; i < vtx.size(); i++) + { + printf(" "); + vtx[i].print(); + } + printf(" vMerkleTree: "); + for (unsigned int i = 0; i < vMerkleTree.size(); i++) + printf("%s ", vMerkleTree[i].ToString().c_str()); + printf("\n"); + } +}; + #endif diff --git a/src/init.cpp b/src/init.cpp index 449ee906b..e88055bde 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -820,7 +820,7 @@ bool AppInit2(boost::thread_group& threadGroup) { CBlockIndex* pindex = (*mi).second; CBlock block; - block.ReadFromDisk(pindex); + ReadBlockFromDisk(block, pindex); block.BuildMerkleTree(); block.print(); printf("\n"); diff --git a/src/main.cpp b/src/main.cpp index 5c9aa3202..da928a4b9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -627,7 +627,7 @@ int CMerkleTx::SetMerkleBranch(const CBlock* pblock) if (pcoinsTip->GetCoins(GetHash(), coins)) { CBlockIndex *pindex = FindBlockByHeight(coins.nHeight); if (pindex) { - if (!blockTmp.ReadFromDisk(pindex)) + if (!ReadBlockFromDisk(blockTmp, pindex)) return 0; pblock = &blockTmp; } @@ -1114,7 +1114,7 @@ bool GetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock if (pindexSlow) { CBlock block; - if (block.ReadFromDisk(pindexSlow)) { + if (ReadBlockFromDisk(block, pindexSlow)) { BOOST_FOREACH(const CTransaction &tx, block.vtx) { if (tx.GetHash() == hash) { txOut = tx; @@ -1146,12 +1146,62 @@ CBlockIndex* FindBlockByHeight(int nHeight) return vBlockIndexByHeight[nHeight]; } -bool CBlock::ReadFromDisk(const CBlockIndex* pindex) +bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos) { - if (!ReadFromDisk(pindex->GetBlockPos())) + // Open history file to append + CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); + if (!fileout) + return error("WriteBlockToDisk() : OpenBlockFile failed"); + + // Write index header + unsigned int nSize = fileout.GetSerializeSize(block); + fileout << FLATDATA(Params().MessageStart()) << nSize; + + // Write block + long fileOutPos = ftell(fileout); + if (fileOutPos < 0) + return error("WriteBlockToDisk() : ftell failed"); + pos.nPos = (unsigned int)fileOutPos; + fileout << block; + + // Flush stdio buffers and commit to disk before returning + fflush(fileout); + if (!IsInitialBlockDownload()) + FileCommit(fileout); + + return true; +} + +bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos) +{ + block.SetNull(); + + // Open history file to read + CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("ReadBlockFromDisk(CBlock&, CDiskBlockPos&) : OpenBlockFile failed"); + + // Read block + try { + filein >> block; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + + // Check the header + if (!CheckProofOfWork(block.GetHash(), block.nBits)) + return error("ReadBlockFromDisk(CBlock&, CDiskBlockPos&) : errors in block header"); + + return true; +} + +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex) +{ + if (!ReadBlockFromDisk(block, pindex->GetBlockPos())) return false; - if (GetHash() != pindex->GetBlockHash()) - return error("CBlock::ReadFromDisk() : GetHash() doesn't match index"); + if (block.GetHash() != pindex->GetBlockHash()) + return error("ReadBlockFromDisk(CBlock&, CBlockIndex*) : GetHash() doesn't match index"); return true; } @@ -1553,7 +1603,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, CCoinsViewCach -bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &view, bool *pfClean) +bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { assert(pindex == view.GetBestBlock()); @@ -1569,12 +1619,12 @@ bool CBlock::DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoin if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) return error("DisconnectBlock() : failure reading undo data"); - if (blockUndo.vtxundo.size() + 1 != vtx.size()) + if (blockUndo.vtxundo.size() + 1 != block.vtx.size()) return error("DisconnectBlock() : block and undo data inconsistent"); // undo transactions in reverse order - for (int i = vtx.size() - 1; i >= 0; i--) { - const CTransaction &tx = vtx[i]; + for (int i = block.vtx.size() - 1; i >= 0; i--) { + const CTransaction &tx = block.vtx[i]; uint256 hash = tx.GetHash(); // check that all outputs are available @@ -1667,10 +1717,10 @@ void ThreadScriptCheck() { scriptcheckqueue.Thread(); } -bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsViewCache &view, bool fJustCheck) +bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool fJustCheck) { // Check it again in case a previous version let a bad block in - if (!CheckBlock(state, !fJustCheck, !fJustCheck)) + if (!CheckBlock(block, state, !fJustCheck, !fJustCheck)) return false; // verify that the view's current state corresponds to the previous block @@ -1678,7 +1728,7 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi // Special case for the genesis block, skipping connection of its transactions // (its coinbase is unspendable) - if (GetHash() == Params().HashGenesisBlock()) { + if (block.GetHash() == Params().HashGenesisBlock()) { view.SetBestBlock(pindex); pindexGenesisBlock = pindex; return true; @@ -1702,8 +1752,8 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi !((pindex->nHeight==91842 && pindex->GetBlockHash() == uint256("0x00000000000a4d0a398161ffc163c503763b1f4360639393e0e4c8e300e0caec")) || (pindex->nHeight==91880 && pindex->GetBlockHash() == uint256("0x00000000000743f190a18c5577a3c2d2a1f610ae9601ac046a38084ccb7cd721"))); if (fEnforceBIP30) { - for (unsigned int i=0; iGetBlockPos(), GetSizeOfCompactSize(vtx.size())); + CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector > vPos; - vPos.reserve(vtx.size()); - for (unsigned int i=0; inHeight, GetTxHash(i)); + UpdateCoins(tx, state, view, txundo, pindex->nHeight, block.GetTxHash(i)); if (!tx.IsCoinBase()) blockundo.vtxundo.push_back(txundo); - vPos.push_back(std::make_pair(GetTxHash(i), pos)); + vPos.push_back(std::make_pair(block.GetTxHash(i), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } int64 nTime = GetTimeMicros() - nStart; if (fBenchmark) - printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)vtx.size(), 0.001 * nTime, 0.001 * nTime / vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); + printf("- Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin)\n", (unsigned)block.vtx.size(), 0.001 * nTime, 0.001 * nTime / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * nTime / (nInputs-1)); - if (GetValueOut(vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) - return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", GetValueOut(vtx[0]), GetBlockValue(pindex->nHeight, nFees))); + if (GetValueOut(block.vtx[0]) > GetBlockValue(pindex->nHeight, nFees)) + return state.DoS(100, error("ConnectBlock() : coinbase pays too much (actual=%"PRI64d" vs limit=%"PRI64d")", GetValueOut(block.vtx[0]), GetBlockValue(pindex->nHeight, nFees))); if (!control.Wait()) return state.DoS(100, false); @@ -1813,8 +1863,8 @@ bool CBlock::ConnectBlock(CValidationState &state, CBlockIndex* pindex, CCoinsVi assert(view.SetBestBlock(pindex)); // Watch for transactions paying to me - for (unsigned int i=0; i vResurrect; BOOST_FOREACH(CBlockIndex* pindex, vDisconnect) { CBlock block; - if (!block.ReadFromDisk(pindex)) + if (!ReadBlockFromDisk(block, pindex)) return state.Abort(_("Failed to read block")); int64 nStart = GetTimeMicros(); - if (!block.DisconnectBlock(state, pindex, view)) + if (!DisconnectBlock(block, state, pindex, view)) return error("SetBestBlock() : DisconnectBlock %s failed", pindex->GetBlockHash().ToString().c_str()); if (fBenchmark) printf("- Disconnect: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); @@ -1880,10 +1930,10 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) vector vDelete; BOOST_FOREACH(CBlockIndex *pindex, vConnect) { CBlock block; - if (!block.ReadFromDisk(pindex)) + if (!ReadBlockFromDisk(block, pindex)) return state.Abort(_("Failed to read block")); int64 nStart = GetTimeMicros(); - if (!block.ConnectBlock(state, pindex, view)) { + if (!ConnectBlock(block, state, pindex, view)) { if (state.IsInvalid()) { InvalidChainFound(pindexNew); InvalidBlockFound(pindex); @@ -1993,25 +2043,25 @@ bool SetBestChain(CValidationState &state, CBlockIndex* pindexNew) } -bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos) +bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos) { // Check for duplicate - uint256 hash = GetHash(); + uint256 hash = block.GetHash(); if (mapBlockIndex.count(hash)) return state.Invalid(error("AddToBlockIndex() : %s already exists", hash.ToString().c_str())); // Construct new block index object - CBlockIndex* pindexNew = new CBlockIndex(*this); + CBlockIndex* pindexNew = new CBlockIndex(block); assert(pindexNew); map::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; pindexNew->phashBlock = &((*mi).first); - map::iterator miPrev = mapBlockIndex.find(hashPrevBlock); + map::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock); if (miPrev != mapBlockIndex.end()) { pindexNew->pprev = (*miPrev).second; pindexNew->nHeight = pindexNew->pprev->nHeight + 1; } - pindexNew->nTx = vtx.size(); + 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->nFile = pos.nFile; @@ -2032,7 +2082,7 @@ bool CBlock::AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos) // Notify UI to display prev block's coinbase if it was ours static uint256 hashPrevBestCoinBase; UpdatedTransaction(hashPrevBestCoinBase); - hashPrevBestCoinBase = GetTxHash(0); + hashPrevBestCoinBase = block.GetTxHash(0); } if (!pblocktree->Flush()) @@ -2138,51 +2188,51 @@ bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigne } -bool CBlock::CheckBlock(CValidationState &state, bool fCheckPOW, bool fCheckMerkleRoot) const +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. // Size limits - if (vtx.empty() || vtx.size() > MAX_BLOCK_SIZE || ::GetSerializeSize(*this, 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")); // Check proof of work matches claimed amount - if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) + if (fCheckPOW && !CheckProofOfWork(block.GetHash(), block.nBits)) return state.DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp - if (GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) + if (block.GetBlockTime() > GetAdjustedTime() + 2 * 60 * 60) return state.Invalid(error("CheckBlock() : block timestamp too far in the future")); // First transaction must be coinbase, the rest must not be - if (vtx.empty() || !vtx[0].IsCoinBase()) + if (block.vtx.empty() || !block.vtx[0].IsCoinBase()) return state.DoS(100, error("CheckBlock() : first tx is not coinbase")); - for (unsigned int i = 1; i < vtx.size(); i++) - if (vtx[i].IsCoinBase()) + for (unsigned int i = 1; i < block.vtx.size(); i++) + if (block.vtx[i].IsCoinBase()) return state.DoS(100, error("CheckBlock() : more than one coinbase")); // Check transactions - BOOST_FOREACH(const CTransaction& tx, vtx) + BOOST_FOREACH(const CTransaction& tx, block.vtx) if (!CheckTransaction(tx, state)) return error("CheckBlock() : CheckTransaction failed"); // Build the merkle tree already. We need it anyway later, and it makes the // block cache the transaction hashes, which means they don't need to be // recalculated many times during this block's validation. - BuildMerkleTree(); + block.BuildMerkleTree(); // Check for duplicate txids. This is caught by ConnectInputs(), // but catching it earlier avoids a potential DoS attack: set uniqueTx; - for (unsigned int i=0; i::iterator mi = mapBlockIndex.find(hashPrevBlock); + map::iterator mi = mapBlockIndex.find(block.hashPrevBlock); if (mi == mapBlockIndex.end()) return state.DoS(10, error("AcceptBlock() : prev block not found")); pindexPrev = (*mi).second; nHeight = pindexPrev->nHeight+1; // Check proof of work - if (nBits != GetNextWorkRequired(pindexPrev, this)) + if (block.nBits != GetNextWorkRequired(pindexPrev, &block)) return state.DoS(100, error("AcceptBlock() : incorrect proof of work")); // Check timestamp against prev - if (GetBlockTime() <= pindexPrev->GetMedianTimePast()) + if (block.GetBlockTime() <= pindexPrev->GetMedianTimePast()) return state.Invalid(error("AcceptBlock() : block's timestamp is too early")); // Check that all transactions are finalized - BOOST_FOREACH(const CTransaction& tx, vtx) - if (!IsFinalTx(tx, nHeight, GetBlockTime())) + BOOST_FOREACH(const CTransaction& tx, block.vtx) + if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) return state.DoS(10, error("AcceptBlock() : contains a non-final transaction")); // Check that the block chain matches the known block chain up to a checkpoint @@ -2231,7 +2281,7 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) return state.DoS(100, error("AcceptBlock() : rejected by checkpoint lock-in at %d", nHeight)); // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: - if (nVersion < 2) + if (block.nVersion < 2) { if ((!TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 950, 1000)) || (TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 75, 100))) @@ -2240,14 +2290,14 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) } } // Enforce block.nVersion=2 rule that the coinbase starts with serialized block height - if (nVersion >= 2) + 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, pindexPrev, 750, 1000)) || (TestNet() && CBlockIndex::IsSuperMajority(2, pindexPrev, 51, 100))) { CScript expect = CScript() << nHeight; - if (!std::equal(expect.begin(), expect.end(), vtx[0].vin[0].scriptSig.begin())) + if (!std::equal(expect.begin(), expect.end(), block.vtx[0].vin[0].scriptSig.begin())) return state.DoS(100, error("AcceptBlock() : block height mismatch in coinbase")); } } @@ -2255,16 +2305,16 @@ bool CBlock::AcceptBlock(CValidationState &state, CDiskBlockPos *dbp) // Write block to history file try { - unsigned int nBlockSize = ::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION); + unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); CDiskBlockPos blockPos; if (dbp != NULL) blockPos = *dbp; - if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, nTime, dbp != NULL)) + if (!FindBlockPos(state, blockPos, nBlockSize+8, nHeight, block.nTime, dbp != NULL)) return error("AcceptBlock() : FindBlockPos failed"); if (dbp == NULL) - if (!WriteToDisk(blockPos)) + if (!WriteBlockToDisk(block, blockPos)) return state.Abort(_("Failed to write block")); - if (!AddToBlockIndex(state, blockPos)) + if (!AddToBlockIndex(block, state, blockPos)) return error("AcceptBlock() : AddToBlockIndex failed"); } catch(std::runtime_error &e) { return state.Abort(_("System error: ") + e.what()); @@ -2316,7 +2366,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl return state.Invalid(error("ProcessBlock() : already have block (orphan) %s", hash.ToString().c_str())); // Preliminary checks - if (!pblock->CheckBlock(state)) + if (!CheckBlock(*pblock, state)) return error("ProcessBlock() : CheckBlock FAILED"); CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex); @@ -2357,7 +2407,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl } // Store to disk - if (!pblock->AcceptBlock(state, dbp)) + if (!AcceptBlock(*pblock, state, dbp)) return error("ProcessBlock() : AcceptBlock FAILED"); // Recursively process any orphan blocks that depended on this one @@ -2373,7 +2423,7 @@ bool ProcessBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, CDiskBl CBlock* pblockOrphan = (*mi).second; // 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 (pblockOrphan->AcceptBlock(stateDummy)) + if (AcceptBlock(*pblockOrphan, stateDummy)) vWorkQueue.push_back(pblockOrphan->GetHash()); mapOrphanBlocks.erase(pblockOrphan->GetHash()); delete pblockOrphan; @@ -2710,10 +2760,10 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) break; CBlock block; // check level 0: read from disk - if (!block.ReadFromDisk(pindex)) - return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + if (!ReadBlockFromDisk(block, pindex)) + return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // check level 1: verify block validity - if (nCheckLevel >= 1 && !block.CheckBlock(state)) + if (nCheckLevel >= 1 && !CheckBlock(block, state)) return error("VerifyDB() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); // check level 2: verify undo validity if (nCheckLevel >= 2 && pindex) { @@ -2727,7 +2777,7 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) // check level 3: check for inconsistencies during memory-only disconnect of tip blocks if (nCheckLevel >= 3 && pindex == pindexState && (coins.GetCacheSize() + pcoinsTip->GetCacheSize()) <= 2*nCoinCacheSize + 32000) { bool fClean = true; - if (!block.DisconnectBlock(state, pindex, coins, &fClean)) + if (!DisconnectBlock(block, state, pindex, coins, &fClean)) return error("VerifyDB() : *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); pindexState = pindex->pprev; if (!fClean) { @@ -2747,9 +2797,9 @@ bool VerifyDB(int nCheckLevel, int nCheckDepth) boost::this_thread::interruption_point(); pindex = pindex->GetNextInMainChain(); CBlock block; - if (!block.ReadFromDisk(pindex)) - return error("VerifyDB() : *** block.ReadFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); - if (!block.ConnectBlock(state, pindex, coins)) + if (!ReadBlockFromDisk(block, pindex)) + return error("VerifyDB() : *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); + if (!ConnectBlock(block, state, pindex, coins)) return error("VerifyDB() : *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); } } @@ -2800,9 +2850,9 @@ bool InitBlockIndex() { CValidationState state; if (!FindBlockPos(state, blockPos, nBlockSize+8, 0, block.nTime)) return error("LoadBlockIndex() : FindBlockPos failed"); - if (!block.WriteToDisk(blockPos)) + if (!WriteBlockToDisk(block, blockPos)) return error("LoadBlockIndex() : writing genesis block to disk failed"); - if (!block.AddToBlockIndex(state, blockPos)) + if (!AddToBlockIndex(block, state, blockPos)) return error("LoadBlockIndex() : genesis block not accepted"); } catch(std::runtime_error &e) { return error("LoadBlockIndex() : failed to initialize block database: %s", e.what()); @@ -2858,7 +2908,7 @@ void PrintBlockTree() // print item CBlock block; - block.ReadFromDisk(pindex); + ReadBlockFromDisk(block, pindex); printf("%d (blk%05u.dat:0x%x) %s tx %"PRIszu"", pindex->nHeight, pindex->GetBlockPos().nFile, pindex->GetBlockPos().nPos, @@ -3082,7 +3132,7 @@ void static ProcessGetData(CNode* pfrom) if (mi != mapBlockIndex.end()) { CBlock block; - block.ReadFromDisk((*mi).second); + ReadBlockFromDisk(block, (*mi).second); if (inv.type == MSG_BLOCK) pfrom->PushMessage("block", block); else // MSG_FILTERED_BLOCK) @@ -4404,7 +4454,7 @@ CBlockTemplate* CreateNewBlock(CReserveKey& reservekey) indexDummy.nHeight = pindexPrev->nHeight + 1; CCoinsViewCache viewNew(*pcoinsTip, true); CValidationState state; - if (!pblock->ConnectBlock(state, &indexDummy, viewNew, true)) + if (!ConnectBlock(*pblock, state, &indexDummy, viewNew, true)) throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); } diff --git a/src/main.h b/src/main.h index c5e5f2bfe..9e0235fa5 100644 --- a/src/main.h +++ b/src/main.h @@ -581,204 +581,33 @@ public: }; -class CBlock : public CBlockHeader -{ -public: - // network and disk - std::vector vtx; - // memory only - mutable std::vector vMerkleTree; - - CBlock() - { - SetNull(); - } - - CBlock(const CBlockHeader &header) - { - SetNull(); - *((CBlockHeader*)this) = header; - } - - IMPLEMENT_SERIALIZE - ( - READWRITE(*(CBlockHeader*)this); - READWRITE(vtx); - ) - - void SetNull() - { - CBlockHeader::SetNull(); - vtx.clear(); - vMerkleTree.clear(); - } - - CBlockHeader GetBlockHeader() const - { - CBlockHeader block; - block.nVersion = nVersion; - block.hashPrevBlock = hashPrevBlock; - block.hashMerkleRoot = hashMerkleRoot; - block.nTime = nTime; - block.nBits = nBits; - block.nNonce = nNonce; - return block; - } - - uint256 BuildMerkleTree() const - { - vMerkleTree.clear(); - BOOST_FOREACH(const CTransaction& tx, vtx) - vMerkleTree.push_back(tx.GetHash()); - int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - for (int i = 0; i < nSize; i += 2) - { - int i2 = std::min(i+1, nSize-1); - vMerkleTree.push_back(Hash(BEGIN(vMerkleTree[j+i]), END(vMerkleTree[j+i]), - BEGIN(vMerkleTree[j+i2]), END(vMerkleTree[j+i2]))); - } - j += nSize; - } - return (vMerkleTree.empty() ? 0 : vMerkleTree.back()); - } - - const uint256 &GetTxHash(unsigned int nIndex) const { - assert(vMerkleTree.size() > 0); // BuildMerkleTree must have been called first - assert(nIndex < vtx.size()); - return vMerkleTree[nIndex]; - } - - std::vector GetMerkleBranch(int nIndex) const - { - if (vMerkleTree.empty()) - BuildMerkleTree(); - std::vector vMerkleBranch; - int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) - { - int i = std::min(nIndex^1, nSize-1); - vMerkleBranch.push_back(vMerkleTree[j+i]); - nIndex >>= 1; - j += nSize; - } - return vMerkleBranch; - } - - static uint256 CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) - { - if (nIndex == -1) - return 0; - BOOST_FOREACH(const uint256& otherside, vMerkleBranch) - { - if (nIndex & 1) - hash = Hash(BEGIN(otherside), END(otherside), BEGIN(hash), END(hash)); - else - hash = Hash(BEGIN(hash), END(hash), BEGIN(otherside), END(otherside)); - nIndex >>= 1; - } - return hash; - } - - bool WriteToDisk(CDiskBlockPos &pos) - { - // Open history file to append - CAutoFile fileout = CAutoFile(OpenBlockFile(pos), SER_DISK, CLIENT_VERSION); - if (!fileout) - return error("CBlock::WriteToDisk() : OpenBlockFile failed"); - - // Write index header - unsigned int nSize = fileout.GetSerializeSize(*this); - fileout << FLATDATA(Params().MessageStart()) << nSize; - - // Write block - long fileOutPos = ftell(fileout); - if (fileOutPos < 0) - return error("CBlock::WriteToDisk() : ftell failed"); - pos.nPos = (unsigned int)fileOutPos; - fileout << *this; - - // Flush stdio buffers and commit to disk before returning - fflush(fileout); - if (!IsInitialBlockDownload()) - FileCommit(fileout); - - return true; - } - - bool ReadFromDisk(const CDiskBlockPos &pos) - { - SetNull(); - - // Open history file to read - CAutoFile filein = CAutoFile(OpenBlockFile(pos, true), SER_DISK, CLIENT_VERSION); - if (!filein) - return error("CBlock::ReadFromDisk() : OpenBlockFile failed"); - - // Read block - try { - filein >> *this; - } - catch (std::exception &e) { - return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); - } - - // Check the header - if (!CheckProofOfWork(GetHash(), nBits)) - return error("CBlock::ReadFromDisk() : errors in block header"); - - return true; - } +/** Functions for disk access for blocks */ +bool WriteBlockToDisk(CBlock& block, CDiskBlockPos& pos); +bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos); +bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex); +/** Functions for validating blocks and updating the block tree */ - void print() const - { - printf("CBlock(hash=%s, ver=%d, hashPrevBlock=%s, hashMerkleRoot=%s, nTime=%u, nBits=%08x, nNonce=%u, vtx=%"PRIszu")\n", - GetHash().ToString().c_str(), - nVersion, - hashPrevBlock.ToString().c_str(), - hashMerkleRoot.ToString().c_str(), - nTime, nBits, nNonce, - vtx.size()); - for (unsigned int i = 0; i < vtx.size(); i++) - { - printf(" "); - vtx[i].print(); - } - printf(" vMerkleTree: "); - for (unsigned int i = 0; i < vMerkleTree.size(); i++) - printf("%s ", vMerkleTree[i].ToString().c_str()); - printf("\n"); - } +/** Undo the effects of this block (with given index) on the UTXO set represented by coins. + * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean + * will be true if no problems were found. Otherwise, the return value will be false in case + * of problems. Note that in any case, coins may be modified. */ +bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool* pfClean = NULL); +// Apply the effects of this block (with given index) on the UTXO set represented by coins +bool ConnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& coins, bool fJustCheck = false); - /** Undo the effects of this block (with given index) on the UTXO set represented by coins. - * In case pfClean is provided, operation will try to be tolerant about errors, and *pfClean - * will be true if no problems were found. Otherwise, the return value will be false in case - * of problems. Note that in any case, coins may be modified. */ - bool DisconnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool *pfClean = NULL); - - // Apply the effects of this block (with given index) on the UTXO set represented by coins - bool ConnectBlock(CValidationState &state, CBlockIndex *pindex, CCoinsViewCache &coins, bool fJustCheck=false); - - // Read a block from disk - bool ReadFromDisk(const CBlockIndex* pindex); - - // Add this block to the block index, and if necessary, switch the active block chain to this - bool AddToBlockIndex(CValidationState &state, const CDiskBlockPos &pos); - - // Context-independent validity checks - bool CheckBlock(CValidationState &state, bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; - - // Store block on disk - // if dbp is provided, the file is known to already reside on disk - bool AcceptBlock(CValidationState &state, CDiskBlockPos *dbp = NULL); -}; +// Add this block to the block index, and if necessary, switch the active block chain to this +bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos); +// Context-independent validity checks +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); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 6b2ef8315..3c24016fe 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -163,7 +163,7 @@ Value getblock(const Array& params, bool fHelp) CBlock block; CBlockIndex* pblockindex = mapBlockIndex[hash]; - block.ReadFromDisk(pblockindex); + ReadBlockFromDisk(block, pblockindex); if (!fVerbose) { diff --git a/src/test/checkblock_tests.cpp b/src/test/checkblock_tests.cpp index d626f9a6f..5675c40e7 100644 --- a/src/test/checkblock_tests.cpp +++ b/src/test/checkblock_tests.cpp @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(May15) // After May 15'th, big blocks are OK: forkingBlock.nTime = tMay15; // Invalidates PoW - BOOST_CHECK(forkingBlock.CheckBlock(state, false, false)); + BOOST_CHECK(CheckBlock(forkingBlock, state, false, false)); } SetMockTime(0); diff --git a/src/wallet.cpp b/src/wallet.cpp index b09f94980..488787f96 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -804,7 +804,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) } CBlock block; - block.ReadFromDisk(pindex); + ReadBlockFromDisk(block, pindex); BOOST_FOREACH(CTransaction& tx, block.vtx) { if (AddToWalletIfInvolvingMe(tx.GetHash(), tx, &block, fUpdate))