Bitcoin Core  26.1.0
P2P Digital Currency
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>
16 #include <test/util/setup_common.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 
27 namespace {
28 const BasicTestingSetup* g_setup;
29 
30 int32_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 
49 FUZZ_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 
64 CNetAddr RandAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext& fast_random_context)
65 {
66  CNetAddr addr;
67  if (fuzzed_data_provider.remaining_bytes() > 1 && fuzzed_data_provider.ConsumeBool()) {
68  addr = ConsumeNetAddr(fuzzed_data_provider);
69  } else {
70  // The networks [1..6] correspond to CNetAddr::BIP155Network (private).
71  static const std::map<uint8_t, uint8_t> net_len_map = {{1, ADDR_IPV4_SIZE},
72  {2, ADDR_IPV6_SIZE},
73  {4, ADDR_TORV3_SIZE},
74  {5, ADDR_I2P_SIZE},
75  {6, ADDR_CJDNS_SIZE}};
76  uint8_t net = fast_random_context.randrange(5) + 1; // [1..5]
77  if (net == 3) {
78  net = 6;
79  }
80 
81  DataStream s{};
82 
83  s << net;
84  s << fast_random_context.randbytes(net_len_map.at(net));
85 
86  s >> CAddress::V2_NETWORK(addr);
87  }
88 
89  // Return a dummy IPv4 5.5.5.5 if we generated an invalid address.
90  if (!addr.IsValid()) {
91  in_addr v4_addr = {};
92  v4_addr.s_addr = 0x05050505;
93  addr = CNetAddr{v4_addr};
94  }
95 
96  return addr;
97 }
98 
100 void FillAddrman(AddrMan& addrman, FuzzedDataProvider& fuzzed_data_provider)
101 {
102  // Add a fraction of the addresses to the "tried" table.
103  // 0, 1, 2, 3 corresponding to 0%, 100%, 50%, 33%
104  const size_t n = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 3);
105 
106  const size_t num_sources = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50);
107  CNetAddr prev_source;
108  // Generate a FastRandomContext seed to use inside the loops instead of
109  // fuzzed_data_provider. When fuzzed_data_provider is exhausted it
110  // just returns 0.
111  FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
112  for (size_t i = 0; i < num_sources; ++i) {
113  const auto source = RandAddr(fuzzed_data_provider, fast_random_context);
114  const size_t num_addresses = fast_random_context.randrange(500) + 1; // [1..500]
115 
116  for (size_t j = 0; j < num_addresses; ++j) {
117  const auto addr = CAddress{CService{RandAddr(fuzzed_data_provider, fast_random_context), 8333}, NODE_NETWORK};
118  const std::chrono::seconds time_penalty{fast_random_context.randrange(100000001)};
119  addrman.Add({addr}, source, time_penalty);
120 
121  if (n > 0 && addrman.Size() % n == 0) {
122  addrman.Good(addr, Now<NodeSeconds>());
123  }
124 
125  // Add 10% of the addresses from more than one source.
126  if (fast_random_context.randrange(10) == 0 && prev_source.IsValid()) {
127  addrman.Add({addr}, prev_source, time_penalty);
128  }
129  }
130  prev_source = source;
131  }
132 }
133 
135 {
136 public:
137  explicit AddrManDeterministic(const NetGroupManager& netgroupman, FuzzedDataProvider& fuzzed_data_provider)
138  : AddrMan(netgroupman, /*deterministic=*/true, GetCheckRatio())
139  {
140  WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
141  }
142 
150  bool operator==(const AddrManDeterministic& other) const
151  {
152  LOCK2(m_impl->cs, other.m_impl->cs);
153 
154  if (m_impl->mapInfo.size() != other.m_impl->mapInfo.size() || m_impl->nNew != other.m_impl->nNew ||
155  m_impl->nTried != other.m_impl->nTried) {
156  return false;
157  }
158 
159  // Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
160  // Keys may be different.
161 
162  auto addrinfo_hasher = [](const AddrInfo& a) {
163  CSipHasher hasher(0, 0);
164  auto addr_key = a.GetKey();
165  auto source_key = a.source.GetAddrBytes();
166  hasher.Write(TicksSinceEpoch<std::chrono::seconds>(a.m_last_success));
167  hasher.Write(a.nAttempts);
168  hasher.Write(a.nRefCount);
169  hasher.Write(a.fInTried);
170  hasher.Write(a.GetNetwork());
171  hasher.Write(a.source.GetNetwork());
172  hasher.Write(addr_key.size());
173  hasher.Write(source_key.size());
174  hasher.Write(addr_key);
175  hasher.Write(source_key);
176  return (size_t)hasher.Finalize();
177  };
178 
179  auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
180  return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.m_last_success, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) ==
181  std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.m_last_success, rhs.nAttempts, rhs.nRefCount, rhs.fInTried);
182  };
183 
184  using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>;
185 
186  const size_t num_addresses{m_impl->mapInfo.size()};
187 
188  Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
189  for (const auto& [id, addr] : m_impl->mapInfo) {
190  addresses.insert(addr);
191  }
192 
193  Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
194  for (const auto& [id, addr] : other.m_impl->mapInfo) {
195  other_addresses.insert(addr);
196  }
197 
198  if (addresses != other_addresses) {
199  return false;
200  }
201 
202  auto IdsReferToSameAddress = [&](int id, int other_id) EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs, other.m_impl->cs) {
203  if (id == -1 && other_id == -1) {
204  return true;
205  }
206  if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) {
207  return false;
208  }
209  return m_impl->mapInfo.at(id) == other.m_impl->mapInfo.at(other_id);
210  };
211 
212  // Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]`
213  // contains just an id and the address is to be found in `mapInfo.at(id)`. The ids
214  // themselves may differ between `vvNew` and `other.vvNew`.
215  for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) {
216  for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
217  if (!IdsReferToSameAddress(m_impl->vvNew[i][j], other.m_impl->vvNew[i][j])) {
218  return false;
219  }
220  }
221  }
222 
223  // Same for `vvTried`.
224  for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) {
225  for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
226  if (!IdsReferToSameAddress(m_impl->vvTried[i][j], other.m_impl->vvTried[i][j])) {
227  return false;
228  }
229  }
230  }
231 
232  return true;
233  }
234 };
235 
237 {
238  FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
239  SetMockTime(ConsumeTime(fuzzed_data_provider));
240  NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
241  auto addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider);
242  if (fuzzed_data_provider.ConsumeBool()) {
243  const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
244  DataStream ds{serialized_data};
245  try {
246  ds >> *addr_man_ptr;
247  } catch (const std::ios_base::failure&) {
248  addr_man_ptr = std::make_unique<AddrManDeterministic>(netgroupman, fuzzed_data_provider);
249  }
250  }
251  AddrManDeterministic& addr_man = *addr_man_ptr;
252  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
253  CallOneOf(
254  fuzzed_data_provider,
255  [&] {
256  addr_man.ResolveCollisions();
257  },
258  [&] {
259  (void)addr_man.SelectTriedCollision();
260  },
261  [&] {
262  std::vector<CAddress> addresses;
263  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000) {
264  addresses.push_back(ConsumeAddress(fuzzed_data_provider));
265  }
266  addr_man.Add(addresses, ConsumeNetAddr(fuzzed_data_provider), std::chrono::seconds{ConsumeTime(fuzzed_data_provider, 0, 100000000)});
267  },
268  [&] {
269  addr_man.Good(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}});
270  },
271  [&] {
272  addr_man.Attempt(ConsumeService(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool(), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}});
273  },
274  [&] {
275  addr_man.Connected(ConsumeService(fuzzed_data_provider), NodeSeconds{std::chrono::seconds{ConsumeTime(fuzzed_data_provider)}});
276  },
277  [&] {
278  addr_man.SetServices(ConsumeService(fuzzed_data_provider), ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS));
279  });
280  }
281  const AddrMan& const_addr_man{addr_man};
282  std::optional<Network> network;
283  if (fuzzed_data_provider.ConsumeBool()) {
284  network = fuzzed_data_provider.PickValueInArray(ALL_NETWORKS);
285  }
286  (void)const_addr_man.GetAddr(
287  /*max_addresses=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
288  /*max_pct=*/fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
289  network);
290  (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool(), network);
291  std::optional<bool> in_new;
292  if (fuzzed_data_provider.ConsumeBool()) {
293  in_new = fuzzed_data_provider.ConsumeBool();
294  }
295  (void)const_addr_man.Size(network, in_new);
296  DataStream data_stream{};
297  data_stream << const_addr_man;
298 }
299 
300 // Check that serialize followed by unserialize produces the same addrman.
301 FUZZ_TARGET(addrman_serdeser, .init = initialize_addrman)
302 {
303  FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
304  SetMockTime(ConsumeTime(fuzzed_data_provider));
305 
306  NetGroupManager netgroupman{ConsumeNetGroupManager(fuzzed_data_provider)};
307  AddrManDeterministic addr_man1{netgroupman, fuzzed_data_provider};
308  AddrManDeterministic addr_man2{netgroupman, fuzzed_data_provider};
309 
310  DataStream data_stream{};
311 
312  FillAddrman(addr_man1, fuzzed_data_provider);
313  data_stream << addr_man1;
314  data_stream >> addr_man2;
315  assert(addr_man1 == addr_man2);
316 }
const std::unique_ptr< AddrManImpl > m_impl
Definition: addrman.h:90
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:1285
assert(!tx.IsCoinBase())
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
static constexpr int ADDRMAN_BUCKET_SIZE
Definition: addrman_impl.h:34
WeakEnumType ConsumeWeakEnum(FuzzedDataProvider &fuzzed_data_provider, const WeakEnumType(&all_types)[size]) noexcept
Definition: util.h:132
bool SanityCheckASMap(const std::vector< bool > &asmap, int bits)
Definition: asmap.cpp:133
static constexpr size_t ADDR_TORV3_SIZE
Size of TORv3 address (in bytes).
Definition: netaddress.h:96
static constexpr int ADDRMAN_TRIED_BUCKET_COUNT
Definition: addrman_impl.h:28
static constexpr SerParams V2_NETWORK
Definition: protocol.h:405
static constexpr size_t ADDR_IPV4_SIZE
Size of IPv4 address (in bytes).
Definition: netaddress.h:89
CNetAddr ConsumeNetAddr(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: net.cpp:28
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:18
Netgroup manager.
Definition: netgroup.h:16
std::vector< B > ConsumeRandomLengthByteVector(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition: util.h:58
void ResolveCollisions()
See if any to-be-evicted tried table entries have been tested and if so resolve the collisions...
Definition: addrman.cpp:1305
Stochastic address manager.
Definition: addrman.h:87
Basic testing setup.
Definition: setup_common.h:49
bool IsValid() const
Definition: netaddress.cpp:425
static constexpr size_t ADDR_CJDNS_SIZE
Size of CJDNS address (in bytes).
Definition: netaddress.h:102
CAddress ConsumeAddress(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: net.cpp:57
std::chrono::time_point< NodeClock, std::chrono::seconds > NodeSeconds
Definition: time.h:23
constexpr ServiceFlags ALL_SERVICE_FLAGS[]
Definition: net.h:61
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:81
int nAttempts
connection attempts since last successful attempt
Definition: addrman_impl.h:55
#define LOCK2(cs1, cs2)
Definition: sync.h:259
const char * source
Definition: rpcconsole.cpp:59
static constexpr size_t ADDR_IPV6_SIZE
Size of IPv6 address (in bytes).
Definition: netaddress.h:92
void initialize_addrman()
Definition: addrman.cpp:36
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&#39;s new table.
Definition: addrman.cpp:1290
DataStream ConsumeDataStream(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition: util.h:74
int nRefCount
reference count in new sets (memory only)
Definition: addrman_impl.h:58
static constexpr size_t ADDR_I2P_SIZE
Size of I2P address (in bytes).
Definition: netaddress.h:99
void FillAddrman(AddrMan &addrman, FuzzedDataProvider &fuzzed_data_provider)
Fill addrman with lots of addresses from lots of sources.
Definition: addrman.cpp:100
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:192
ArgsManager * args
Definition: context.h:61
CService ConsumeService(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: net.h:93
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:534
Fast randomness source.
Definition: random.h:143
Extended statistics about a CAddress.
Definition: addrman_impl.h:39
void Attempt(const CService &addr, bool fCountFailure, NodeSeconds time=Now< NodeSeconds >())
Mark an entry as connection attempted to.
Definition: addrman.cpp:1300
A CService with information about it as peer.
Definition: protocol.h:362
void Connected(const CService &addr, NodeSeconds time=Now< NodeSeconds >())
We have successfully connected to this peer.
Definition: addrman.cpp:1330
std::vector< bool > ConsumeRandomLengthBitVector(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition: util.h:69
uint64_t Finalize() const
Compute the 64-bit SipHash-2-4 of the data written so far.
Definition: siphash.cpp:77
std::vector< CAddress > GetAddr(size_t max_addresses, size_t max_pct, std::optional< Network > network) const
Return all or many randomly selected addresses, optionally by network.
Definition: addrman.cpp:1320
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:302
std::vector< B > randbytes(size_t len)
Generate random bytes.
Definition: random.cpp:596
NetGroupManager ConsumeNetGroupManager(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: addrman.cpp:42
CNetAddr source
where knowledge about this address first came from
Definition: addrman_impl.h:49
Network address.
Definition: netaddress.h:115
void ReadFromStream(AddrMan &addr, DataStream &ssPeers)
Only used by tests.
Definition: addrdb.cpp:180
std::pair< CAddress, NodeSeconds > SelectTriedCollision()
Randomly select an address in the tried table that another address is attempting to evict...
Definition: addrman.cpp:1310
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:35
static constexpr int ADDRMAN_NEW_BUCKET_COUNT
Definition: addrman_impl.h:31
bool operator==(const AddrManDeterministic &other) const
Compare with another AddrMan.
Definition: addrman.cpp:150
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:481
AddrManDeterministic(const NetGroupManager &netgroupman, FuzzedDataProvider &fuzzed_data_provider)
Definition: addrman.cpp:137
void SetServices(const CService &addr, ServiceFlags nServices)
Update an entry&#39;s service bits.
Definition: addrman.cpp:1335
static int32_t GetCheckRatio(const NodeContext &node_ctx)
SipHash-2-4.
Definition: siphash.h:14
constexpr auto ALL_NETWORKS
Definition: net.h:93
bool fInTried
in tried set? (memory only)
Definition: addrman_impl.h:61
FUZZ_TARGET(data_stream_addr_man,.init=initialize_addrman)
Definition: addrman.cpp:49
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:36
CNetAddr RandAddr(FuzzedDataProvider &fuzzed_data_provider, FastRandomContext &fast_random_context)
Generate a random address.
Definition: addrman.cpp:64
T ConsumeIntegralInRange(T min, T max)
bool Good(const CService &addr, NodeSeconds time=Now< NodeSeconds >())
Mark an address record as accessible and attempt to move it to addrman&#39;s tried table.
Definition: addrman.cpp:1295
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:170
node::NodeContext m_node
Definition: setup_common.h:50
NodeSeconds m_last_success
last successful connection by us
Definition: addrman_impl.h:52
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
Definition: random.h:202