From 3cd01fdf0e540c4e06cd27b6c0d6b6abc00767d1 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Wed, 9 May 2012 17:24:44 +0000 Subject: [PATCH] CreateNewBlock: Check that the produced CBlock is acceptable (except for proof-of-work and merkletree, since those need to be provided later) This throws an exception from CreateNewBlock otherwise, which is not safe without #1245! --- src/main.cpp | 43 +++++++++++++++++++++++++++++++------------ src/main.h | 4 ++-- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index d795ca1d..48bf0a54 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1252,10 +1252,10 @@ bool CBlock::DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex) return true; } -bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) +bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck) { // Check it again in case a previous version let a bad block in - if (!CheckBlock()) + if (!CheckBlock(!fJustCheck, !fJustCheck)) return false; // Do not allow blocks that contain transactions which 'overwrite' older transactions, @@ -1283,7 +1283,13 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) bool fStrictPayToScriptHash = (pindex->nTime >= nBIP16SwitchTime); //// issue here: it doesn't know the version - unsigned int nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); + unsigned int nTxPos; + if (fJustCheck) + // FetchInputs treats CDiskTxPos(1,1,1) as a special "refer to memorypool" indicator + // Since we're just checking the block and not actually connecting it, it might not (and probably shouldn't) be on the disk to get the transaction from + nTxPos = 1; + else + nTxPos = pindex->nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK) - 1 + GetSizeOfCompactSize(vtx.size()); map mapQueuedChanges; int64 nFees = 0; @@ -1295,7 +1301,8 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) return DoS(100, error("ConnectBlock() : too many sigops")); CDiskTxPos posThisTx(pindex->nFile, pindex->nBlockPos, nTxPos); - nTxPos += ::GetSerializeSize(tx, SER_DISK); + if (!fJustCheck) + nTxPos += ::GetSerializeSize(tx, SER_DISK); MapPrevTx mapInputs; if (!tx.IsCoinBase()) @@ -1323,6 +1330,12 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) mapQueuedChanges[tx.GetHash()] = CTxIndex(posThisTx, tx.vout.size()); } + if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) + return false; + + if (fJustCheck) + return true; + // Write queued txindex changes for (map::iterator mi = mapQueuedChanges.begin(); mi != mapQueuedChanges.end(); ++mi) { @@ -1330,9 +1343,6 @@ bool CBlock::ConnectBlock(CTxDB& txdb, CBlockIndex* pindex) return error("ConnectBlock() : UpdateTxIndex failed"); } - if (vtx[0].GetValueOut() > GetBlockValue(pindex->nHeight, nFees)) - return false; - // Update block index on disk without changing it in memory. // The memory index structure will be changed after the db commits. if (pindex->pprev) @@ -1619,7 +1629,7 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos) -bool CBlock::CheckBlock() const +bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot) const { // These are checks that are independent of context // that can be verified before saving an orphan block. @@ -1629,7 +1639,7 @@ bool CBlock::CheckBlock() const return DoS(100, error("CheckBlock() : size limits failed")); // Check proof of work matches claimed amount - if (!CheckProofOfWork(GetHash(), nBits)) + if (fCheckPOW && !CheckProofOfWork(GetHash(), nBits)) return DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp @@ -1657,7 +1667,7 @@ bool CBlock::CheckBlock() const return DoS(100, error("CheckBlock() : out-of-bounds SigOpCount")); // Check merkleroot - if (hashMerkleRoot != BuildMerkleTree()) + if (fCheckMerkleRoot && hashMerkleRoot != BuildMerkleTree()) return DoS(100, error("CheckBlock() : hashMerkleRoot mismatch")); return true; @@ -3066,6 +3076,9 @@ public: uint64 nLastBlockTx = 0; uint64 nLastBlockSize = 0; +const char* pszDummy = "\0\0"; +CScript scriptDummy(std::vector(pszDummy, pszDummy + sizeof(pszDummy))); + CBlock* CreateNewBlock(CReserveKey& reservekey) { CBlockIndex* pindexPrev = pindexBest; @@ -3224,16 +3237,22 @@ CBlock* CreateNewBlock(CReserveKey& reservekey) nLastBlockSize = nBlockSize; printf("CreateNewBlock(): total size %lu\n", nBlockSize); - } pblock->vtx[0].vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees); // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - pblock->hashMerkleRoot = pblock->BuildMerkleTree(); pblock->UpdateTime(pindexPrev); pblock->nBits = GetNextWorkRequired(pindexPrev, pblock.get()); pblock->nNonce = 0; + pblock->vtx[0].vin[0].scriptSig = scriptDummy; + CBlockIndex indexDummy(1, 1, *pblock); + indexDummy.pprev = pindexPrev; + indexDummy.nHeight = pindexPrev->nHeight + 1; + if (!pblock->ConnectBlock(txdb, &indexDummy, true)) + throw std::runtime_error("CreateNewBlock() : ConnectBlock failed"); + } + return pblock.release(); } diff --git a/src/main.h b/src/main.h index 18d5dbdd..996b727b 100644 --- a/src/main.h +++ b/src/main.h @@ -1017,11 +1017,11 @@ public: bool DisconnectBlock(CTxDB& txdb, CBlockIndex* pindex); - bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex); + bool ConnectBlock(CTxDB& txdb, CBlockIndex* pindex, bool fJustCheck=false); bool ReadFromDisk(const CBlockIndex* pindex, bool fReadTransactions=true); bool SetBestChain(CTxDB& txdb, CBlockIndex* pindexNew); bool AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos); - bool CheckBlock() const; + bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true) const; bool AcceptBlock(); private: