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