Skip to content

Commit 1f00538

Browse files
Merge dashpay#6084: fix: backport bitcoin#26909, allow for silent overwriting of inconsistent peers.dat
adba609 addrman: allow for silent overwriting of inconsistent peers.dat (Kittywhiskers Van Gogh) fbb2b51 merge bitcoin#26909: prevent peers.dat corruptions by only serializing once (Kittywhiskers Van Gogh) Pull request description: ## Additional Information [bitcoin#22762](bitcoin#22762) (backported as part of [dash#6043](dashpay#6043)) did away with then-existing behaviour of overwriting `peers.dat` silently if found corrupt with the rationale of preventing situations where the wrong file is pointed at or the file is written by a higher version of Core. Alongside a change in behaviour, refactoring also took place and further changes were built on top of them. Since then, there have been reports of an increasing number of "Corrupt data. Consistency check failed with code -5: iostream error" errors from builds based on `develop`. Reverting the pull request that introduced this change in behaviour is non-trivial due to the number of backports that build on top of the refactoring brought along with it. Nor were any other error messages found except for the one mentioned above. The tendency for `peers.dat` to corrupt itself has also been documented upstream ([bitcoin#26599](bitcoin#26599)), with the issue marked as closed with the merger of [bitcoin#26909](bitcoin#26909). Therefore, to remedy the above problem, alongside backporting [bitcoin#26909](bitcoin#26909), to avoid inconvenience, instead of reverting all progress made from backporting (as the benefits of not overwriting `peers.dat` for having the wrong magic altogether, for example, is something that doesn't need to be reverted), only inconsistent `peers.dat` files will be overwritten and the action logged with no user intervention required. ## Breaking Changes None expected. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK adba609 knst: utACK adba609 PastaPastaPasta: utACK adba609 Tree-SHA512: 3e09e7a77c82cce333fe9f02f137485e362f7816c450aef3d18950b9fd57276b4a21cbd1fe90b3eefd62ede0947970ed367c5917930a0656833bc38c0629e408
2 parents 2b95b60 + adba609 commit 1f00538

File tree

6 files changed

+64
-11
lines changed

6 files changed

+64
-11
lines changed

