diff --git a/src/seeder/db.h b/src/seeder/db.h index a170b74bc..25a4fd0d7 100644 --- a/src/seeder/db.h +++ b/src/seeder/db.h @@ -1,407 +1,404 @@ #ifndef BITCOIN_SEEDER_DB_H #define BITCOIN_SEEDER_DB_H #include "netbase.h" #include "protocol.h" +#include "sync.h" #include "util.h" #include #include #include #include #include #include #define MIN_RETRY 1000 #define REQUIRE_VERSION 70001 static inline int GetRequireHeight(const bool testnet = fTestNet) { return testnet ? 500000 : 350000; } static inline std::string ToString(const CService &ip) { std::string str = ip.ToString(); while (str.size() < 22) str += ' '; return str; } class CAddrStat { private: float weight; float count; float reliability; public: CAddrStat() : weight(0), count(0), reliability(0) {} void Update(bool good, int64_t 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); } IMPLEMENT_SERIALIZE(READWRITE(weight); READWRITE(count); READWRITE(reliability);) friend class CAddrInfo; }; class CAddrReport { public: CService ip; int clientVersion; int blocks; double uptime[5]; std::string clientSubVersion; int64_t lastSuccess; bool fGood; uint64_t services; }; class CAddrInfo { private: CService ip; uint64_t services; int64_t lastTry; int64_t ourLastTry; int64_t ourLastSuccess; int64_t ignoreTill; CAddrStat stat2H; CAddrStat stat8H; CAddrStat stat1D; CAddrStat stat1W; CAddrStat stat1M; int clientVersion; int blocks; int total; int success; std::string clientSubVersion; public: CAddrInfo() : services(0), lastTry(0), ourLastTry(0), ourLastSuccess(0), ignoreTill(0), clientVersion(0), blocks(0), total(0), success(0) {} CAddrReport GetReport() const { CAddrReport ret; ret.ip = ip; ret.clientVersion = clientVersion; ret.clientSubVersion = clientSubVersion; ret.blocks = blocks; 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; return ret; } bool IsGood() const { if (ip.GetPort() != GetDefaultPort()) return false; if (!(services & NODE_NETWORK)) return false; if (!(services & NODE_BITCOIN_CASH)) return false; if (!ip.IsRoutable()) return false; if (clientVersion && clientVersion < REQUIRE_VERSION) return false; if (blocks && blocks < GetRequireHeight()) return false; if (total <= 3 && success * 2 >= total) return true; 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; return false; } int GetBanTime() const { if (IsGood()) return 0; if (clientVersion && clientVersion < 31900) { return 604800; } 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; } return 0; } int GetIgnoreTime() const { if (IsGood()) return 0; 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; } return 0; } void Update(bool good); friend class CAddrDb; IMPLEMENT_SERIALIZE(uint8_t version = 4; READWRITE(version); READWRITE(ip); READWRITE(services); READWRITE(lastTry); uint8_t tried = ourLastTry != 0; READWRITE(tried); if (tried) { READWRITE(ourLastTry); READWRITE(ignoreTill); READWRITE(stat2H); READWRITE(stat8H); READWRITE(stat1D); READWRITE(stat1W); if (version >= 1) READWRITE(stat1M); else if (!fWrite) *((CAddrStat *)(&stat1M)) = stat1W; READWRITE(total); READWRITE(success); READWRITE(clientVersion); if (version >= 2) READWRITE(clientSubVersion); if (version >= 3) READWRITE(blocks); if (version >= 4) READWRITE(ourLastSuccess); }) }; class CAddrDbStats { public: int nBanned; int nAvail; int nTracked; int nNew; int nGood; int nAge; }; struct CServiceResult { CService service; bool fGood; int nBanTime; int nHeight; int nClientV; std::string strClientV; int64_t ourLastSuccess; }; // seen nodes // / \ // (a) banned nodes available nodes-------------- // / | \ // tracked nodes (b) unknown nodes (e) active nodes // / \ // (d) good nodes (c) non-good nodes class CAddrDb { private: mutable CCriticalSection cs; // number of address id's int nId; // map address id to address info (b,c,d,e) std::map idToInfo; // map ip to id (b,c,d,e) std::map ipToId; // sequence of tried nodes, in order we have tried connecting to them (c,d) std::deque ourId; // set of nodes not yet tried (b) std::set unkId; // set of good nodes (d, good e) std::set goodId; int nDirty; protected: // internal routines that assume proper locks are acquired // add an address void Add_(const CAddress &addr, bool force); // get an IP to test (must call Good_, Bad_, or Skipped_ on result // afterwards) bool Get_(CServiceResult &ip, int &wait); bool GetMany_(std::vector &ips, int max, int &wait); // mark an IP as good (must have been returned by Get_) void Good_(const CService &ip, int clientV, std::string clientSV, int blocks); // mark an IP as bad (and optionally ban it) (must have been returned by // Get_) void Bad_(const CService &ip, int ban); // mark an IP as skipped (must have been returned by Get_) void Skipped_(const CService &ip); // look up id of an IP int Lookup_(const CService &ip); // get a random set of IPs (shared lock only) void GetIPs_(std::set &ips, uint64_t requestedFlags, int max, const bool *nets); public: // nodes that are banned, with their unban time (a) std::map banned; 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(); - stats.nAge = time(nullptr) - idToInfo[ourId[0]].ourLastTry; - } + LOCK(cs); + stats.nBanned = banned.size(); + stats.nAvail = idToInfo.size(); + stats.nTracked = ourId.size(); + stats.nGood = goodId.size(); + stats.nNew = unkId.size(); + stats.nAge = time(nullptr) - idToInfo[ourId[0]].ourLastTry; } void ResetIgnores() { for (std::map::iterator it = idToInfo.begin(); it != idToInfo.end(); it++) { (*it).second.ignoreTill = 0; } } std::vector GetAll() { std::vector ret; - SHARED_CRITICAL_BLOCK(cs) { - for (std::deque::const_iterator it = ourId.begin(); - it != ourId.end(); it++) { - const CAddrInfo &info = idToInfo[*it]; - if (info.success > 0) { - ret.push_back(info.GetReport()); - } + LOCK(cs); + for (std::deque::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; } // serialization code // format: // nVersion (0 for now) // n (number of ips in (b,c,d)) // CAddrInfo[n] // 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 IMPLEMENT_SERIALIZE(({ int nVersion = 0; READWRITE(nVersion); - SHARED_CRITICAL_BLOCK(cs) { - if (fWrite) { - CAddrDb *db = const_cast(this); - int n = ourId.size() + unkId.size(); - READWRITE(n); - for (std::deque::const_iterator it = - ourId.begin(); - it != ourId.end(); it++) { - std::map::iterator ci = - db->idToInfo.find(*it); - READWRITE((*ci).second); - } - for (std::set::const_iterator it = - unkId.begin(); - it != unkId.end(); it++) { - std::map::iterator ci = - db->idToInfo.find(*it); - READWRITE((*ci).second); - } - } else { - CAddrDb *db = const_cast(this); - db->nId = 0; - int n; - READWRITE(n); - for (int i = 0; i < n; i++) { - CAddrInfo info; - READWRITE(info); - if (!info.GetBanTime()) { - int id = db->nId++; - db->idToInfo[id] = info; - db->ipToId[info.ip] = id; - if (info.ourLastTry) { - db->ourId.push_back(id); - if (info.IsGood()) - db->goodId.insert(id); - } else { - db->unkId.insert(id); - } + LOCK(cs); + if (fWrite) { + CAddrDb *db = const_cast(this); + int n = ourId.size() + unkId.size(); + READWRITE(n); + for (std::deque::const_iterator it = + ourId.begin(); + it != ourId.end(); it++) { + std::map::iterator ci = + db->idToInfo.find(*it); + READWRITE((*ci).second); + } + for (std::set::const_iterator it = + unkId.begin(); + it != unkId.end(); it++) { + std::map::iterator ci = + db->idToInfo.find(*it); + READWRITE((*ci).second); + } + } else { + CAddrDb *db = const_cast(this); + db->nId = 0; + int n; + READWRITE(n); + for (int i = 0; i < n; i++) { + CAddrInfo info; + READWRITE(info); + if (!info.GetBanTime()) { + int id = db->nId++; + db->idToInfo[id] = info; + db->ipToId[info.ip] = id; + if (info.ourLastTry) { + db->ourId.push_back(id); + if (info.IsGood()) + db->goodId.insert(id); + } else { + db->unkId.insert(id); } } - db->nDirty++; } - READWRITE(banned); + db->nDirty++; } + READWRITE(banned); });) void Add(const CAddress &addr, bool fForce = false) { - CRITICAL_BLOCK(cs) + LOCK(cs); Add_(addr, fForce); } void Add(const std::vector &vAddr, bool fForce = false) { - CRITICAL_BLOCK(cs) - for (int i = 0; i < vAddr.size(); i++) + LOCK(cs); + for (size_t i = 0; i < vAddr.size(); i++) { Add_(vAddr[i], fForce); + } } void Good(const CService &addr, int clientVersion, std::string clientSubVersion, int blocks) { - CRITICAL_BLOCK(cs) + LOCK(cs); Good_(addr, clientVersion, clientSubVersion, blocks); } void Skipped(const CService &addr) { - CRITICAL_BLOCK(cs) + LOCK(cs); Skipped_(addr); } void Bad(const CService &addr, int ban = 0) { - CRITICAL_BLOCK(cs) + LOCK(cs); Bad_(addr, ban); } bool Get(CServiceResult &ip, int &wait) { - CRITICAL_BLOCK(cs) + LOCK(cs); return Get_(ip, wait); } void GetMany(std::vector &ips, int max, int &wait) { - CRITICAL_BLOCK(cs) { - while (max > 0) { - CServiceResult ip = {}; - if (!Get_(ip, wait)) return; - ips.push_back(ip); - max--; - } + LOCK(cs); + while (max > 0) { + CServiceResult ip = {}; + if (!Get_(ip, wait)) return; + ips.push_back(ip); + max--; } } void ResultMany(const std::vector &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); - } else { - Bad_(ips[i].service, ips[i].nBanTime); - } + LOCK(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); + } else { + Bad_(ips[i].service, ips[i].nBanTime); } } } void GetIPs(std::set &ips, uint64_t requestedFlags, int max, const bool *nets) { - SHARED_CRITICAL_BLOCK(cs) + LOCK(cs); GetIPs_(ips, requestedFlags, max, nets); } }; #endif diff --git a/src/seeder/util.h b/src/seeder/util.h index bf63136dc..fcebac226 100644 --- a/src/seeder/util.h +++ b/src/seeder/util.h @@ -1,108 +1,70 @@ #ifndef BITCOIN_SEEDER_UTIL_H #define BITCOIN_SEEDER_UTIL_H #include #include #include #include #include "uint256.h" #define loop for (;;) #define BEGIN(a) ((char *)&(a)) #define END(a) ((char *)&((&(a))[1])) #define UBEGIN(a) ((uint8_t *)&(a)) #define UEND(a) ((uint8_t *)&((&(a))[1])) #define ARRAYLEN(array) (sizeof(array) / sizeof((array)[0])) #define WSAGetLastError() errno #define WSAEINVAL EINVAL #define WSAEALREADY EALREADY #define WSAEWOULDBLOCK EWOULDBLOCK #define WSAEMSGSIZE EMSGSIZE #define WSAEINTR EINTR #define WSAEINPROGRESS EINPROGRESS #define WSAEADDRINUSE EADDRINUSE #define WSAENOTSOCK EBADF #define INVALID_SOCKET (SOCKET)(~0) #define SOCKET_ERROR -1 -// Wrapper to automatically initialize mutex -class CCriticalSection { -protected: - pthread_rwlock_t mutex; - -public: - explicit CCriticalSection() { pthread_rwlock_init(&mutex, nullptr); } - ~CCriticalSection() { pthread_rwlock_destroy(&mutex); } - void Enter(bool fShared = false) { - if (fShared) { - pthread_rwlock_rdlock(&mutex); - } else { - pthread_rwlock_wrlock(&mutex); - } - } - void Leave() { pthread_rwlock_unlock(&mutex); } -}; - -// Automatically leave critical section when leaving block, needed for exception -// safety -class CCriticalBlock { -protected: - CCriticalSection *pcs; - -public: - CCriticalBlock(CCriticalSection &cs, bool fShared = false) : pcs(&cs) { - pcs->Enter(fShared); - } - operator bool() const { return true; } - ~CCriticalBlock() { pcs->Leave(); } -}; - -#define CRITICAL_BLOCK(cs) \ - if (CCriticalBlock criticalblock = CCriticalBlock(cs)) - -#define SHARED_CRITICAL_BLOCK(cs) \ - if (CCriticalBlock criticalblock = CCriticalBlock(cs, true)) - template inline uint256 Hash(const T1 pbegin, const T1 pend) { static uint8_t pblank[1]; uint256 hash1; SHA256((pbegin == pend ? pblank : (uint8_t *)&pbegin[0]), (pend - pbegin) * sizeof(pbegin[0]), (uint8_t *)&hash1); uint256 hash2; SHA256((uint8_t *)&hash1, sizeof(hash1), (uint8_t *)&hash2); return hash2; } static inline void Sleep(int nMilliSec) { struct timespec wa; wa.tv_sec = nMilliSec / 1000; wa.tv_nsec = (nMilliSec % 1000) * 1000000; nanosleep(&wa, nullptr); } std::string vstrprintf(const std::string &format, va_list ap); static inline std::string strprintf(const std::string &format, ...) { va_list arg_ptr; va_start(arg_ptr, format); std::string ret = vstrprintf(format, arg_ptr); va_end(arg_ptr); return ret; } static inline bool error(std::string err, ...) { return false; } static inline bool my_printf(std::string err, ...) { return true; } std::vector DecodeBase32(const char *p, bool *pfInvalid = nullptr); std::string DecodeBase32(const std::string &str); std::string EncodeBase32(const uint8_t *pch, size_t len); std::string EncodeBase32(const std::string &str); #endif