seeder/db.h

360 lines
11 KiB
C
Raw Normal View History

2011-12-16 18:37:05 +01:00
#include <stdint.h>
2011-12-19 23:18:06 +01:00
#include <math.h>
2011-12-16 18:37:05 +01:00
#include <set>
#include <map>
#include <vector>
#include <deque>
#include "netbase.h"
#include "protocol.h"
#include "util.h"
2011-12-20 05:20:50 +01:00
#define MIN_RETRY 1000
2011-12-19 23:18:06 +01:00
2015-07-29 15:16:26 +02:00
#define REQUIRE_VERSION 70001
static inline int GetRequireHeight(const bool testnet = fTestNet)
{
2015-07-29 15:16:26 +02:00
return testnet ? 500000 : 350000;
}
2012-05-25 15:41:27 +02:00
std::string static inline ToString(const CService &ip) {
2011-12-20 14:29:21 +01:00
std::string str = ip.ToString();
while (str.size() < 22) str += ' ';
return str;
}
2011-12-23 02:43:32 +01:00
class CAddrStat {
2011-12-22 03:25:35 +01:00
private:
float weight;
2011-12-23 02:43:32 +01:00
float count;
float reliability;
2011-12-22 03:25:35 +01:00
public:
2011-12-23 02:43:32 +01:00
CAddrStat() : weight(0), count(0), reliability(0) {}
void Update(bool good, int64 age, double tau) {
double f = exp(-age/tau);
reliability = reliability * f + (good ? (1.0-f) : 0);
count = count * f + 1;
weight = weight * f + (1.0-f);
2011-12-22 03:25:35 +01:00
}
2011-12-23 02:43:32 +01:00
IMPLEMENT_SERIALIZE (
READWRITE(weight);
READWRITE(count);
READWRITE(reliability);
)
friend class CAddrInfo;
};
2011-12-22 03:25:35 +01:00
2011-12-31 00:39:53 +01:00
class CAddrReport {
public:
2012-05-25 15:41:27 +02:00
CService ip;
2011-12-31 00:39:53 +01:00
int clientVersion;
2012-05-25 16:05:14 +02:00
int blocks;
2012-02-23 18:01:09 +01:00
double uptime[5];
std::string clientSubVersion;
int64_t lastSuccess;
bool fGood;
uint64_t services;
2011-12-31 00:39:53 +01:00
};
2011-12-16 18:37:05 +01:00
class CAddrInfo {
private:
2012-05-25 15:41:27 +02:00
CService ip;
2011-12-16 18:37:05 +01:00
uint64_t services;
2011-12-20 08:35:22 +01:00
int64 lastTry;
int64 ourLastTry;
int64 ourLastSuccess;
2011-12-23 02:43:32 +01:00
int64 ignoreTill;
CAddrStat stat2H;
CAddrStat stat8H;
CAddrStat stat1D;
CAddrStat stat1W;
2012-02-23 18:01:09 +01:00
CAddrStat stat1M;
2011-12-23 02:43:32 +01:00
int clientVersion;
2012-05-25 16:05:14 +02:00
int blocks;
2011-12-19 23:18:06 +01:00
int total;
int success;
2012-02-23 18:01:09 +01:00
std::string clientSubVersion;
2011-12-19 23:18:06 +01:00
public:
CAddrInfo() : services(0), lastTry(0), ourLastTry(0), ourLastSuccess(0), ignoreTill(0), clientVersion(0), blocks(0), total(0), success(0) {}
2011-12-23 02:43:32 +01:00
2011-12-31 00:39:53 +01:00
CAddrReport GetReport() const {
CAddrReport ret;
ret.ip = ip;
ret.clientVersion = clientVersion;
2012-02-23 18:01:09 +01:00
ret.clientSubVersion = clientSubVersion;
2012-05-25 16:05:14 +02:00
ret.blocks = blocks;
2012-02-23 18:01:09 +01:00
ret.uptime[0] = stat2H.reliability;
ret.uptime[1] = stat8H.reliability;
ret.uptime[2] = stat1D.reliability;
ret.uptime[3] = stat1W.reliability;
ret.uptime[4] = stat1M.reliability;
ret.lastSuccess = ourLastSuccess;
ret.fGood = IsGood();
ret.services = services;
2011-12-31 00:39:53 +01:00
return ret;
}
bool IsGood() const {
2013-01-24 05:28:07 +01:00
if (ip.GetPort() != GetDefaultPort()) return false;
2011-12-23 02:43:32 +01:00
if (!(services & NODE_NETWORK)) return false;
if (!ip.IsRoutable()) return false;
if (clientVersion && clientVersion < REQUIRE_VERSION) return false;
if (blocks && blocks < GetRequireHeight()) return false;
2011-12-23 02:43:32 +01:00
if (total <= 3 && success * 2 >= total) return true;
2012-05-30 19:17:17 +02:00
if (stat2H.reliability > 0.85 && stat2H.count > 2) return true;
if (stat8H.reliability > 0.70 && stat8H.count > 4) return true;
if (stat1D.reliability > 0.55 && stat1D.count > 8) return true;
if (stat1W.reliability > 0.45 && stat1W.count > 16) return true;
if (stat1M.reliability > 0.35 && stat1M.count > 32) return true;
2011-12-23 02:43:32 +01:00
return false;
}
2011-12-31 00:39:53 +01:00
int GetBanTime() const {
2011-12-23 02:43:32 +01:00
if (IsGood()) return 0;
2012-05-30 16:51:44 +02:00
if (clientVersion && clientVersion < 31900) { return 604800; }
2013-06-09 09:31:39 +02:00
if (stat1M.reliability - stat1M.weight + 1.0 < 0.15 && stat1M.count > 32) { return 30*86400; }
if (stat1W.reliability - stat1W.weight + 1.0 < 0.10 && stat1W.count > 16) { return 7*86400; }
if (stat1D.reliability - stat1D.weight + 1.0 < 0.05 && stat1D.count > 8) { return 1*86400; }
2011-12-23 02:43:32 +01:00
return 0;
2011-12-19 23:18:06 +01:00
}
2011-12-31 00:39:53 +01:00
int GetIgnoreTime() const {
2011-12-23 02:43:32 +01:00
if (IsGood()) return 0;
2013-06-09 09:31:39 +02:00
if (stat1M.reliability - stat1M.weight + 1.0 < 0.20 && stat1M.count > 2) { return 10*86400; }
if (stat1W.reliability - stat1W.weight + 1.0 < 0.16 && stat1W.count > 2) { return 3*86400; }
if (stat1D.reliability - stat1D.weight + 1.0 < 0.12 && stat1D.count > 2) { return 8*3600; }
if (stat8H.reliability - stat8H.weight + 1.0 < 0.08 && stat8H.count > 2) { return 2*3600; }
2011-12-23 02:43:32 +01:00
return 0;
2011-12-19 23:18:06 +01:00
}
2011-12-23 02:43:32 +01:00
2011-12-19 23:45:18 +01:00
void Update(bool good);
2011-12-19 23:18:06 +01:00
friend class CAddrDb;
2011-12-20 08:35:22 +01:00
IMPLEMENT_SERIALIZE (
unsigned char version = 4;
2011-12-20 08:35:22 +01:00
READWRITE(version);
READWRITE(ip);
READWRITE(services);
READWRITE(lastTry);
unsigned char tried = ourLastTry != 0;
READWRITE(tried);
if (tried) {
READWRITE(ourLastTry);
READWRITE(ignoreTill);
READWRITE(stat2H);
READWRITE(stat8H);
READWRITE(stat1D);
READWRITE(stat1W);
2012-02-23 18:01:09 +01:00
if (version >= 1)
READWRITE(stat1M);
else
if (!fWrite)
*((CAddrStat*)(&stat1M)) = stat1W;
READWRITE(total);
READWRITE(success);
READWRITE(clientVersion);
2012-02-23 18:01:09 +01:00
if (version >= 2)
READWRITE(clientSubVersion);
2012-05-25 16:05:14 +02:00
if (version >= 3)
READWRITE(blocks);
if (version >= 4)
READWRITE(ourLastSuccess);
}
2011-12-20 08:35:22 +01:00
)
2011-12-19 23:18:06 +01:00
};
2011-12-16 18:37:05 +01:00
2011-12-26 01:04:24 +01:00
class CAddrDbStats {
public:
int nBanned;
int nAvail;
int nTracked;
int nNew;
int nGood;
2011-12-26 15:53:22 +01:00
int nAge;
2011-12-26 01:04:24 +01:00
};
2013-04-13 22:37:06 +02:00
struct CServiceResult {
CService service;
uint64_t services;
2013-04-13 22:37:06 +02:00
bool fGood;
int nBanTime;
int nHeight;
int nClientV;
std::string strClientV;
int64 ourLastSuccess;
2013-04-13 22:37:06 +02:00
};
2011-12-16 18:51:45 +01:00
// seen nodes
// / \
2011-12-20 14:29:21 +01:00
// (a) banned nodes available nodes--------------
// / | \
// tracked nodes (b) unknown nodes (e) active nodes
2011-12-16 18:51:45 +01:00
// / \
// (d) good nodes (c) non-good nodes
2011-12-16 18:37:05 +01:00
class CAddrDb {
private:
2011-12-20 08:35:22 +01:00
mutable CCriticalSection cs;
2011-12-16 18:37:05 +01:00
int nId; // number of address id's
2011-12-20 14:29:21 +01:00
std::map<int, CAddrInfo> idToInfo; // map address id to address info (b,c,d,e)
2012-05-25 15:41:27 +02:00
std::map<CService, int> ipToId; // map ip to id (b,c,d,e)
2011-12-19 23:18:06 +01:00
std::deque<int> ourId; // sequence of tried nodes, in order we have tried connecting to them (c,d)
std::set<int> unkId; // set of nodes not yet tried (b)
2011-12-20 14:29:21 +01:00
std::set<int> goodId; // set of good nodes (d, good e)
2011-12-23 02:43:32 +01:00
int nDirty;
2011-12-16 18:37:05 +01:00
2011-12-19 23:18:06 +01:00
protected:
2011-12-20 14:29:21 +01:00
// internal routines that assume proper locks are acquired
2011-12-26 01:04:24 +01:00
void Add_(const CAddress &addr, bool force); // add an address
bool Get_(CServiceResult &ip, int& wait); // get an IP to test (must call Good_, Bad_, or Skipped_ on result afterwards)
2013-04-13 22:37:06 +02:00
bool GetMany_(std::vector<CServiceResult> &ips, int max, int& wait);
void Good_(const CService &ip, int clientV, std::string clientSV, int blocks, uint64_t services); // mark an IP as good (must have been returned by Get_)
2012-05-25 15:41:27 +02:00
void Bad_(const CService &ip, int ban); // mark an IP as bad (and optionally ban it) (must have been returned by Get_)
void Skipped_(const CService &ip); // mark an IP as skipped (must have been returned by Get_)
int Lookup_(const CService &ip); // look up id of an IP
void GetIPs_(std::set<CNetAddr>& ips, uint64_t requestedFlags, int max, const bool *nets); // get a random set of IPs (shared lock only)
2011-12-16 18:37:05 +01:00
2011-12-19 23:18:06 +01:00
public:
2012-05-25 15:41:27 +02:00
std::map<CService, time_t> banned; // nodes that are banned, with their unban time (a)
2011-12-20 08:35:22 +01:00
2011-12-26 01:04:24 +01:00
void GetStats(CAddrDbStats &stats) {
SHARED_CRITICAL_BLOCK(cs) {
stats.nBanned = banned.size();
stats.nAvail = idToInfo.size();
stats.nTracked = ourId.size();
stats.nGood = goodId.size();
stats.nNew = unkId.size();
2011-12-26 15:53:22 +01:00
stats.nAge = time(NULL) - idToInfo[ourId[0]].ourLastTry;
2011-12-26 01:04:24 +01:00
}
}
2012-05-30 16:51:44 +02:00
void ResetIgnores() {
for (std::map<int, CAddrInfo>::iterator it = idToInfo.begin(); it != idToInfo.end(); it++) {
(*it).second.ignoreTill = 0;
}
}
2011-12-26 01:04:24 +01:00
2011-12-31 00:39:53 +01:00
std::vector<CAddrReport> GetAll() {
std::vector<CAddrReport> ret;
SHARED_CRITICAL_BLOCK(cs) {
for (std::deque<int>::const_iterator it = ourId.begin(); it != ourId.end(); it++) {
const CAddrInfo &info = idToInfo[*it];
if (info.success > 0) {
ret.push_back(info.GetReport());
}
}
}
return ret;
}
2011-12-23 02:43:32 +01:00
// serialization code
2011-12-20 14:29:21 +01:00
// format:
// nVersion (0 for now)
2011-12-23 02:43:32 +01:00
// n (number of ips in (b,c,d))
// CAddrInfo[n]
2011-12-20 14:29:21 +01:00
// banned
// acquires a shared lock (this does not suffice for read mode, but we assume that only happens at startup, single-threaded)
// this way, dumping does not interfere with GetIPs_, which is called from the DNS thread
2011-12-20 08:35:22 +01:00
IMPLEMENT_SERIALIZE (({
int nVersion = 0;
READWRITE(nVersion);
2011-12-20 09:31:28 +01:00
SHARED_CRITICAL_BLOCK(cs) {
2011-12-20 08:35:22 +01:00
if (fWrite) {
CAddrDb *db = const_cast<CAddrDb*>(this);
2011-12-23 02:43:32 +01:00
int n = ourId.size() + unkId.size();
READWRITE(n);
2011-12-20 08:35:22 +01:00
for (std::deque<int>::const_iterator it = ourId.begin(); it != ourId.end(); it++) {
std::map<int, CAddrInfo>::iterator ci = db->idToInfo.find(*it);
READWRITE((*ci).second);
}
for (std::set<int>::const_iterator it = unkId.begin(); it != unkId.end(); it++) {
std::map<int, CAddrInfo>::iterator ci = db->idToInfo.find(*it);
READWRITE((*ci).second);
}
} else {
CAddrDb *db = const_cast<CAddrDb*>(this);
db->nId = 0;
2011-12-23 02:43:32 +01:00
int n;
READWRITE(n);
for (int i=0; i<n; i++) {
2011-12-20 08:35:22 +01:00
CAddrInfo info;
READWRITE(info);
2011-12-23 02:43:32 +01:00
if (!info.GetBanTime()) {
2011-12-20 14:29:21 +01:00
int id = db->nId++;
db->idToInfo[id] = info;
db->ipToId[info.ip] = id;
2011-12-23 02:43:32 +01:00
if (info.ourLastTry) {
db->ourId.push_back(id);
if (info.IsGood()) db->goodId.insert(id);
} else {
db->unkId.insert(id);
}
2011-12-20 14:29:21 +01:00
}
2011-12-20 08:35:22 +01:00
}
2011-12-23 02:43:32 +01:00
db->nDirty++;
2011-12-20 08:35:22 +01:00
}
READWRITE(banned);
}
});)
2011-12-26 01:04:24 +01:00
void Add(const CAddress &addr, bool fForce = false) {
2011-12-19 23:18:06 +01:00
CRITICAL_BLOCK(cs)
2011-12-26 01:04:24 +01:00
Add_(addr, fForce);
2011-12-19 23:18:06 +01:00
}
2011-12-26 01:04:24 +01:00
void Add(const std::vector<CAddress> &vAddr, bool fForce = false) {
2011-12-19 23:18:06 +01:00
CRITICAL_BLOCK(cs)
for (int i=0; i<vAddr.size(); i++)
2011-12-26 01:04:24 +01:00
Add_(vAddr[i], fForce);
2011-12-19 23:18:06 +01:00
}
void Good(const CService &addr, int clientVersion, std::string clientSubVersion, int blocks, uint64_t services) {
2011-12-19 23:18:06 +01:00
CRITICAL_BLOCK(cs)
Good_(addr, clientVersion, clientSubVersion, blocks, services);
2011-12-19 23:18:06 +01:00
}
2012-05-25 15:41:27 +02:00
void Skipped(const CService &addr) {
2011-12-19 23:18:06 +01:00
CRITICAL_BLOCK(cs)
Skipped_(addr);
}
2012-05-25 15:41:27 +02:00
void Bad(const CService &addr, int ban = 0) {
2011-12-19 23:18:06 +01:00
CRITICAL_BLOCK(cs)
Bad_(addr, ban);
}
bool Get(CServiceResult &ip, int& wait) {
2011-12-19 23:18:06 +01:00
CRITICAL_BLOCK(cs)
2011-12-20 06:42:48 +01:00
return Get_(ip, wait);
2011-12-19 23:18:06 +01:00
}
2013-04-13 22:37:06 +02:00
void GetMany(std::vector<CServiceResult> &ips, int max, int& wait) {
CRITICAL_BLOCK(cs) {
while (max > 0) {
CServiceResult ip = {};
if (!Get_(ip, wait))
2013-04-13 22:37:06 +02:00
return;
ips.push_back(ip);
max--;
}
}
}
void ResultMany(const std::vector<CServiceResult> &ips) {
CRITICAL_BLOCK(cs) {
for (int i=0; i<ips.size(); i++) {
if (ips[i].fGood) {
Good_(ips[i].service, ips[i].nClientV, ips[i].strClientV, ips[i].nHeight, ips[i].services);
2013-04-13 22:37:06 +02:00
} else {
Bad_(ips[i].service, ips[i].nBanTime);
}
}
}
}
void GetIPs(std::set<CNetAddr>& ips, uint64_t requestedFlags, int max, const bool *nets) {
2011-12-20 09:31:28 +01:00
SHARED_CRITICAL_BLOCK(cs)
GetIPs_(ips, requestedFlags, max, nets);
2011-12-19 23:18:06 +01:00
}
};