Introduce CHashVerifier to hash read data

This is necessary later, when we drop the nVersion field from the undo
data. At that point deserializing and reserializing the data won't
roundtrip anymore, and thus that approach can't be used to verify
checksums anymore.

With this CHashVerifier approach, we can deserialize while hashing the
exact serialized form that was used. This is both more efficient and
more correct in that case.
This commit is contained in:
Pieter Wuille 2017-04-25 11:29:14 -07:00
parent f54580e7e4
commit e484652fc3
2 changed files with 39 additions and 5 deletions

View file

@ -160,6 +160,41 @@ public:
}
};
/** Reads data from an underlying stream, while hashing the read data. */
template<typename Source>
class CHashVerifier : public CHashWriter
{
private:
Source* source;
public:
CHashVerifier(Source* source_) : CHashWriter(source_->GetType(), source_->GetVersion()), source(source_) {}
void read(char* pch, size_t nSize)
{
source->read(pch, nSize);
this->write(pch, nSize);
}
void ignore(size_t nSize)
{
char data[1024];
while (nSize > 0) {
size_t now = std::min<size_t>(nSize, 1024);
read(data, now);
nSize -= now;
}
}
template<typename T>
CHashVerifier<Source>& operator>>(T& obj)
{
// Unserialize from this stream
::Unserialize(*this, obj);
return (*this);
}
};
/** Compute the 256-bit hash of an object's serialization. */
template<typename T>
uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)

View file

@ -1210,8 +1210,10 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin
// Read block
uint256 hashChecksum;
CHashVerifier<CAutoFile> verifier(&filein); // We need a CHashVerifier as reserializing may lose data
try {
filein >> blockundo;
verifier << hashBlock;
verifier >> blockundo;
filein >> hashChecksum;
}
catch (const std::exception& e) {
@ -1219,10 +1221,7 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CDiskBlockPos& pos, const uin
}
// Verify checksum
CHashWriter hasher(SER_GETHASH, PROTOCOL_VERSION);
hasher << hashBlock;
hasher << blockundo;
if (hashChecksum != hasher.GetHash())
if (hashChecksum != verifier.GetHash())
return error("%s: Checksum mismatch", __func__);
return true;