Skip to content

Commit 803ef70

Browse files
committed
Merge bitcoin/bitcoin#20233: addrman: Make consistency checks a runtime option
a4d7854 [addrman] Make addrman consistency checks a runtime option (John Newbery) 10aac24 [tests] Make deterministic addrman use nKey = 1 (John Newbery) fa9710f [addrman] Add deterministic argument to CAddrMan ctor (John Newbery) ee458d8 Add missing const to CAddrMan::Check_() (MarcoFalke) Pull request description: CAddrMan has internal consistency checks. Currently, these are only run when the program is compiled with the `DEBUG_ADDRMAN` option. This option is not enabled on any of our CI builds, and it's likely that no-one is running them at all. This PR makes consistency checks a (hidden) runtime option that can be enabled with `-checkaddrman`, where `-checkaddrman=n` will result in the consistency checks running every n operations (similar to `-checkmempool=n`). We set the ratio to 1/100 for our unit tests, and leave it disabled by default for all networks. Additionally, a consistency check failure now asserts, rather than logging and continuing. This matches the behavior of CTxMemPool and TxRequestTracker, where a failed consistency check asserts. ACKs for top commit: jonatack: ACK a4d7854 per `git diff 00fd089 a4d7854`, tested by adding logging similar to #22479 and running with `-checkaddrman=<n>` for various values 0/1/10/100 etc, tested the updated docs with `bitcoind -help-debug | grep -A2 "checkaddrman\|checkmempool"` and verified rebased on master that compiling with `CPPFLAGS="-DDEBUG_ADDRMAN"` no longer causes the build to error. mzumsande: Code-review ACK a4d7854 theStack: Code-review ACK a4d7854 Tree-SHA512: eaee003f7a99154822c5b5efbc62008d32c1efbecc6fec6e183427f6b2ae5d30b3be7924e3a7271b1a1de91517f5bd2a70011d45358c3105c6a0702f12b70f7c
2 parents 439e58c + a4d7854 commit 803ef70

File tree

11 files changed

+113
-104
lines changed

11 files changed

+113
-104
lines changed

src/addrman.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -431,11 +431,14 @@ CAddrInfo CAddrMan::Select_(bool newOnly) const
431431
}
432432
}
433433

434-
#ifdef DEBUG_ADDRMAN
435-
int CAddrMan::Check_()
434+
int CAddrMan::Check_() const
436435
{
437436
AssertLockHeld(cs);
438437

438+
// Run consistency checks 1 in m_consistency_check_ratio times if enabled
439+
if (m_consistency_check_ratio == 0) return 0;
440+
if (insecure_rand.randrange(m_consistency_check_ratio) >= 1) return 0;
441+
439442
std::unordered_set<int> setTried;
440443
std::unordered_map<int, int> mapNew;
441444

@@ -458,8 +461,10 @@ int CAddrMan::Check_()
458461
return -4;
459462
mapNew[n] = info.nRefCount;
460463
}
461-
if (mapAddr[info] != n)
464+
const auto it{mapAddr.find(info)};
465+
if (it == mapAddr.end() || it->second != n) {
462466
return -5;
467+
}
463468
if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
464469
return -14;
465470
if (info.nLastTry < 0)
@@ -478,10 +483,13 @@ int CAddrMan::Check_()
478483
if (vvTried[n][i] != -1) {
479484
if (!setTried.count(vvTried[n][i]))
480485
return -11;
481-
if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey, m_asmap) != n)
486+
const auto it{mapInfo.find(vvTried[n][i])};
487+
if (it == mapInfo.end() || it->second.GetTriedBucket(nKey, m_asmap) != n) {
482488
return -17;
483-
if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i)
489+
}
490+
if (it->second.GetBucketPosition(nKey, false, n) != i) {
484491
return -18;
492+
}
485493
setTried.erase(vvTried[n][i]);
486494
}
487495
}
@@ -492,8 +500,10 @@ int CAddrMan::Check_()
492500
if (vvNew[n][i] != -1) {
493501
if (!mapNew.count(vvNew[n][i]))
494502
return -12;
495-
if (mapInfo[vvNew[n][i]].GetBucketPosition(nKey, true, n) != i)
503+
const auto it{mapInfo.find(vvNew[n][i])};
504+
if (it == mapInfo.end() || it->second.GetBucketPosition(nKey, true, n) != i) {
496505
return -19;
506+
}
497507
if (--mapNew[vvNew[n][i]] == 0)
498508
mapNew.erase(vvNew[n][i]);
499509
}
@@ -509,7 +519,6 @@ int CAddrMan::Check_()
509519