src/addrdb.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,9 @@ bool SerializeDB(Stream& stream, const Data& data)
3333
{
3434
// Write and commit header, data
3535
try {
36-
CHashWriter hasher(stream.GetType(), stream.GetVersion());
37-
stream << Params().MessageStart() << data;
38-
hasher << Params().MessageStart() << data;
39-
stream << hasher.GetHash();
36+
HashedSourceWriter hashwriter{stream};
37+
hashwriter << Params().MessageStart() << data;
38+
stream << hashwriter.GetHash();
4039
} catch (const std::exception& e) {
4140
return error("%s: Serialize or I/O error - %s", __func__, e.what());
4241
}
@@ -197,6 +196,15 @@ std::optional<bilingual_str> LoadAddrman(const std::vector<bool>& asmap, const A
197196
addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
198197
LogPrintf("Creating peers.dat because the file was not found (%s)\n", path_addr);
199198
DumpPeerAddresses(args, *addrman);
199+
} catch (const DbInconsistentError& e) {
200+
// Addrman has shown a tendency to corrupt itself even with graceful shutdowns on known-good
201+
// hardware. As the user would have to delete and recreate a new database regardless to cope
202+
// with frequent corruption, we are restoring old behaviour that does the same, silently.
203+
//
204+
// TODO: Evaluate cause and fix, revert this change at some point.
205+
addrman = std::make_unique<AddrMan>(asmap, /* deterministic */ false, /* consistency_check_ratio */ check_addrman);
206+
LogPrintf("Creating peers.dat because of invalid or corrupt file (%s)\n", e.what());
207+
DumpPeerAddresses(args, *addrman);
200208
} catch (const std::exception& e) {
201209
addrman = nullptr;
202210
return strprintf(_("Invalid or corrupt peers.dat (%s). If you believe this is a bug, please report it to %s. As a workaround, you can move the file (%s) out of the way (rename, move, or delete) to have a new one created on the next start."),

src/addrman.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ void AddrManImpl::Unserialize(Stream& s_)
391391

392392
const int check_code{ForceCheckAddrman()};
393393
if (check_code != 0) {
394-
throw std::ios_base::failure(strprintf(
394+
throw DbInconsistentError(strprintf(
395395
"Corrupt data. Consistency check failed with code %s",
396396
check_code));
397397
}
@@ -1168,8 +1168,7 @@ void AddrMan::Unserialize(Stream& s_)
11681168
}
11691169

11701170
// explicit instantiation
1171-
template void AddrMan::Serialize(CHashWriter& s) const;
1172-
template void AddrMan::Serialize(CAutoFile& s) const;
1171+
template void AddrMan::Serialize(HashedSourceWriter<CAutoFile>& s) const;
11731172
template void AddrMan::Serialize(CDataStream& s) const;
11741173
template void AddrMan::Unserialize(CAutoFile& s);
11751174
template void AddrMan::Unserialize(CHashVerifier<CAutoFile>& s);

src/addrman.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ class AddrManImpl;
2323
/** Default for -checkaddrman */
2424
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS{0};
2525

26+
class DbInconsistentError : public std::exception
27+
{
28+
using std::exception::exception;
29+
const std::string error;
30+
31+
public:
32+
explicit DbInconsistentError(const std::string _error) : error{_error} {}
33+
const char* what() const noexcept override { return error.c_str(); }
34+
};
35+
2636
/** Stochastic address manager
2737
*
2838
* Design goals:

src/hash.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,30 @@ class CHashVerifier : public CHashWriter
201201
}
202202
};
203203

204+
/** Writes data to an underlying source stream, while hashing the written data. */
205+
template <typename Source>
206+
class HashedSourceWriter : public CHashWriter
207+
{
208+
private:
209+
Source& m_source;
210+
211+
public:
212+
explicit HashedSourceWriter(Source& source LIFETIMEBOUND) : CHashWriter{source.GetType(), source.GetVersion()}, m_source{source} {}
213+
214+
void write(Span<const std::byte> src)
215+
{
216+
m_source.write(src);
217+
CHashWriter::write(src);
218+
}
219+
220+
template <typename T>
221+
HashedSourceWriter& operator<<(const T& obj)
222+
{
223+
::Serialize(*this, obj);
224+
return *this;
225+
}
226+
};
227+
204228
/** Compute the 256-bit hash of an object's serialization. */
205229
template<typename T>
206230
uint256 SerializeHash(const T& obj, int nType=SER_GETHASH, int nVersion=PROTOCOL_VERSION)

src/test/streams_tests.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,4 +437,18 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
437437
fs::remove("streams_test_tmp");
438438
}
439439

440+
BOOST_AUTO_TEST_CASE(streams_hashed)
441+
{
442+
CDataStream stream(SER_NETWORK, INIT_PROTO_VERSION);
443+
HashedSourceWriter hash_writer{stream};
444+
const std::string data{"bitcoin"};
445+
hash_writer << data;
446+
447+
CHashVerifier hash_verifier{&stream};
448+
std::string result;
449+
hash_verifier >> result;
450+
BOOST_CHECK_EQUAL(data, result);
451+
BOOST_CHECK_EQUAL(hash_writer.GetHash(), hash_verifier.GetHash());
452+
}
453+
440454
BOOST_AUTO_TEST_SUITE_END()

test/functional/feature_addrman.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,8 @@ def run_test(self):
123123
self.log.info("Check that corrupt addrman cannot be read (failed check)")
124124
self.stop_node(0)
125125
write_addrman(peers_dat, bucket_key=0)
126-
self.nodes[0].assert_start_raises_init_error(
127-
expected_msg=init_error("Corrupt data. Consistency check failed with code -16: .*"),
128-
match=ErrorMatch.FULL_REGEX,
129-
)
126+
with self.nodes[0].assert_debug_log(['Creating peers.dat because of invalid or corrupt file']):
127+
self.start_node(0)
130128

131129
self.log.info("Check that missing addrman is recreated")
132130
self.stop_node(0)

0 commit comments

Comments
 (0)