Add --nostatslog option
[novacoin-seeder.git] / db.cpp
1 #include "db.h"
2 #include <stdlib.h>
3
4 using namespace std;
5
6 void CAddrInfo::Update(bool good) {
7   uint32_t now = time(NULL);
8   if (ourLastTry == 0)
9     ourLastTry = now - MIN_RETRY;
10   int age = now - ourLastTry;
11   lastTry = now;
12   ourLastTry = now;
13   total++;
14   if (good) success++;
15   stat2H.Update(good, age, 3600*2);
16   stat8H.Update(good, age, 3600*8);
17   stat1D.Update(good, age, 3600*24);
18   stat1W.Update(good, age, 3600*24*7);
19   stat1M.Update(good, age, 3600*24*30);
20   int ign = GetIgnoreTime();
21   if (ign && (ignoreTill==0 || ignoreTill < ign+now)) ignoreTill = ign+now;
22 //  printf("%s: got %s result: success=%i/%i; 2H:%.2f%%-%.2f%%(%.2f) 8H:%.2f%%-%.2f%%(%.2f) 1D:%.2f%%-%.2f%%(%.2f) 1W:%.2f%%-%.2f%%(%.2f) \n", ToString(ip).c_str(), good ? "good" : "bad", success, total, 
23 //  100.0 * stat2H.reliability, 100.0 * (stat2H.reliability + 1.0 - stat2H.weight), stat2H.count,
24 //  100.0 * stat8H.reliability, 100.0 * (stat8H.reliability + 1.0 - stat8H.weight), stat8H.count,
25 //  100.0 * stat1D.reliability, 100.0 * (stat1D.reliability + 1.0 - stat1D.weight), stat1D.count,
26 //  100.0 * stat1W.reliability, 100.0 * (stat1W.reliability + 1.0 - stat1W.weight), stat1W.count);
27 }
28
29 bool CAddrDb::Get_(CService &ip, int &wait) {
30   int64 now = time(NULL);
31   int cont = 0;
32   int tot = unkId.size();
33   do {
34     deque<int>::iterator it = ourId.begin();
35     while (it < ourId.end()) {
36       if (now - idToInfo[*it].ourLastTry > MIN_RETRY) {
37         tot++;
38         it++;
39       } else {
40         break;
41       }
42     }
43     if (tot == 0) {
44       if (ourId.size() > 0) {
45         wait = MIN_RETRY - (now - idToInfo[ourId.front()].ourLastTry);
46       } else {
47         wait = 5;
48       }
49       return false;
50     }
51     int rnd = rand() % tot;
52     int ret;
53     if (rnd < unkId.size()) {
54       if (rnd*10 < unkId.size()) {
55         // once every 10 attempts, restart with the oldest unknown IP
56         set<int>::iterator it = unkId.begin();
57         ret = *it;
58       } else {
59         // 90% of the time try the last learned IP
60         set<int>::reverse_iterator it = unkId.rbegin();
61         ret = *it;
62       }
63       unkId.erase(ret);
64     } else {
65       ret = ourId.front();
66       if (time(NULL) - idToInfo[ret].ourLastTry < MIN_RETRY) return false;
67       ourId.pop_front();
68     }
69     if (idToInfo[ret].ignoreTill && idToInfo[ret].ignoreTill < now) {
70       ourId.push_back(ret);
71       idToInfo[ret].ourLastTry = now;
72     } else {
73       ip = idToInfo[ret].ip;
74       break;
75     }
76   } while(1);
77   nDirty++;
78   return true;
79 }
80
81 int CAddrDb::Lookup_(const CService &ip) {
82   if (ipToId.count(ip))
83     return ipToId[ip];
84   return -1;
85 }
86
87 void CAddrDb::Good_(const CService &addr, int clientV, std::string clientSV, int blocks) {
88   int id = Lookup_(addr);
89   if (id == -1) return;
90   unkId.erase(id);
91   banned.erase(addr);
92   CAddrInfo &info = idToInfo[id];
93   info.clientVersion = clientV;
94   info.clientSubVersion = clientSV;
95   info.blocks = blocks;
96   info.Update(true);
97   if (info.IsGood() && goodId.count(id)==0) {
98     goodId.insert(id);
99 //    printf("%s: good; %i good nodes now\n", ToString(addr).c_str(), (int)goodId.size());
100   }
101   nDirty++;
102   ourId.push_back(id);
103 }
104
105 void CAddrDb::Bad_(const CService &addr, int ban)
106 {
107   int id = Lookup_(addr);
108   if (id == -1) return;
109   unkId.erase(id);
110   CAddrInfo &info = idToInfo[id];
111   info.Update(false);
112   uint32_t now = time(NULL);
113   int ter = info.GetBanTime();
114   if (ter) {
115 //    printf("%s: terrible\n", ToString(addr).c_str());
116     if (ban < ter) ban = ter;
117   }
118   if (ban > 0) {
119 //    printf("%s: ban for %i seconds\n", ToString(addr).c_str(), ban);
120     banned[info.ip] = ban + now;
121     ipToId.erase(info.ip);
122     goodId.erase(id);
123     idToInfo.erase(id);
124   } else {
125     if (/*!info.IsGood() && */ goodId.count(id)==1) {
126       goodId.erase(id);
127 //      printf("%s: not good; %i good nodes left\n", ToString(addr).c_str(), (int)goodId.size());
128     }
129     ourId.push_back(id);
130   }
131   nDirty++;
132 }
133
134 void CAddrDb::Skipped_(const CService &addr)
135 {
136   int id = Lookup_(addr);
137   if (id == -1) return;
138   unkId.erase(id);
139   ourId.push_back(id);
140 //  printf("%s: skipped\n", ToString(addr).c_str());
141   nDirty++;
142 }
143
144
145 void CAddrDb::Add_(const CAddress &addr, bool force) {
146   if (!force && !addr.IsRoutable())
147     return;
148   CService ipp(addr);
149   if (banned.count(ipp)) {
150     time_t bantime = banned[ipp];
151     if (force || (bantime < time(NULL) && addr.nTime > bantime))
152       banned.erase(ipp);
153     else
154       return;
155   }
156   if (ipToId.count(ipp)) {
157     CAddrInfo &ai = idToInfo[ipToId[ipp]];
158     if (addr.nTime > ai.lastTry || ai.services != addr.nServices)
159     {
160       ai.lastTry = addr.nTime;
161       ai.services |= addr.nServices;
162 //      printf("%s: updated\n", ToString(addr).c_str());
163     }
164     if (force) {
165       ai.ignoreTill = 0;
166     }
167     return;
168   }
169   CAddrInfo ai;
170   ai.ip = ipp;
171   ai.services = addr.nServices;
172   ai.lastTry = addr.nTime;
173   ai.ourLastTry = 0;
174   ai.total = 0;
175   ai.success = 0;
176   int id = nId++;
177   idToInfo[id] = ai;
178   ipToId[ipp] = id;
179 //  printf("%s: added\n", ToString(ipp).c_str(), ipToId[ipp]);
180   unkId.insert(id);
181   nDirty++;
182 }
183
184 void CAddrDb::GetIPs_(set<CNetAddr>& ips, int max, const bool* nets) {
185   if (goodId.size() == 0) {
186     int id = -1;
187     if (ourId.size() == 0) {
188       if (unkId.size() == 0) return;
189       id = *unkId.begin();
190     } else {
191       id = *ourId.begin();
192     }
193     if (id >= 0) {
194       ips.insert(idToInfo[id].ip);
195     }
196     return;
197   }
198   if (max > goodId.size() / 2)
199     max = goodId.size() / 2;
200   if (max < 1)
201     max = 1;
202   int low = *goodId.begin();
203   int high = *goodId.rbegin();
204   set<int> ids;
205   while (ids.size() < max) {
206     int range = high-low+1;
207     int pos = low + (rand() % range);
208     int id = *(goodId.lower_bound(pos));
209     ids.insert(id);
210   }
211   for (set<int>::const_iterator it = ids.begin(); it != ids.end(); it++) {
212     CService &ip = idToInfo[*it].ip;
213     if (nets[ip.GetNetwork()])
214       ips.insert(ip);
215   }
216 }