mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-19 09:53:47 +01:00
Merge bitcoin/bitcoin#26909: net: prevent peers.dat corruptions by only serializing once
5eabb61b23
addrdb: Only call Serialize() once (Martin Zumsande)da6c7aeca3
hash: add HashedSourceWriter (Martin Zumsande) Pull request description: There have been various reports of corruption of `peers.dat` recently, see #26599. As explained in [this post](https://github.com/bitcoin/bitcoin/issues/26599#issuecomment-1381082886) in more detail, the underlying issue is likely that we currently serialize `AddrMan` twice - once for the file stream, once for the hasher that helps create the checksum - and if `AddrMan` changes in between these two calls, the checksum doesn't match the data and the resulting `peers.dat` is corrupted. This PR attempts to fix this by introducing and using `HashedSourceWriter` - a class that keeps a running hash while serializing data, similar to the existing `CHashVerifier` which does the analogous thing while unserializing data. Something like this was suggested before, see https://github.com/bitcoin/bitcoin/pull/10248#discussion_r120694343. Fixes #26599 (not by changing the behavior in case of a crash, but by hopefully fixing the underlying cause of these corruptions). ACKs for top commit: sipa: utACK5eabb61b23
naumenkogs: utACK5eabb61b23
Tree-SHA512: f19ad37c37088d3a9825c60de2efe85cc2b7a21b79b9156024d33439e021443ef977a5f8424a7981bcc13d73d11e30eaa82de60e14d88b3568a67b03e02b153b
This commit is contained in:
commit
b5c88a5479
@ -34,10 +34,9 @@ bool SerializeDB(Stream& stream, const Data& data)
|
||||
{
|
||||
// Write and commit header, data
|
||||
try {
|
||||
CHashWriter hasher(stream.GetType(), stream.GetVersion());
|
||||
stream << Params().MessageStart() << data;
|
||||
hasher << Params().MessageStart() << data;
|
||||
stream << hasher.GetHash();
|
||||
HashedSourceWriter hashwriter{stream};
|
||||
hashwriter << Params().MessageStart() << data;
|
||||
stream << hashwriter.GetHash();
|
||||
} catch (const std::exception& e) {
|
||||
return error("%s: Serialize or I/O error - %s", __func__, e.what());
|
||||
}
|
||||
|
@ -1178,8 +1178,7 @@ void AddrMan::Unserialize(Stream& s_)
|
||||
}
|
||||
|
||||
// explicit instantiation
|
||||
template void AddrMan::Serialize(CHashWriter& s) const;
|
||||
template void AddrMan::Serialize(CAutoFile& s) const;
|
||||
template void AddrMan::Serialize(HashedSourceWriter<CAutoFile>& s) const;
|
||||
template void AddrMan::Serialize(CDataStream& s) const;
|
||||
template void AddrMan::Unserialize(CAutoFile& s);
|
||||
template void AddrMan::Unserialize(CHashVerifier<CAutoFile>& s);
|
||||
|
25
src/hash.h
25
src/hash.h
@ -6,6 +6,7 @@
|
||||
#ifndef BITCOIN_HASH_H
|
||||
#define BITCOIN_HASH_H
|
||||
|
||||
#include <attributes.h>
|
||||
#include <crypto/common.h>
|
||||
#include <crypto/ripemd160.h>
|
||||
#include <crypto/sha256.h>
|
||||
@ -199,6 +200,30 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/** Writes data to an underlying source stream, while hashing the written data. */
|
||||
template <typename Source>
|
||||
class HashedSourceWriter : public CHashWriter
|
||||
{
|
||||
private:
|
||||
Source& m_source;
|
||||
|
||||
public:
|
||||
explicit HashedSourceWriter(Source& source LIFETIMEBOUND) : CHashWriter{source.GetType(), source.GetVersion()}, m_source{source} {}
|
||||
|
||||
void write(Span<const std::byte> src)
|
||||
{
|
||||
m_source.write(src);
|
||||
CHashWriter::write(src);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HashedSourceWriter& operator<<(const T& obj)
|
||||
{
|
||||
::Serialize(*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)
|
||||
|
@ -500,4 +500,18 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
|
||||
fs::remove(streams_test_filename);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(streams_hashed)
|
||||
{
|
||||
CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);
|
||||
HashedSourceWriter hash_writer{stream};
|
||||
const std::string data{"bitcoin"};
|
||||
hash_writer << data;
|
||||
|
||||
CHashVerifier hash_verifier{&stream};
|
||||
std::string result;
|
||||
hash_verifier >> result;
|
||||
BOOST_CHECK_EQUAL(data, result);
|
||||
BOOST_CHECK_EQUAL(hash_writer.GetHash(), hash_verifier.GetHash());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
Loading…
Reference in New Issue
Block a user