Bitcoin Core  27.99.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  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 
87 void 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 {
123 public:
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 = FastRandomContext{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.
289 FUZZ_TARGET(addrman_serdeser, .init = initialize_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
Definition: addrman_impl.h:27
static constexpr int ADDRMAN_BUCKET_SIZE
Definition: addrman_impl.h:33
static constexpr int ADDRMAN_NEW_BUCKET_COUNT
Definition: addrman_impl.h:30
static int32_t GetCheckRatio(const NodeContext &node_ctx)
Extended statistics about a CAddress.
Definition: addrman_impl.h:39
CNetAddr source
where knowledge about this address first came from
Definition: addrman_impl.h:48
bool fInTried
in tried set? (memory only)
Definition: addrman_impl.h:60
NodeSeconds m_last_success
last successful connection by us
Definition: addrman_impl.h:51
int nRefCount
reference count in new sets (memory only)
Definition: addrman_impl.h:57
int nAttempts
connection attempts since last successful attempt
Definition: addrman_impl.h:54
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
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
Definition: args.cpp:480
A CService with information about it as peer.
Definition: protocol.h:367
Network address.
Definition: netaddress.h:112
bool IsValid() const
Definition: netaddress.cpp:428
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:145
T ConsumeIntegralInRange(T min, T max)
T PickValueInArray(const T(&array)[size])
Netgroup manager.
Definition: netgroup.h:16
#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
Definition: rpcconsole.cpp:60
Basic testing setup.
Definition: setup_common.h:54
node::NodeContext m_node
Definition: setup_common.h:56
ArgsManager * args
Definition: context.h:70
#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
FUZZ_TARGET(data_stream_addr_man,.init=initialize_addrman)
Definition: addrman.cpp:49
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
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
std::vector< bool > ConsumeRandomLengthBitVector(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition: util.h:68
constexpr ServiceFlags ALL_SERVICE_FLAGS[]
Definition: net.h:89
constexpr auto ALL_NETWORKS
Definition: net.h:121
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:32
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())