From da6c7aeca38e1d0ab5839a374c26af0504d603fc Mon Sep 17 00:00:00 2001 From: Martin Zumsande Date: Sun, 15 Jan 2023 20:18:11 -0500 Subject: [PATCH 1/2] hash: add HashedSourceWriter This class is the counterpart to CHashVerifier, in that it writes data to an underlying source stream, while keeping a hash of the written data. --- src/hash.h | 25 +++++++++++++++++++++++++ src/test/streams_tests.cpp | 14 ++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/src/hash.h b/src/hash.h index b18a031268..6500f1c709 100644 --- a/src/hash.h +++ b/src/hash.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_HASH_H #define BITCOIN_HASH_H +#include #include #include #include @@ -199,6 +200,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 dce230ac10..03117f76ae 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -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() From 5eabb61b2386d00e93e6bbb2f493a56d1b326ad9 Mon Sep 17 00:00:00 2001 From: Martin Zumsande Date: Fri, 13 Jan 2023 14:12:25 -0500 Subject: [PATCH 2/2] addrdb: Only call Serialize() once The previous logic would call it once for serializing into the filestream, and then again for serializing into the hasher. If AddrMan was changed in between these calls by another thread, the resulting peers.dat would be corrupt with non-matching checksum and data. Fix this by using HashedSourceWriter, which writes the data to the underlying stream and keeps track of the hash in one go. --- src/addrdb.cpp | 7 +++---- src/addrman.cpp | 3 +-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/addrdb.cpp b/src/addrdb.cpp index d95c07d6a8..7c954dbf3a 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -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()); } diff --git a/src/addrman.cpp b/src/addrman.cpp index 91eedeebe1..f86bdfa3df 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -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& s) const; template void AddrMan::Serialize(CDataStream& s) const; template void AddrMan::Unserialize(CAutoFile& s); template void AddrMan::Unserialize(CHashVerifier& s);