Bitcoin Core 22.99.0
P2P Digital Currency
addrman.cpp
Go to the documentation of this file.
1// Copyright (c) 2020-2021 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 <merkleblock.h>
10#include <random.h>
12#include <test/fuzz/fuzz.h>
13#include <test/fuzz/util.h>
14#include <time.h>
15#include <util/asmap.h>
16
17#include <cassert>
18#include <cstdint>
19#include <optional>
20#include <string>
21#include <vector>
22
24{
26}
27
29{
30 FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
31 CDataStream data_stream = ConsumeDataStream(fuzzed_data_provider);
32 AddrMan addr_man(/* asmap */ std::vector<bool>(), /* deterministic */ false, /* consistency_check_ratio */ 0);
33 try {
34 ReadFromStream(addr_man, data_stream);
35 } catch (const std::exception&) {
36 }
37}
38
42CNetAddr RandAddr(FuzzedDataProvider& fuzzed_data_provider, FastRandomContext& fast_random_context)
43{
44 CNetAddr addr;
45 if (fuzzed_data_provider.remaining_bytes() > 1 && fuzzed_data_provider.ConsumeBool()) {
46 addr = ConsumeNetAddr(fuzzed_data_provider);
47 } else {
48 // The networks [1..6] correspond to CNetAddr::BIP155Network (private).
49 static const std::map<uint8_t, uint8_t> net_len_map = {{1, ADDR_IPV4_SIZE},
50 {2, ADDR_IPV6_SIZE},
51 {4, ADDR_TORV3_SIZE},
52 {5, ADDR_I2P_SIZE},
53 {6, ADDR_CJDNS_SIZE}};
54 uint8_t net = fast_random_context.randrange(5) + 1; // [1..5]
55 if (net == 3) {
56 net = 6;
57 }
58
60
61 s << net;
62 s << fast_random_context.randbytes(net_len_map.at(net));
63
64 s >> addr;
65 }
66
67 // Return a dummy IPv4 5.5.5.5 if we generated an invalid address.
68 if (!addr.IsValid()) {
69 in_addr v4_addr = {};
70 v4_addr.s_addr = 0x05050505;
71 addr = CNetAddr{v4_addr};
72 }
73
74 return addr;
75}
76
78void FillAddrman(AddrMan& addrman, FuzzedDataProvider& fuzzed_data_provider)
79{
80 // Add a fraction of the addresses to the "tried" table.
81 // 0, 1, 2, 3 corresponding to 0%, 100%, 50%, 33%
82 const size_t n = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 3);
83
84 const size_t num_sources = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 50);
85 CNetAddr prev_source;
86 // Generate a FastRandomContext seed to use inside the loops instead of
87 // fuzzed_data_provider. When fuzzed_data_provider is exhausted it
88 // just returns 0.
89 FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)};
90 for (size_t i = 0; i < num_sources; ++i) {
91 const auto source = RandAddr(fuzzed_data_provider, fast_random_context);
92 const size_t num_addresses = fast_random_context.randrange(500) + 1; // [1..500]
93
94 for (size_t j = 0; j < num_addresses; ++j) {
95 const auto addr = CAddress{CService{RandAddr(fuzzed_data_provider, fast_random_context), 8333}, NODE_NETWORK};
96 const auto time_penalty = fast_random_context.randrange(100000001);
97 addrman.Add({addr}, source, time_penalty);
98
99 if (n > 0 && addrman.size() % n == 0) {
100 addrman.Good(addr, GetTime());
101 }
102
103 // Add 10% of the addresses from more than one source.
104 if (fast_random_context.randrange(10) == 0 && prev_source.IsValid()) {
105 addrman.Add({addr}, prev_source, time_penalty);
106 }
107 }
108 prev_source = source;
109 }
110}
111
113{
114public:
115 explicit AddrManDeterministic(std::vector<bool> asmap, FuzzedDataProvider& fuzzed_data_provider)
116 : AddrMan(std::move(asmap), /* deterministic */ true, /* consistency_check_ratio */ 0)
117 {
118 WITH_LOCK(m_impl->cs, m_impl->insecure_rand = FastRandomContext{ConsumeUInt256(fuzzed_data_provider)});
119 }
120
129 {
130 LOCK2(m_impl->cs, other.m_impl->cs);
131
132 if (m_impl->mapInfo.size() != other.m_impl->mapInfo.size() || m_impl->nNew != other.m_impl->nNew ||
133 m_impl->nTried != other.m_impl->nTried) {
134 return false;
135 }
136
137 // Check that all values in `mapInfo` are equal to all values in `other.mapInfo`.
138 // Keys may be different.
139
140 auto addrinfo_hasher = [](const AddrInfo& a) {
141 CSipHasher hasher(0, 0);
142 auto addr_key = a.GetKey();
143 auto source_key = a.source.GetAddrBytes();
144 hasher.Write(a.nLastSuccess);
145 hasher.Write(a.nAttempts);
146 hasher.Write(a.nRefCount);
147 hasher.Write(a.fInTried);
148 hasher.Write(a.GetNetwork());
149 hasher.Write(a.source.GetNetwork());
150 hasher.Write(addr_key.size());
151 hasher.Write(source_key.size());
152 hasher.Write(addr_key.data(), addr_key.size());
153 hasher.Write(source_key.data(), source_key.size());
154 return (size_t)hasher.Finalize();
155 };
156
157 auto addrinfo_eq = [](const AddrInfo& lhs, const AddrInfo& rhs) {
158 return std::tie(static_cast<const CService&>(lhs), lhs.source, lhs.nLastSuccess, lhs.nAttempts, lhs.nRefCount, lhs.fInTried) ==
159 std::tie(static_cast<const CService&>(rhs), rhs.source, rhs.nLastSuccess, rhs.nAttempts, rhs.nRefCount, rhs.fInTried);
160 };
161
162 using Addresses = std::unordered_set<AddrInfo, decltype(addrinfo_hasher), decltype(addrinfo_eq)>;
163
164 const size_t num_addresses{m_impl->mapInfo.size()};
165
166 Addresses addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
167 for (const auto& [id, addr] : m_impl->mapInfo) {
168 addresses.insert(addr);
169 }
170
171 Addresses other_addresses{num_addresses, addrinfo_hasher, addrinfo_eq};
172 for (const auto& [id, addr] : other.m_impl->mapInfo) {
173 other_addresses.insert(addr);
174 }
175
176 if (addresses != other_addresses) {
177 return false;
178 }
179
180 auto IdsReferToSameAddress = [&](int id, int other_id) EXCLUSIVE_LOCKS_REQUIRED(m_impl->cs, other.m_impl->cs) {
181 if (id == -1 && other_id == -1) {
182 return true;
183 }
184 if ((id == -1 && other_id != -1) || (id != -1 && other_id == -1)) {
185 return false;
186 }
187 return m_impl->mapInfo.at(id) == other.m_impl->mapInfo.at(other_id);
188 };
189
190 // Check that `vvNew` contains the same addresses as `other.vvNew`. Notice - `vvNew[i][j]`
191 // contains just an id and the address is to be found in `mapInfo.at(id)`. The ids
192 // themselves may differ between `vvNew` and `other.vvNew`.
193 for (size_t i = 0; i < ADDRMAN_NEW_BUCKET_COUNT; ++i) {
194 for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
195 if (!IdsReferToSameAddress(m_impl->vvNew[i][j], other.m_impl->vvNew[i][j])) {
196 return false;
197 }
198 }
199 }
200
201 // Same for `vvTried`.
202 for (size_t i = 0; i < ADDRMAN_TRIED_BUCKET_COUNT; ++i) {
203 for (size_t j = 0; j < ADDRMAN_BUCKET_SIZE; ++j) {
204 if (!IdsReferToSameAddress(m_impl->vvTried[i][j], other.m_impl->vvTried[i][j])) {
205 return false;
206 }
207 }
208 }
209
210 return true;
211 }
212};
213
214[[nodiscard]] inline std::vector<bool> ConsumeAsmap(FuzzedDataProvider& fuzzed_data_provider) noexcept
215{
216 std::vector<bool> asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider);
217 if (!SanityCheckASMap(asmap, 128)) asmap.clear();
218 return asmap;
219}
220
222{
223 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
224 SetMockTime(ConsumeTime(fuzzed_data_provider));
225 std::vector<bool> asmap = ConsumeAsmap(fuzzed_data_provider);
226 auto addr_man_ptr = std::make_unique<AddrManDeterministic>(asmap, fuzzed_data_provider);
227 if (fuzzed_data_provider.ConsumeBool()) {
228 const std::vector<uint8_t> serialized_data{ConsumeRandomLengthByteVector(fuzzed_data_provider)};
229 CDataStream ds(serialized_data, SER_DISK, INIT_PROTO_VERSION);
230 const auto ser_version{fuzzed_data_provider.ConsumeIntegral<int32_t>()};
231 ds.SetVersion(ser_version);
232 try {
233 ds >> *addr_man_ptr;
234 } catch (const std::ios_base::failure&) {
235 addr_man_ptr = std::make_unique<AddrManDeterministic>(asmap, fuzzed_data_provider);
236 }
237 }
238 AddrManDeterministic& addr_man = *addr_man_ptr;
239 while (fuzzed_data_provider.ConsumeBool()) {
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 while (fuzzed_data_provider.ConsumeBool()) {
251 const std::optional<CAddress> opt_address = ConsumeDeserializable<CAddress>(fuzzed_data_provider);
252 if (!opt_address) {
253 break;
254 }
255 addresses.push_back(*opt_address);
256 }
257 const std::optional<CNetAddr> opt_net_addr = ConsumeDeserializable<CNetAddr>(fuzzed_data_provider);
258 if (opt_net_addr) {
259 addr_man.Add(addresses, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, 100000000));
260 }
261 },
262 [&] {
263 const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
264 if (opt_service) {
265 addr_man.Good(*opt_service, ConsumeTime(fuzzed_data_provider));
266 }
267 },
268 [&] {
269 const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
270 if (opt_service) {
271 addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider));
272 }
273 },
274 [&] {
275 const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
276 if (opt_service) {
277 addr_man.Connected(*opt_service, ConsumeTime(fuzzed_data_provider));
278 }
279 },
280 [&] {
281 const std::optional<CService> opt_service = ConsumeDeserializable<CService>(fuzzed_data_provider);
282 if (opt_service) {
283 addr_man.SetServices(*opt_service, ConsumeWeakEnum(fuzzed_data_provider, ALL_SERVICE_FLAGS));
284 }
285 });
286 }
287 const AddrMan& const_addr_man{addr_man};
288 (void)const_addr_man.GetAddr(
289 /* max_addresses */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
290 /* max_pct */ fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096),
291 /* network */ std::nullopt);
292 (void)const_addr_man.Select(fuzzed_data_provider.ConsumeBool());
293 (void)const_addr_man.size();
295 data_stream << const_addr_man;
296}
297
298// Check that serialize followed by unserialize produces the same addrman.
300{
301 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
302 SetMockTime(ConsumeTime(fuzzed_data_provider));
303
304 std::vector<bool> asmap = ConsumeAsmap(fuzzed_data_provider);
305 AddrManDeterministic addr_man1{asmap, fuzzed_data_provider};
306 AddrManDeterministic addr_man2{asmap, fuzzed_data_provider};
307
309
310 FillAddrman(addr_man1, fuzzed_data_provider);
311 data_stream << addr_man1;
312 data_stream >> addr_man2;
313 assert(addr_man1 == addr_man2);
314}
void ReadFromStream(AddrMan &addr, CDataStream &ssPeers)
Only used by tests.
Definition: addrdb.cpp:179
static constexpr int ADDRMAN_TRIED_BUCKET_COUNT
Definition: addrman_impl.h:25
static constexpr int ADDRMAN_BUCKET_SIZE
Definition: addrman_impl.h:31
static constexpr int ADDRMAN_NEW_BUCKET_COUNT
Definition: addrman_impl.h:28
void SelectParams(const std::string &network)
Sets the params returned by Params() to those for the given chain name.
Extended statistics about a CAddress.
Definition: addrman_impl.h:37
CNetAddr source
where knowledge about this address first came from
Definition: addrman_impl.h:46
bool fInTried
in tried set? (memory only)
Definition: addrman_impl.h:58
int64_t nLastSuccess
last successful connection by us
Definition: addrman_impl.h:49
int nRefCount
reference count in new sets (memory only)
Definition: addrman_impl.h:55
int nAttempts
connection attempts since last successful attempt
Definition: addrman_impl.h:52
AddrManDeterministic(std::vector< bool > asmap, FuzzedDataProvider &fuzzed_data_provider)
Definition: addrman.cpp:115
bool operator==(const AddrManDeterministic &other)
Compare with another AddrMan.
Definition: addrman.cpp:128
Stochastic address manager.
Definition: addrman.h:55
std::pair< CAddress, int64_t > SelectTriedCollision()
Randomly select an address in the tried table that another address is attempting to evict.
Definition: addrman.cpp:1173
const std::unique_ptr< AddrManImpl > m_impl
Definition: addrman.h:56
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:1183
bool Add(const std::vector< CAddress > &vAddr, const CNetAddr &source, int64_t nTimePenalty=0)
Attempt to add one or more addresses to addrman's new table.
Definition: addrman.cpp:1153
void Good(const CService &addr, int64_t nTime=GetAdjustedTime())
Mark an entry as accessible, possibly moving it from "new" to "tried".
Definition: addrman.cpp:1158
void ResolveCollisions()
See if any to-be-evicted tried table entries have been tested and if so resolve the collisions.
Definition: addrman.cpp:1168
void Connected(const CService &addr, int64_t nTime=GetAdjustedTime())
We have successfully connected to this peer.
Definition: addrman.cpp:1188
void Attempt(const CService &addr, bool fCountFailure, int64_t nTime=GetAdjustedTime())
Mark an entry as connection attempted to.
Definition: addrman.cpp:1163
size_t size() const
Return the number of (unique) addresses in all tables.
Definition: addrman.cpp:1148
void SetServices(const CService &addr, ServiceFlags nServices)
Update an entry's service bits.
Definition: addrman.cpp:1193
A CService with information about it as peer.
Definition: protocol.h:359
static const std::string REGTEST
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:205
void SetVersion(int n)
Definition: streams.h:362
Network address.
Definition: netaddress.h:119
bool IsValid() const
Definition: netaddress.cpp:451
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:523
SipHash-2-4.
Definition: siphash.h:14
uint64_t Finalize() const
Compute the 64-bit SipHash-2-4 of the data written so far.
Definition: siphash.cpp:76
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
Fast randomness source.
Definition: random.h:120
std::vector< unsigned char > randbytes(size_t len)
Generate random bytes.
Definition: random.cpp:626
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
Definition: random.h:190
T ConsumeIntegralInRange(T min, T max)
static NodeId id
static constexpr size_t ADDR_CJDNS_SIZE
Size of CJDNS address (in bytes).
Definition: netaddress.h:107
static constexpr int ADDRV2_FORMAT
A flag that is ORed into the protocol version to designate that addresses should be serialized in (un...
Definition: netaddress.h:34
static constexpr size_t ADDR_TORV3_SIZE
Size of TORv3 address (in bytes).
Definition: netaddress.h:101
static constexpr size_t ADDR_I2P_SIZE
Size of I2P address (in bytes).
Definition: netaddress.h:104
static constexpr size_t ADDR_IPV4_SIZE
Size of IPv4 address (in bytes).
Definition: netaddress.h:94
static constexpr size_t ADDR_IPV6_SIZE
Size of IPv6 address (in bytes).
Definition: netaddress.h:97
@ NODE_NETWORK
Definition: protocol.h:277
const char * source
Definition: rpcconsole.cpp:63
@ SER_DISK
Definition: serialize.h:139
@ SER_NETWORK
Definition: serialize.h:138
#define LOCK2(cs1, cs2)
Definition: sync.h:227
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:270
FUZZ_TARGET_INIT(data_stream_addr_man, initialize_addrman)
Definition: addrman.cpp:28
void FillAddrman(AddrMan &addrman, FuzzedDataProvider &fuzzed_data_provider)
Fill addrman with lots of addresses from lots of sources.
Definition: addrman.cpp:78
std::vector< bool > ConsumeAsmap(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: addrman.cpp:214
CNetAddr RandAddr(FuzzedDataProvider &fuzzed_data_provider, FastRandomContext &fast_random_context)
Generate a random address.
Definition: addrman.cpp:42
void initialize_addrman()
Definition: addrman.cpp:23
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:227
CNetAddr ConsumeNetAddr(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.cpp:374
WeakEnumType ConsumeWeakEnum(FuzzedDataProvider &fuzzed_data_provider, const WeakEnumType(&all_types)[size]) noexcept
Definition: util.h:115
std::vector< bool > ConsumeRandomLengthBitVector(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition: util.h:69
CDataStream ConsumeDataStream(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition: util.h:74
uint256 ConsumeUInt256(FuzzedDataProvider &fuzzed_data_provider) noexcept
Definition: util.h:153
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:40
std::vector< uint8_t > ConsumeRandomLengthByteVector(FuzzedDataProvider &fuzzed_data_provider, const std::optional< size_t > &max_length=std::nullopt) noexcept
Definition: util.h:61
constexpr ServiceFlags ALL_SERVICE_FLAGS[]
Definition: net.h:47
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:101
int64_t GetTime()
DEPRECATED Use either GetTimeSeconds (not mockable) or GetTime<T> (mockable)
Definition: time.cpp:26
bool SanityCheckASMap(const std::vector< bool > &asmap, int bits)
Definition: asmap.cpp:129
assert(!tx.IsCoinBase())
static const int INIT_PROTO_VERSION
initial proto version, to be increased after version/verack negotiation
Definition: version.h:15
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:12