510520
return 0;
511521
}
512-
#endif
513522

514523
void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network) const
515524
{

src/addrman.h

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#include <unordered_map>
2727
#include <vector>
2828

29+
/** Default for -checkaddrman */
30+
static constexpr int32_t DEFAULT_ADDRMAN_CONSISTENCY_CHECKS{0};
31+
2932
/**
3033
* Extended statistics about a CAddress
3134
*/
@@ -124,8 +127,8 @@ class CAddrInfo : public CAddress
124127
* attempt was unsuccessful.
125128
* * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not
126129
* be observable by adversaries.
127-
* * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN will introduce frequent (and expensive)
128-
* consistency checks for the entire data structure.
130+
* * Several indexes are kept for high performance. Setting m_consistency_check_ratio with the -checkaddrman
131+
* configuration option will introduce (expensive) consistency checks for the entire data structure.
129132
*/
130133

131134
//! total number of buckets for tried addresses
@@ -493,9 +496,12 @@ class CAddrMan
493496
mapAddr.clear();
494497
}
495498

496-
CAddrMan()
499+
explicit CAddrMan(bool deterministic, int32_t consistency_check_ratio)
500+
: insecure_rand{deterministic},
501+
m_consistency_check_ratio{consistency_check_ratio}
497502
{
498503
Clear();
504+
if (deterministic) nKey = uint256{1};
499505
}
500506

501507
~CAddrMan()
@@ -637,13 +643,13 @@ class CAddrMan
637643
//! secret key to randomize bucket select with
638644
uint256 nKey;
639645

640-
//! Source of random numbers for randomization in inner loops
641-
mutable FastRandomContext insecure_rand GUARDED_BY(cs);
642-
643646
//! A mutex to protect the inner data structures.
644647
mutable Mutex cs;
645648

646649
private:
650+
//! Source of random numbers for randomization in inner loops
651+
mutable FastRandomContext insecure_rand GUARDED_BY(cs);
652+
647653
//! Serialization versions.
648654
enum Format : uint8_t {
649655
V0_HISTORICAL = 0, //!< historic format, before commit e6b343d88
@@ -698,6 +704,9 @@ class CAddrMan
698704
//! Holds addrs inserted into tried table that collide with existing entries. Test-before-evict discipline used to resolve these collisions.
699705
std::set<int> m_tried_collisions;
700706

707+
/** Perform consistency checks every m_consistency_check_ratio operations (if non-zero). */
708+
const int32_t m_consistency_check_ratio;
709+
701710
//! Find an entry.
702711
CAddrInfo* Find(const CNetAddr& addr, int *pnId = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs);
703712

@@ -735,22 +744,19 @@ class CAddrMan
735744
CAddrInfo SelectTriedCollision_() EXCLUSIVE_LOCKS_REQUIRED(cs);
736745

737746
//! Consistency check
738-
void Check() const
739-
EXCLUSIVE_LOCKS_REQUIRED(cs)
747+
void Check() const EXCLUSIVE_LOCKS_REQUIRED(cs)
740748
{
741-
#ifdef DEBUG_ADDRMAN
742749
AssertLockHeld(cs);
750+
743751
const int err = Check_();
744752
if (err) {
745753
LogPrintf("ADDRMAN CONSISTENCY CHECK FAILED!!! err=%i\n", err);
754+
assert(false);
746755
}
747-
#endif
748756
}
749757

750-
#ifdef DEBUG_ADDRMAN
751758
//! Perform consistency check. Returns an error code or zero.
752759
int Check_() const EXCLUSIVE_LOCKS_REQUIRED(cs);
753-
#endif
754760

755761
/**
756762
* Return all or many randomly selected addresses, optionally by network.

src/bench/addrman.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ static void AddrManAdd(benchmark::Bench& bench)
7272
{
7373
CreateAddresses();
7474

75-
CAddrMan addrman;
75+
CAddrMan addrman(/* deterministic */ false, /* consistency_check_ratio */ 0);
7676

