aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>2023-01-19 16:02:54 +0100
committerMarcoFalke <*~=`'#}+{/-|&$^_@721217.xyz>2023-01-19 16:03:08 +0100
commitb5c88a547996776dbdc2e101bae9b67ac639fd02 (patch)
tree78389772458a6b8d9efe0ca91d8d140bef2be0a8 /src
parent05e3468fb3c084840a93aed31c67f062ef30ef58 (diff)
parent5eabb61b2386d00e93e6bbb2f493a56d1b326ad9 (diff)
Merge bitcoin/bitcoin#26909: net: prevent peers.dat corruptions by only serializing once
5eabb61b2386d00e93e6bbb2f493a56d1b326ad9 addrdb: Only call Serialize() once (Martin Zumsande) da6c7aeca38e1d0ab5839a374c26af0504d603fc 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: utACK 5eabb61b2386d00e93e6bbb2f493a56d1b326ad9 naumenkogs: utACK 5eabb61b2386d00e93e6bbb2f493a56d1b326ad9 Tree-SHA512: f19ad37c37088d3a9825c60de2efe85cc2b7a21b79b9156024d33439e021443ef977a5f8424a7981bcc13d73d11e30eaa82de60e14d88b3568a67b03e02b153b
Diffstat (limited to 'src')
-rw-r--r--src/addrdb.cpp7
-rw-r--r--src/addrman.cpp3
-rw-r--r--src/hash.h25
-rw-r--r--src/test/streams_tests.cpp14
4 files changed, 43 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<CAutoFile>& s) const;
template void AddrMan::Serialize(CDataStream& s) const;
template void AddrMan::Unserialize(CAutoFile& s);
template void AddrMan::Unserialize(CHashVerifier<CAutoFile>& s);
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 <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)
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()