Bitcoin ABC 0.34.3
P2P Digital Currency
db.h
Go to the documentation of this file.
1// Copyright (c) 2017-2019 The Bitcoin 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#ifndef BITCOIN_SEEDER_DB_H
6#define BITCOIN_SEEDER_DB_H
7
8#include <chainparams.h>
9#include <netbase.h>
11#include <protocol.h>
12#include <seeder/bitcoin.h>
13#include <seeder/util.h>
14#include <sync.h>
15#include <util/time.h>
16
17#include <cmath>
18#include <cstdint>
19#include <deque>
20#include <map>
21#include <set>
22#include <vector>
23
24#define MIN_RETRY 1000
25
26static inline int GetRequireHeight() {
27 return Params().Checkpoints().mapCheckpoints.rbegin()->first;
28}
29
30static inline bool HasCheckpoint() {
31 return !Params().Checkpoints().mapCheckpoints.empty() &&
32 GetRequireHeight() > 0;
33}
34
35static inline std::string ToString(const CService &ip) {
36 std::string str = ip.ToStringAddrPort();
37 while (str.size() < 22) {
38 str += ' ';
39 }
40 return str;
41}
42
43class CAddrStat {
44private:
45 float weight;
46 float count;
48
49public:
51
52 void Update(bool good, int64_t age, double tau) {
53 double f = exp(-age / tau);
54 reliability = reliability * f + (good ? (1.0 - f) : 0);
55 count = count * f + 1;
56 weight = weight * f + (1.0 - f);
57 }
58
60 READWRITE(obj.weight, obj.count, obj.reliability);
61 }
62
63 friend class SeederAddrInfo;
64};
65
67 OK,
74};
75
77public:
80 int blocks;
81 double uptime[5];
82 std::string clientSubVersion;
83 int64_t lastSuccess;
85 uint64_t services;
86};
87
89private:
91 uint64_t services;
92 int64_t lastTry;
93 int64_t ourLastTry;
95 int64_t ignoreTill;
103 int total;
105 std::string clientSubVersion;
107
108public:
110 : services(0), lastTry(0), ourLastTry(0), ourLastSuccess(0),
111 ignoreTill(0), clientVersion(0), blocks(0), total(0), success(0) {}
112
114 CAddrReport ret;
115 ret.ip = ip;
118 ret.blocks = blocks;
119 ret.uptime[0] = stat2H.reliability;
120 ret.uptime[1] = stat8H.reliability;
121 ret.uptime[2] = stat1D.reliability;
122 ret.uptime[3] = stat1W.reliability;
123 ret.uptime[4] = stat1M.reliability;
126 ret.services = services;
127 return ret;
128 }
129
130 bool IsReliable() const {
132 }
133
134 // Return the first detected reason a node is unreliable or `OK` if no
135 // reason found
137 if (!(services & NODE_NETWORK)) {
139 }
140 if (!ip.IsRoutable()) {
142 }
145 }
146 if (blocks && blocks < GetRequireHeight()) {
148 }
149
150 if ((total > 3 || success * 2 < total) &&
151 (stat2H.reliability <= 0.85 || stat2H.count <= 2) &&
152 (stat8H.reliability <= 0.70 || stat8H.count <= 4) &&
153 (stat1D.reliability <= 0.55 || stat1D.count <= 8) &&
154 (stat1W.reliability <= 0.45 || stat1W.count <= 16) &&
155 (stat1M.reliability <= 0.35 || stat1M.count <= 32)) {
157 }
158 if (!checkpointVerified) {
160 }
162 }
163
164 int64_t GetBanTime() const {
165 if (IsReliable()) {
166 return 0;
167 }
168 if (clientVersion && clientVersion < 31900) {
169 return 604800;
170 }
171 if (stat1M.reliability - stat1M.weight + 1.0 < 0.15 &&
172 stat1M.count > 32) {
173 return 30 * 86400;
174 }
175 if (stat1W.reliability - stat1W.weight + 1.0 < 0.10 &&
176 stat1W.count > 16) {
177 return 7 * 86400;
178 }
179 if (stat1D.reliability - stat1D.weight + 1.0 < 0.05 &&
180 stat1D.count > 8) {
181 return 1 * 86400;
182 }
183 return 0;
184 }
185
186 int64_t GetIgnoreTime() const {
187 if (IsReliable()) {
188 return 0;
189 }
190 if (stat1M.reliability - stat1M.weight + 1.0 < 0.20 &&
191 stat1M.count > 2) {
192 return 10 * 86400;
193 }
194 if (stat1W.reliability - stat1W.weight + 1.0 < 0.16 &&
195 stat1W.count > 2) {
196 return 3 * 86400;
197 }
198 if (stat1D.reliability - stat1D.weight + 1.0 < 0.12 &&
199 stat1D.count > 2) {
200 return 8 * 3600;
201 }
202 if (stat8H.reliability - stat8H.weight + 1.0 < 0.08 &&
203 stat8H.count > 2) {
204 return 2 * 3600;
205 }
206 return 0;
207 }
208
209 void Update(bool good);
210
211 friend class CAddrDb;
212
214 uint8_t version = 5;
215 READWRITE(version, WithParams(CAddress::V1_DISK, obj.ip), obj.services,
216 obj.lastTry);
217 uint8_t tried = obj.ourLastTry != 0;
218 READWRITE(tried);
219 if (!tried) {
220 return;
221 }
222
223 READWRITE(obj.ourLastTry, obj.ignoreTill, obj.stat2H, obj.stat8H,
224 obj.stat1D, obj.stat1W);
225 if (version >= 1) {
226 READWRITE(obj.stat1M);
227 } else {
228 SER_WRITE(obj, *((CAddrStat *)(&obj.stat1M)) = obj.stat1W);
229 }
230 READWRITE(obj.total, obj.success, obj.clientVersion);
231 if (version >= 2) {
232 READWRITE(obj.clientSubVersion);
233 }
234 if (version >= 3) {
235 READWRITE(obj.blocks);
236 }
237 if (version >= 4) {
238 READWRITE(obj.ourLastSuccess);
239 }
240 if (version >= 5) {
241 READWRITE(obj.checkpointVerified);
242 } else {
243 // To avoid a sudden drop of all nodes when seeders upgrade,
244 // initially mark all nodes as having their checkpoints verified,
245 // to keep previously considered good nodes live until they are
246 // later proven bad
247 SER_READ(obj, obj.checkpointVerified = true);
248 }
249 }
250};
251
253public:
257 int nNew;
258 int nGood;
259 int nAge;
260};
261
264 uint64_t services;
265 bool fGood;
269 std::string strClientV;
272};
273
283class CAddrDb {
284private:
286 // number of address id's
287 int nId;
288 // map address id to address info (b,c,d,e)
289 std::map<int, SeederAddrInfo> idToInfo;
290 // map ip to id (b,c,d,e)
291 std::map<CService, int> ipToId;
292 // sequence of tried nodes, in order we have tried connecting to them (c,d)
293 std::deque<int> ourId;
294 // set of nodes not yet tried (b)
295 std::set<int> unkId;
296 // set of good nodes (d, good e)
297 std::set<int> goodId;
298
299protected:
300 // internal routines that assume proper locks are acquired
301 // add an address
302 void Add_(const CAddress &addr, bool force);
303 // get an IP to test (must call Good_ or Bad_ on result afterwards)
304 bool Get_(CServiceResult &ip);
305 // mark an IP as good (must have been returned by Get_)
306 void Good_(const CService &ip, int clientV, std::string clientSV,
307 int blocks, uint64_t services, bool checkpointVerified);
308 // mark an IP as bad (and optionally ban it) (must have been returned by
309 // Get_)
310 void Bad_(const CService &ip, int ban);
311 // look up id of an IP
312 int Lookup_(const CService &ip);
313 // get a random set of IPs (shared lock only)
314 void GetIPs_(std::set<CNetAddr> &ips, uint64_t requestedFlags, uint32_t max,
315 const bool *nets);
316
317public:
318 // nodes that are banned, with their unban time (a)
319 std::map<CService, int64_t> banned;
320
321 void GetStats(CAddrDbStats &stats) const {
322 LOCK(cs);
323 stats.nBanned = banned.size();
324 stats.nAvail = idToInfo.size();
325 stats.nTracked = ourId.size();
326 stats.nGood = goodId.size();
327 stats.nNew = unkId.size();
328 if (ourId.size() > 0) {
329 stats.nAge = GetTime() - idToInfo.at(ourId.at(0)).ourLastTry;
330 } else {
331 stats.nAge = 0;
332 }
333 }
334
336 for (std::map<int, SeederAddrInfo>::iterator it = idToInfo.begin();
337 it != idToInfo.end(); it++) {
338 (*it).second.ignoreTill = 0;
339 }
340 }
341
342 std::vector<CAddrReport> GetAll() {
343 std::vector<CAddrReport> ret;
344 LOCK(cs);
345 for (std::deque<int>::const_iterator it = ourId.begin();
346 it != ourId.end(); it++) {
347 const SeederAddrInfo &info = idToInfo[*it];
348 if (info.success > 0) {
349 ret.push_back(info.GetReport());
350 }
351 }
352 return ret;
353 }
354
355 // serialization code
356 // format:
357 // nVersion (0 for now)
358 // n (number of ips in (b,c,d))
359 // SeederAddrInfo[n]
360 // banned
361 // acquires a shared lock (this does not suffice for read mode, but we
362 // assume that only happens at startup, single-threaded) this way, dumping
363 // does not interfere with GetIPs_, which is called from the DNS thread
364 template <typename Stream> void Serialize(Stream &s) const {
365 LOCK(cs);
366
367 int nVersion = 0;
368 s << nVersion;
369
370 CAddrDb *db = const_cast<CAddrDb *>(this);
371 int n = ourId.size() + unkId.size();
372 s << n;
373 for (std::deque<int>::const_iterator it = ourId.begin();
374 it != ourId.end(); it++) {
375 std::map<int, SeederAddrInfo>::iterator ci = db->idToInfo.find(*it);
376 s << (*ci).second;
377 }
378 for (std::set<int>::const_iterator it = unkId.begin();
379 it != unkId.end(); it++) {
380 std::map<int, SeederAddrInfo>::iterator ci = db->idToInfo.find(*it);
381 s << (*ci).second;
382 }
384 }
385
386 template <typename Stream> void Unserialize(Stream &s) {
387 LOCK(cs);
388
389 int nVersion;
390 s >> nVersion;
391
392 CAddrDb *db = const_cast<CAddrDb *>(this);
393 db->nId = 0;
394 int n;
395 s >> n;
396 for (int i = 0; i < n; i++) {
397 SeederAddrInfo info;
398 s >> info;
399 if (!info.GetBanTime()) {
400 int id = db->nId++;
401 db->idToInfo[id] = info;
402 db->ipToId[info.ip] = id;
403 if (info.ourLastTry) {
404 db->ourId.push_back(id);
405 if (info.IsReliable()) {
406 db->goodId.insert(id);
407 }
408 } else {
409 db->unkId.insert(id);
410 }
411 }
412 }
413
415 }
416
417 void Add(const CAddress &addr, bool fForce = false) {
418 LOCK(cs);
419 Add_(addr, fForce);
420 }
421
422 void Add(const std::vector<CAddress> &vAddr, bool fForce = false) {
423 LOCK(cs);
424 for (size_t i = 0; i < vAddr.size(); i++) {
425 Add_(vAddr[i], fForce);
426 }
427 }
428
429 void GetMany(std::vector<CServiceResult> &ips, int max) {
430 LOCK(cs);
431 while (max > 0) {
432 CServiceResult ip = {};
433 if (!Get_(ip)) {
434 return;
435 }
436 ips.push_back(ip);
437 max--;
438 }
439 }
440
441 void ResultMany(const std::vector<CServiceResult> &ips) {
442 LOCK(cs);
443 for (size_t i = 0; i < ips.size(); i++) {
444 if (ips[i].fGood) {
445 Good_(ips[i].service, ips[i].nClientV, ips[i].strClientV,
446 ips[i].nHeight, ips[i].services,
447 ips[i].checkpointVerified);
448 } else {
449 Bad_(ips[i].service, ips[i].nBanTime);
450 }
451 }
452 }
453
454 void GetIPs(std::set<CNetAddr> &ips, uint64_t requestedFlags, uint32_t max,
455 const bool *nets) {
456 LOCK(cs);
457 GetIPs_(ips, requestedFlags, max, nets);
458 }
459};
460
461#endif // BITCOIN_SEEDER_DB_H
const CChainParams & Params()
Return the currently selected parameters.
Definition: chainparams.cpp:21
Definition: db.h:283
void Add(const CAddress &addr, bool fForce=false)
Definition: db.h:417
void GetMany(std::vector< CServiceResult > &ips, int max)
Definition: db.h:429
std::set< int > unkId
Definition: db.h:295
void Bad_(const CService &ip, int ban)
Definition: db.cpp:110
RecursiveMutex cs
Definition: db.h:285
void ResultMany(const std::vector< CServiceResult > &ips)
Definition: db.h:441
void GetStats(CAddrDbStats &stats) const
Definition: db.h:321
void GetIPs_(std::set< CNetAddr > &ips, uint64_t requestedFlags, uint32_t max, const bool *nets)
Definition: db.cpp:188
void Good_(const CService &ip, int clientV, std::string clientSV, int blocks, uint64_t services, bool checkpointVerified)
Definition: db.cpp:87
std::vector< CAddrReport > GetAll()
Definition: db.h:342
int nId
Definition: db.h:287
void GetIPs(std::set< CNetAddr > &ips, uint64_t requestedFlags, uint32_t max, const bool *nets)
Definition: db.h:454
std::set< int > goodId
Definition: db.h:297
void Unserialize(Stream &s)
Definition: db.h:386
std::map< CService, int > ipToId
Definition: db.h:291
std::map< CService, int64_t > banned
Definition: db.h:319
std::deque< int > ourId
Definition: db.h:293
std::map< int, SeederAddrInfo > idToInfo
Definition: db.h:289
void ResetIgnores()
Definition: db.h:335
bool Get_(CServiceResult &ip)
Definition: db.cpp:44
void Add(const std::vector< CAddress > &vAddr, bool fForce=false)
Definition: db.h:422
void Serialize(Stream &s) const
Definition: db.h:364
void Add_(const CAddress &addr, bool force)
Definition: db.cpp:143
int Lookup_(const CService &ip)
Definition: db.cpp:80
int nTracked
Definition: db.h:256
int nAvail
Definition: db.h:255
int nNew
Definition: db.h:257
int nGood
Definition: db.h:258
int nBanned
Definition: db.h:254
int nAge
Definition: db.h:259
Definition: db.h:76
double uptime[5]
Definition: db.h:81
uint64_t services
Definition: db.h:85
int blocks
Definition: db.h:80
CService ip
Definition: db.h:78
int clientVersion
Definition: db.h:79
std::string clientSubVersion
Definition: db.h:82
int64_t lastSuccess
Definition: db.h:83
ReliabilityStatus reliabilityStatus
Definition: db.h:84
Definition: db.h:43
SERIALIZE_METHODS(CAddrStat, obj)
Definition: db.h:59
CAddrStat()
Definition: db.h:50
float count
Definition: db.h:46
float weight
Definition: db.h:45
float reliability
Definition: db.h:47
void Update(bool good, int64_t age, double tau)
Definition: db.h:52
A CService with information about it as peer.
Definition: protocol.h:441
static constexpr SerParams V1_DISK
Definition: protocol.h:498
const CCheckpointData & Checkpoints() const
Definition: chainparams.h:145
bool IsRoutable() const
Definition: netaddress.cpp:516
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:573
std::string ToStringAddrPort() const
int success
Definition: db.h:104
CAddrStat stat8H
Definition: db.h:97
CAddrStat stat1W
Definition: db.h:99
int64_t lastTry
Definition: db.h:92
SERIALIZE_METHODS(SeederAddrInfo, obj)
Definition: db.h:213
int64_t GetIgnoreTime() const
Definition: db.h:186
bool checkpointVerified
Definition: db.h:106
CAddrStat stat1D
Definition: db.h:98
CAddrStat stat2H
Definition: db.h:96
CAddrReport GetReport() const
Definition: db.h:113
int64_t ignoreTill
Definition: db.h:95
int64_t GetBanTime() const
Definition: db.h:164
std::string clientSubVersion
Definition: db.h:105
int total
Definition: db.h:103
bool IsReliable() const
Definition: db.h:130
int64_t ourLastTry
Definition: db.h:93
ReliabilityStatus GetReliabilityStatus() const
Definition: db.h:136
CAddrStat stat1M
Definition: db.h:100
CService ip
Definition: db.h:90
void Update(bool good)
Definition: db.cpp:11
int blocks
Definition: db.h:102
uint64_t services
Definition: db.h:91
int64_t ourLastSuccess
Definition: db.h:94
SeederAddrInfo()
Definition: db.h:109
int clientVersion
Definition: db.h:101
unsigned int nHeight
@ NODE_NETWORK
Definition: protocol.h:342
static const int MIN_PEER_PROTO_VERSION
disconnect from peers older than this proto version
static int GetRequireHeight()
Definition: db.h:26
static bool HasCheckpoint()
Definition: db.h:30
ReliabilityStatus
Definition: db.h:66
static std::string ToString(const CService &ip)
Definition: db.h:35
CAddrDb db
Definition: main.cpp:35
#define SER_WRITE(obj, code)
Definition: serialize.h:181
static auto WithParams(const Params &params, T &&t)
Return a wrapper around t that (de)serializes it with specified parameter params.
Definition: serialize.h:1331
#define SER_READ(obj, code)
Definition: serialize.h:177
#define READWRITE(...)
Definition: serialize.h:176
@ OK
The message verification was successful.
MapCheckpoints mapCheckpoints
Definition: chainparams.h:36
int nHeight
Definition: db.h:267
int nBanTime
Definition: db.h:266
CService service
Definition: db.h:263
std::string strClientV
Definition: db.h:269
uint64_t services
Definition: db.h:264
int64_t ourLastSuccess
Definition: db.h:270
bool fGood
Definition: db.h:265
int nClientV
Definition: db.h:268
bool checkpointVerified
Definition: db.h:271
#define LOCK(cs)
Definition: sync.h:295
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
Definition: time.cpp:80