Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
addrman.cpp
Go to the documentation of this file.
1// Copyright (c) 2020-2022 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <addrdb.h>
6#include <addrman.h>
7#include <addrman_impl.h>
8#include <chainparams.h>
9#include <common/args.h>
10#include <merkleblock.h>
11#include <random.h>
13#include <test/fuzz/fuzz.h>
14#include <test/fuzz/util.h>
15#include <test/fuzz/util/net.h>
17#include <time.h>
18#include <util/asmap.h>
19#include <util/chaintype.h>
20
21#include <cassert>
22#include <cstdint>
23#include <optional>
24#include <string>
25#include <vector>
26
27namespace {
28const BasicTestingSetup* g_setup;
29
30int32_t GetCheckRatio()
31{
32 return std::clamp<int32_t>(g_setup->m_node.args->GetIntArg("-checkaddrman", 0), 0, 1000000);
33}
34} // namespace
35
37{
38 static const auto testing_setup = MakeNoLogFileContext<>(ChainType::REGTEST);
39 g_setup = testing_setup.get();
40}
41
42[[nodiscard]] inline NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider& fuzzed_data_provider) noexcept
43{
44 std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
45 if (!SanityCheckASMap(asmap, 128)) asmap.clear();
46 return NetGroupManager(asmap);
47}
48
49FUZZ_TARGET(data_stream_addr_man, .init = initialize_addrman)
50{
51 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
52 DataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
53 NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
54 AddrMan addr_man(netgroupman, /*deterministic=*/false, GetCheckRatio());
55 try {
56 ReadFromStream(addr_man, data_stream);
57 } catch (const std::exception&) {
58 }
59}
60
64CNetAddr RandAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext& fast_random_context)
65{
66 CNetAddr addr;
67 assert(!addr.IsValid());
68 for (size_t i = 0; i < 8 && !addr.IsValid(); ++i) {
69 if (fuzzed_data_provider.remaining_bytes() > 1 && fuzzed_data_provider.ConsumeBool()) {
70 addr = ConsumeNetAddr(fuzzed_data_provider);
71 } else {
72 addr = ConsumeNetAddr(fuzzed_data_provider, &fast_random_context);
73 }
74 }
75
76 // Return a dummy IPv4 5.5.5.5 if we generated an invalid address.
77 if (!addr.IsValid()) {
78 in_addr v4_addr = {};
79 v4_addr.s_addr = 0x05050505;
80 addr = CNetAddr{v4_addr};
81 }
82
83 return addr;
84}
85
87void FillAddrman(AddrMan& addrman, FuzzedDataProvider& fuzzed_data_provider)
88{
89 // Add a fraction of the addresses to the "tried" table.
90 // 0, 1, 2, 3 corresponding to 0%, 100%, 50%, 33%
91 const size_t n = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 3);
92
93 const size_t num_sources = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50);
94 CNetAddr prev_source;
95 // Generate a FastRandomContext seed to use inside the loops instead of
96 // fuzzed_data_provider. When fuzzed_data_provider is exhausted it
97 // just returns 0.
98 FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
99 for (size_t i = 0; i < num_sources; ++i) {
100 const auto source = RandAddr(fuzzed_data_provider, fast_random_context);
101 const size_t num_addresses = fast_random_context.randrange(500) + 1; // [1..500]
102
103 for (size_t j = 0; j < num_addresses; ++j) {
104 const auto addr = CAddress{CService{RandAddr(fuzzed_data_provider, fast_random_context), 8333}, NODE_NETWORK};
105 const std::chrono::seconds time_penalty{fast_random_context.randrange(100000001)};
106 addrman.Add({addr}, source, time_penalty);
107
108 if (n > 0 && addrman.Size() % n == 0) {
109 addrman.Good(addr, Now<NodeSeconds>());
110 }
111
112 // Add 10% of the addresses from more than one source.
113 if (fast_random_context.randrange(10) == 0 && prev_source.IsValid()) {
114 addrman.Add({addr}, prev_source, time_penalty);
115 }
116 }
117 prev_source = source;
118 }
119}
120
122{
123public:
124 explicit AddrManDeterministic(const NetGroupManager& netgroupman, FuzzedDataProvider& fuzzed_data_provider)
125 : AddrMan(netgroupman, /*deterministic=*/true, GetCheckRatio())
126 {
127 WITH_LOCK(m_impl->cs, m_impl->insecure_rand.Reseed(ConsumeUInt256(fuzzed_data_provider)));
128 }
129
137 bool operator==(const AddrManDeterministic& other) const
138 {
139 LOCK2(m_impl->cs, other.m_impl->cs);
140
141 if (m_impl->mapInfo.size() != other.m_impl->mapInfo.size() || m_impl->nNew != other.m_impl->nNew ||
142 m_impl->nTried != other.m_impl->nTried) {
143 return false;
144 }
145
146 // Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
147 // Keys may be different.
148
149 auto addrinfo_hasher = [](const AddrInfo& a) {
150 CSipHasher hasher(0, 0);
151 auto addr_key = a.GetKey();
152 auto source_key = a.source.GetAddrBytes();
153 hasher.Write(TicksSinceEpoch<std::chrono::seconds>(a.m_last_success));
154 hasher.Write(a.nAttempts);
155 hasher.Write(a.nRefCount);
156 hasher.Write(a.fInTried);
157 hasher.Write(a.GetNetwork());
158 hasher.Write(a.source.GetNetwork());
159 hasher.Write(addr_key.size());
160 hasher.Write(source_key.size());
161 hasher.Write(addr_key);
162 hasher.Write(source_key);
163 return (size_t)hasher.Finalize();
164 };
165
166 auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
167 return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.m_last_success, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) ==
168 std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.m_last_success, rhs.nAttempts, rhs.nRefCount, rhs.fInTried);
169 };
170
171 using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>;
172
173 const size_t num_addresses{m_impl->mapInfo.size()};
174
175 Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
176 for (const auto& [id, addr] : m_impl->mapInfo) {
177 addresses.insert(addr);
178 }
179
180 Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
181 for (const auto& [id, addr] : other.m_impl->mapInfo) {
182 other_addresses.insert(addr);
183 }
184
185 if (addresses != other_addresses) {
186 return false;
187 }
188
189 auto IdsReferToSameAddress = [&](int id, int other_id) EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs, other.m_impl->cs) {
190 if (id == -1 && other_id == -1) {
191 return true;
192 }
193 if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) {
194 return false;
195 }
196 return m_impl->mapInfo.at(id) == other.m_impl->mapInfo.at(other_id);
197 };
198
199 // Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]`
200 // contains just an id and the address is to be found in `mapInfo.at(id)`. The ids
201 // themselves may differ between `vvNew` and `other.vvNew`.
202 for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) {
203 for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
204 if (!IdsReferToSameAddress(m_impl->vvNew[i][j], other.m_impl->vvNew[i][j])) {
205 return false;
206 }
207 }
208 }
209
210 // Same for `vvTried`.
211 for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) {
212 for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
213 if (!IdsReferToSameAddress(m_impl->vvTried[i][j], other.m_impl->vvTried[i][j])) {
214 return false;
215 }
216 }
217 }
218
219 return true;
220 }
221};
222
224{
225 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
226 SetMockTime(ConsumeTime(fuzzed_data_provider));
227 NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
228 auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider);
229 if (fuzzed_data_provider.ConsumeBool()) {
230 const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
231 DataStream ds{serialized_data};
232 try {
233 ds >> *addr_man_ptr;
234 } catch (const std::ios_base::failure&) {
235 addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider);
236 }
237 }
238 AddrManDeterministic& addr_man = *addr_man_ptr;
239 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
240 CallOneOf(
241 fuzzed_data_provider,
242 [&] {
243 addr_man.ResolveCollisions();
244 },
245 [&] {
246 (void)addr_man.SelectTriedCollision();
247 },
248 [&] {
249 std::vector<CAddress> addresses;
250 LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
251 addresses.push_back(ConsumeAddress(fuzzed_data_provider));
252 }
253 addr_man.Add(addresses, ConsumeNetAddr(fuzzed_data_provider), std::chrono::seconds{ConsumeTime(fuzzed_data_provider, 0, 100000000)});
254 },
255 [&] {
256 addr_man.Good(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}});
257 },
258 [&] {
259 addr_man.Attempt(ConsumeService(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool(), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}});
260 },
261 [&] {
262 addr_man.Connected(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}});
263 },
264 [&] {
265 addr_man.SetServices(ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS));
266 });
267 }
268 const AddrMan& const_addr_man{addr_man};
269 std::optional<Network> network;
270 if (fuzzed_data_provider.ConsumeBool()) {
271 network = fuzzed_data_provider.PickValueInArray(ALL_NETWORKS);
272 }
273 (void)const_addr_man.GetAddr(
274 /*max_addresses=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
275 /*max_pct=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
276 network,
277 /*filtered=*/fuzzed_data_provider.ConsumeBool());
278 (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), network);
279 std::optional<bool> in_new;
280 if (fuzzed_data_provider.ConsumeBool()) {
281 in_new = fuzzed_data_provider.ConsumeBool();
282 }
283 (void)const_addr_man.Size(network, in_new);
284 DataStream data_stream{};
285 data_stream << const_addr_man;
286}
287
288// Check that serialize followed by unserialize produces the same addrman.
290{
291 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
292 SetMockTime(ConsumeTime(fuzzed_data_provider));
293
294 NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
295 AddrManDeterministic addr_man1{netgroupman, fuzzed_data_provider};
296 AddrManDeterministic addr_man2{netgroupman, fuzzed_data_provider};
297
298 DataStream data_stream{};
299
300 FillAddrman(addr_man1, fuzzed_data_provider);
301 data_stream << addr_man1;
302 data_stream >> addr_man2;
303 assert(addr_man1 == addr_man2);
304}
void ReadFromStream(AddrMan &addr, DataStream &ssPeers)
Only used by tests.
Definition addrdb.cpp:186
static constexpr int ADDRMAN_TRIED_BUCKET_COUNT
static constexpr int ADDRMAN_BUCKET_SIZE
static constexpr int ADDRMAN_NEW_BUCKET_COUNT
static int32_t GetCheckRatio(const NodeContext &node_ctx)
Extended statistics about a CAddress.
CNetAddr source
where knowledge about this address first came from
bool fInTried
in tried set? (memory only)
NodeSeconds m_last_success
last successful connection by us
int nRefCount
reference count in new sets (memory only)
int nAttempts
connection attempts since last successful attempt
AddrManDeterministic(const NetGroupManager &netgroupman, FuzzedDataProvider &fuzzed_data_provider)
Definition addrman.cpp:124
bool operator==(const AddrManDeterministic &other) const
Compare with another AddrMan.
Definition addrman.cpp:137
Stochastic address manager.
Definition addrman.h:88
void Connected(const CService &addr, NodeSeconds time=Now< NodeSeconds >())
We have successfully connected to this peer.
Definition addrman.cpp:1333
const std::unique_ptr< AddrManImpl > m_impl
Definition addrman.h:90
void Attempt(const CService &addr, bool fCountFailure, NodeSeconds time=Now< NodeSeconds >())
Mark an entry as connection attempted to.
Definition addrman.cpp:1303
size_t Size(std::optional< Network > net=std::nullopt, std::optional< bool > in_new=std::nullopt) const
Return size information about addrman.
Definition addrman.cpp:1288
void ResolveCollisions()
See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
Definition addrman.cpp:1308
bool Good(const CService &addr, NodeSeconds time=Now< NodeSeconds >())
Mark an address record as accessible and attempt to move it to addrman's tried table.
Definition addrman.cpp:1298
std::pair< CAddress, NodeSeconds > SelectTriedCollision()
Randomly select an address in the tried table that another address is attempting to evict.
Definition addrman.cpp:1313
bool Add(const std::vector< CAddress > &vAddr, const CNetAddr &source, std::chrono::seconds time_penalty=0s)
Attempt to add one or more addresses to addrman's new table.
Definition addrman.cpp:1293
void SetServices(const CService &addr, ServiceFlags nServices)
Update an entry's service bits.
Definition addrman.cpp:1338
std::vector< CAddress > GetAddr(size_t max_addresses, size_t max_pct, std::optional< Network > network, const bool filtered=true) const
Return all or many randomly selected addresses, optionally by network.
Definition addrman.cpp:1323
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition args.cpp:481
A CService with information about it as peer.
Definition protocol.h:367
Network address.
Definition netaddress.h:112
bool IsValid() const
A combination of a network address (CNetAddr) and a (TCP) port.
Definition netaddress.h:531
SipHash-2-4.
Definition siphash.h:15
uint64_t Finalize() const
Compute the 64-bit SipHash-2-4 of the data written so far.
Definition siphash.cpp:77
CSipHasher & Write(uint64_t data)
Hash a 64-bit integer worth of data It is treated as if this was the little-endian interpretation of ...
Definition siphash.cpp:28
Double ended buffer combining vector and stream-like interfaces.
Definition streams.h:147
Fast randomness source.
Definition random.h:377
T ConsumeIntegralInRange(T min, T max)
Netgroup manager.
Definition netgroup.h:16
#define FUZZ_TARGET(...)
Definition fuzz.h:35
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition fuzz.h:22
@ NODE_NETWORK
Definition protocol.h:315
const char * source
std::unique_ptr< T > MakeNoLogFileContext(const ChainType chain_type=ChainType::REGTEST, TestOpts opts={})
Make a test setup that has disk access to the debug.log file disabled.
Basic testing setup.
node::NodeContext m_node
ArgsManager * args
Definition context.h:71
#define LOCK2(cs1, cs2)
Definition sync.h:258
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition sync.h:301
NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition addrman.cpp:42
void FillAddrman(AddrMan &addrman, FuzzedDataProvider &fuzzed_data_provider)
Fill addrman with lots of addresses from lots of sources.
Definition addrman.cpp:87
CNetAddr RandAddr(FuzzedDataProvider &fuzzed_data_provider, FastRandomContext &fast_random_context)
Generate a random address.
Definition addrman.cpp:64
void initialize_addrman()
Definition addrman.cpp:36
CAddress ConsumeAddress(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition net.cpp:87
CNetAddr ConsumeNetAddr(FuzzedDataProvider &fuzzed_data_provider, FastRandomContext *rand) noexcept
Create a CNetAddr.
Definition net.cpp:28
CService ConsumeService(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition net.h:101
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition util.cpp:34
WeakEnumType ConsumeWeakEnum(FuzzedDataProvider &fuzzed_data_provider, const WeakEnumType(&all_types)[size]) noexcept
Definition util.h:131
DataStream ConsumeDataStream(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition util.h:73
std::vector< bool > ConsumeRandomLengthBitVector(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition util.h:68
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition util.h:169
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition util.h:35
std::vector< B > ConsumeRandomLengthByteVector(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition util.h:57
constexpr ServiceFlags ALL_SERVICE_FLAGS[]
Definition net.h:94
constexpr auto ALL_NETWORKS
Definition net.h:126
#define EXCLUSIVE_LOCKS_REQUIRED(...)
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition time.cpp:32
constexpr auto TicksSinceEpoch(Timepoint t)
Definition time.h:50
T Now()
Return the current time point cast to the given precision.
Definition time.h:91
std::chrono::time_point< NodeClock, std::chrono::seconds > NodeSeconds
Definition time.h:23
bool SanityCheckASMap(const std::vector< bool > &asmap, int bits)
Definition asmap.cpp:133
assert(!tx.IsCoinBase())