From fbb2b51d75f281921fd6b7b3423afd25f73a324d Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 15 Jan 2023 20:18:11 -0500 Subject: [PATCH] merge bitcoin#26909: prevent peers.dat corruptions by only serializing once --- src/addrdb.cpp | 7 +++---- src/addrman.cpp | 3 +-- src/hash.h | 24 ++++++++++++++++++++++++ src/test/streams_tests.cpp | 14 ++++++++++++++ 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/addrdb.cpp b/src/addrdb.cpp index 940f03193c..4de03a8e1e 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -33,10 +33,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()); } diff --git a/src/addrman.cpp b/src/addrman.cpp index f756be4bc3..951c71841b 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -1168,8 +1168,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& s) const; template void AddrMan::Serialize(CDataStream& s) const; template void AddrMan::Unserialize(CAutoFile& s); template void AddrMan::Unserialize(CHashVerifier& s); diff --git a/src/hash.h b/src/hash.h index 8e1b73707c..8c491ef3bc 100644 --- a/src/hash.h +++ b/src/hash.h @@ -201,6 +201,30 @@ public: } }; +/** Writes data to an underlying source stream, while hashing the written data. */ +template +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 src) + { + m_source.write(src); + CHashWriter::write(src); + } + + template + HashedSourceWriter& operator<<(const T& obj) + { + ::Serialize(*this, obj); + return *this; + } +}; + /** Compute the 256-bit hash of an object's serialization. */ template uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION) diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 1c604f52f6..d569a04207 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -437,4 +437,18 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_rand) fs::remove("streams_test_tmp"); } +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()