From 8539361e66eace839060f77f39b770d045e7ce28 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sun, 30 Dec 2012 23:41:41 +0100 Subject: [PATCH] Add checksums to undo data This should be compatible with older code that didn't write checksums. --- src/main.cpp | 22 +++++++--------------- src/main.h | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index bb39f422f9..2619062a6b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1474,19 +1474,11 @@ bool CBlock::DisconnectBlock(CBlockIndex *pindex, CCoinsViewCache &view, bool *p bool fClean = true; CBlockUndo blockUndo; - { - CDiskBlockPos pos = pindex->GetUndoPos(); - if (pos.IsNull()) - return error("DisconnectBlock() : no undo data available"); - CAutoFile fileUndo(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); - if (!fileUndo) - return error("DisconnectBlock() : cannot open undo file"); - try { - fileUndo >> blockUndo; - } catch(std::exception &e) { - return error("DisconnectBlock() : deserialize or I/O error reading udno data"); - } - } + CDiskBlockPos pos = pindex->GetUndoPos(); + if (pos.IsNull()) + return error("DisconnectBlock() : no undo data available"); + if (!blockUndo.ReadFromDisk(pos, pindex->pprev->GetBlockHash())) + return error("DisconnectBlock() : failure reading undo data"); if (blockUndo.vtxundo.size() + 1 != vtx.size()) return error("DisconnectBlock() : block and undo data inconsistent"); @@ -1670,9 +1662,9 @@ bool CBlock::ConnectBlock(CBlockIndex* pindex, CCoinsViewCache &view, bool fJust { if (pindex->GetUndoPos().IsNull()) { CDiskBlockPos pos; - if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 8)) + if (!FindUndoPos(pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock() : FindUndoPos failed"); - if (!blockundo.WriteToDisk(pos)) + if (!blockundo.WriteToDisk(pos, pindex->pprev->GetBlockHash())) return error("ConnectBlock() : CBlockUndo::WriteToDisk failed"); // update nUndoPos in block index diff --git a/src/main.h b/src/main.h index 80fd76452b..557df8bc5f 100644 --- a/src/main.h +++ b/src/main.h @@ -746,7 +746,7 @@ public: READWRITE(vtxundo); ) - bool WriteToDisk(CDiskBlockPos &pos) + bool WriteToDisk(CDiskBlockPos &pos, const uint256 &hashBlock) { // Open history file to append CAutoFile fileout = CAutoFile(OpenUndoFile(pos), SER_DISK, CLIENT_VERSION); @@ -764,6 +764,12 @@ public: pos.nPos = (unsigned int)fileOutPos; fileout << *this; + // calculate & write checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + fileout << hasher.GetHash(); + // Flush stdio buffers and commit to disk before returning fflush(fileout); if (!IsInitialBlockDownload()) @@ -771,6 +777,44 @@ public: return true; } + + bool ReadFromDisk(const CDiskBlockPos &pos, const uint256 &hashBlock) + { + // Open history file to read + CAutoFile filein = CAutoFile(OpenUndoFile(pos, true), SER_DISK, CLIENT_VERSION); + if (!filein) + return error("CBlockUndo::ReadFromDisk() : OpenBlockFile failed"); + + // Read block + uint256 hashChecksum; + try { + filein >> *this; + } + catch (std::exception &e) { + return error("%s() : deserialize or I/O error", __PRETTY_FUNCTION__); + } + + // for compatibility with pre-release code that didn't write checksums to undo data + // TODO: replace by a simply 'filein >> hashChecksum' in the above try block + try { + filein >> hashChecksum; + } catch (std::exception &e) { + hashChecksum = 0; + } + uint32_t hashInit = hashChecksum.Get64(0) & 0xFFFFFFFFUL; + if (hashChecksum == 0 || memcmp(&hashInit, pchMessageStart, 4) == 0) + return true; + + // Verify checksum + CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION); + hasher << hashBlock; + hasher << *this; + if (hashChecksum != hasher.GetHash()) + return error("CBlockUndo::ReadFromDisk() : checksum mismatch"); + + return true; + } + }; /** pruned version of CTransaction: only retains metadata and unspent transaction outputs