7777
bench.run([&] {
7878
AddAddressesToAddrMan(addrman);
@@ -82,7 +82,7 @@ static void AddrManAdd(benchmark::Bench& bench)
8282

8383
static void AddrManSelect(benchmark::Bench& bench)
8484
{
85-
CAddrMan addrman;
85+
CAddrMan addrman(/* deterministic */ false, /* consistency_check_ratio */ 0);
8686

8787
FillAddrMan(addrman);
8888

@@ -94,7 +94,7 @@ static void AddrManSelect(benchmark::Bench& bench)
9494

9595
static void AddrManGetAddr(benchmark::Bench& bench)
9696
{
97-
CAddrMan addrman;
97+
CAddrMan addrman(/* deterministic */ false, /* consistency_check_ratio */ 0);
9898

9999
FillAddrMan(addrman);
100100

@@ -112,10 +112,12 @@ static void AddrManGood(benchmark::Bench& bench)
112112
* we want to do the same amount of work in every loop iteration. */
113113

114114
bench.epochs(5).epochIterations(1);
115+
const size_t addrman_count{bench.epochs() * bench.epochIterations()};
115116

116-
std::vector<CAddrMan> addrmans(bench.epochs() * bench.epochIterations());
117-
for (auto& addrman : addrmans) {
118-
FillAddrMan(addrman);
117+
std::vector<std::unique_ptr<CAddrMan>> addrmans(addrman_count);
118+
for (size_t i{0}; i < addrman_count; ++i) {
119+
addrmans[i] = std::make_unique<CAddrMan>(/* deterministic */ false, /* consistency_check_ratio */ 0);
120+
FillAddrMan(*addrmans[i]);
119121
}
120122

121123
auto markSomeAsGood = [](CAddrMan& addrman) {
@@ -130,7 +132,7 @@ static void AddrManGood(benchmark::Bench& bench)
130132

131133
uint64_t i = 0;
132134
bench.run([&] {
133-
markSomeAsGood(addrmans.at(i));
135+
markSomeAsGood(*addrmans.at(i));
134136
++i;
135137
});
136138
}

src/init.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,8 @@ void SetupServerArgs(ArgsManager& argsman)
501501
argsman.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
502502
argsman.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is: %s (0-4, default: %u)", Join(CHECKLEVEL_DOC, ", "), DEFAULT_CHECKLEVEL), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
503503
argsman.AddArg("-checkblockindex", strprintf("Do a consistency check for the block tree, chainstate, and other validation data structures occasionally. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
504-
argsman.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
504+
argsman.AddArg("-checkaddrman=<n>", strprintf("Run addrman consistency checks every <n> operations. Use 0 to disable. (default: %u)", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
505+
argsman.AddArg("-checkmempool=<n>", strprintf("Run mempool consistency checks every <n> transactions. Use 0 to disable. (default: %u, regtest: %u)", defaultChainParams->DefaultConsistencyChecks(), regtestChainParams->DefaultConsistencyChecks()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
505506
argsman.AddArg("-checkpoints", strprintf("Enable rejection of any forks from the known historical chain until block %s (default: %u)", defaultChainParams->Checkpoints().GetHeight(), DEFAULT_CHECKPOINTS_ENABLED), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
506507
argsman.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
507508
argsman.AddArg("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
@@ -1164,7 +1165,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
11641165
const bool ignores_incoming_txs{args.GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY)};
11651166

11661167
assert(!node.addrman);
1167-
node.addrman = std::make_unique<CAddrMan>();
1168+
auto check_addrman = std::clamp<int32_t>(args.GetArg("-checkaddrman", DEFAULT_ADDRMAN_CONSISTENCY_CHECKS), 0, 1000000);
1169+
node.addrman = std::make_unique<CAddrMan>(/* deterministic */ false, /* consistency_check_ratio */ check_addrman);
11681170
assert(!node.banman);
11691171
node.banman = std::make_unique<BanMan>(gArgs.GetDataDirNet() / "banlist", &uiInterface, args.GetArg("-bantime", DEFAULT_MISBEHAVING_BANTIME));
11701172
assert(!node.connman);

0 commit comments

Comments
 (0)