Merge pull request #2 from svost/master
[novacoin-seeder.git] / bitcoin.cpp
1 #include <algorithm>
2
3 #include "db.h"
4 #include "netbase.h"
5 #include "protocol.h"
6 #include "serialize.h"
7 #include "uint256.h"
8
9 #define BITCOIN_SEED_NONCE  0x0539a019ca550825
10 #define REQUIRE_HEIGHT 142000
11 #define MIN_VERSION 40000
12
13 using namespace std;
14
15 class CNode {
16   SOCKET sock;
17   CDataStream vSend;
18   CDataStream vRecv;
19   unsigned int nHeaderStart;
20   unsigned int nMessageStart;
21   int nVersion;
22   string strSubVer;
23   int nStartingHeight;
24   vector<CAddress> *vAddr;
25   int ban;
26   int64 doneAfter;
27   CAddress you;
28
29   int GetTimeout() {
30       if (you.IsTor())
31           return 60;
32       else
33           return 10;
34   }
35
36   void BeginMessage(const char *pszCommand) {
37     if (nHeaderStart != -1) AbortMessage();
38     nHeaderStart = vSend.size();
39     vSend << CMessageHeader(pszCommand, 0);
40     nMessageStart = vSend.size();
41 //    printf("%s: SEND %s\n", ToString(you).c_str(), pszCommand); 
42   }
43   
44   void AbortMessage() {
45     if (nHeaderStart == -1) return;
46     vSend.resize(nHeaderStart);
47     nHeaderStart = -1;
48     nMessageStart = -1;
49   }
50   
51   void EndMessage() {
52     if (nHeaderStart == -1) return;
53     unsigned int nSize = vSend.size() - nMessageStart;
54     memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nMessageSize), &nSize, sizeof(nSize));
55     uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end());
56     unsigned int nChecksum = 0;
57     memcpy(&nChecksum, &hash, sizeof(nChecksum));
58     assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, nChecksum) + sizeof(nChecksum));
59     memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nChecksum), &nChecksum, sizeof(nChecksum));
60     nHeaderStart = -1;
61     nMessageStart = -1;
62   }
63   
64   void Send() {
65     if (sock == INVALID_SOCKET) return;
66     if (vSend.empty()) return;
67     int nBytes = send(sock, &vSend[0], vSend.size(), 0);
68     if (nBytes > 0) {
69       vSend.erase(vSend.begin(), vSend.begin() + nBytes);
70     } else {
71       close(sock);
72       sock = INVALID_SOCKET;
73     }
74   }
75   
76   void PushVersion() {
77     int64 nTime = time(NULL);
78     uint64 nLocalNonce = BITCOIN_SEED_NONCE;
79     int64 nLocalServices = 0;
80     CAddress me(CService("0.0.0.0"));
81     BeginMessage("version");
82     int nBestHeight = REQUIRE_HEIGHT;
83     string ver = "/novacoin-seeder:0.01/";
84     vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me << nLocalNonce << ver << nBestHeight;
85     EndMessage();
86   }
87  
88   void GotVersion() {
89     // printf("\n%s: version %i\n", ToString(you).c_str(), nVersion);
90     BeginMessage("getaddr");
91     EndMessage();
92     doneAfter = time(NULL) + GetTimeout();
93   }
94
95   bool ProcessMessage(string strCommand, CDataStream& vRecv) {
96     //printf("%s: RECV %s\n", ToString(you).c_str(), strCommand.c_str());
97     if (strCommand == "version") {
98       int64 nTime;
99       CAddress addrMe;
100       CAddress addrFrom;
101       uint64 nNonce = 1;
102       vRecv >> nVersion >> you.nServices >> nTime >> addrMe;
103       if (nVersion == 10300) nVersion = 300;
104       if (nVersion >= 106 && !vRecv.empty())
105         vRecv >> addrFrom >> nNonce;
106       if (nVersion >= 106 && !vRecv.empty())
107         vRecv >> strSubVer;
108       if (nVersion >= 209 && !vRecv.empty())
109         vRecv >> nStartingHeight;
110       if (nStartingHeight && nStartingHeight < REQUIRE_HEIGHT) {
111           // give them a day to catch up
112           ban = 86400;
113           return true;
114       }
115       if (nVersion >= 209) {
116         BeginMessage("verack");
117         EndMessage();
118       }
119       vSend.SetVersion(min(nVersion, PROTOCOL_VERSION));
120       if (nVersion < 209) {
121         this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION));
122         GotVersion();
123       }
124       return false;
125     }
126     
127     if (strCommand == "verack") {
128       this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION));
129       GotVersion();
130       return false;
131     }
132     
133     if (strCommand == "addr") {
134       vector<CAddress> vAddrNew;
135       vRecv >> vAddrNew;
136       // printf("%s: got %i addresses\n", ToString(you).c_str(), (int)vAddrNew.size());
137       int64 now = time(NULL);
138       vector<CAddress>::iterator it = vAddrNew.begin();
139       if (doneAfter == 0 || doneAfter > now + 1) doneAfter = now + 1;
140       while (it != vAddrNew.end()) {
141         CAddress &addr = *it;
142 //        printf("%s: got address %s\n", ToString(you).c_str(), addr.ToString().c_str(), (int)(vAddr->size()));
143         it++;
144         if (addr.nTime <= 100000000 || addr.nTime > now + 600)
145           addr.nTime = now - 5 * 86400;
146         if (addr.nTime > now - 604800)
147           vAddr->push_back(addr);
148 //        printf("%s: added address %s (#%i)\n", ToString(you).c_str(), addr.ToString().c_str(), (int)(vAddr->size()));
149         if (vAddr->size() > 1000) {doneAfter = 1; return true; }
150       }
151       return false;
152     }
153     
154     return false;
155   }
156   
157   bool ProcessMessages() {
158     if (vRecv.empty()) return false;
159     do {
160       CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart));
161       int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader());
162       if (vRecv.end() - pstart < nHeaderSize) {
163         if (vRecv.size() > nHeaderSize) {
164           vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize);
165         }
166         break;
167       }
168       vRecv.erase(vRecv.begin(), pstart);
169       vector<char> vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize);
170       CMessageHeader hdr;
171       vRecv >> hdr;
172       if (!hdr.IsValid()) { 
173         // printf("%s: BAD (invalid header)\n", ToString(you).c_str());
174         ban = 100000; return true;
175       }
176       string strCommand = hdr.GetCommand();
177       unsigned int nMessageSize = hdr.nMessageSize;
178       if (nMessageSize > MAX_SIZE) { 
179         // printf("%s: BAD (message too large)\n", ToString(you).c_str());
180         ban = 100000;
181         return true; 
182       }
183       if (nMessageSize > vRecv.size()) {
184         vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end());
185         break;
186       }
187       uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
188       unsigned int nChecksum = 0;
189       memcpy(&nChecksum, &hash, sizeof(nChecksum));
190       if (nChecksum != hdr.nChecksum) continue;
191       CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion);
192       vRecv.ignore(nMessageSize);
193       if (ProcessMessage(strCommand, vMsg))
194         return true;
195 //      printf("%s: done processing %s\n", ToString(you).c_str(), strCommand.c_str());
196     } while(1);
197     return false;
198   }
199   
200 public:
201   CNode(const CService& ip, vector<CAddress>& vAddrIn) : you(ip), nHeaderStart(-1), nMessageStart(-1), vAddr(&vAddrIn), ban(0), doneAfter(0), nVersion(0) {
202     vSend.SetType(SER_NETWORK);
203     vSend.SetVersion(0);
204     vRecv.SetType(SER_NETWORK);
205     vRecv.SetVersion(0);
206     if (time(NULL) > 1329696000) {
207       vSend.SetVersion(209);
208       vRecv.SetVersion(209);
209     }
210   }
211   bool Run() {
212     bool res = true;
213     if (!ConnectSocket(you, sock)) return false;
214     PushVersion();
215     Send();
216     int64 now;
217     while (now = time(NULL), ban == 0 && (doneAfter == 0 || doneAfter > now) && sock != INVALID_SOCKET) {
218       char pchBuf[0x10000];
219       fd_set set;
220       FD_ZERO(&set);
221       FD_SET(sock,&set);
222       struct timeval wa;
223       if (doneAfter) {
224         wa.tv_sec = doneAfter - now;
225         wa.tv_usec = 0;
226       } else {
227         wa.tv_sec = GetTimeout();
228         wa.tv_usec = 0;
229       }
230       int ret = select(sock+1, &set, NULL, &set, &wa);
231       if (ret != 1) {
232         if (!doneAfter) res = false;
233         break;
234       }
235       int nBytes = recv(sock, pchBuf, sizeof(pchBuf), 0);
236       int nPos = vRecv.size();
237       if (nBytes > 0) {
238         vRecv.resize(nPos + nBytes);
239         memcpy(&vRecv[nPos], pchBuf, nBytes);
240       } else if (nBytes == 0) {
241         // printf("%s: BAD (connection closed prematurely)\n", ToString(you).c_str());
242         res = false;
243         break;
244       } else {
245         // printf("%s: BAD (connection error)\n", ToString(you).c_str());
246         res = false;
247         break;
248       }
249       ProcessMessages();
250       Send();
251     }
252     if (sock == INVALID_SOCKET) res = false;
253     close(sock);
254     sock = INVALID_SOCKET;
255     return (ban == 0) && res;
256   }
257   
258   int GetBan() {
259     return ban;
260   }
261   
262   int GetClientVersion() {
263     return nVersion;
264   }
265   
266   std::string GetClientSubVersion() {
267     return strSubVer;
268   }
269   
270   int GetStartingHeight() {
271     return nStartingHeight;
272   }
273 };
274
275 bool TestNode(const CService &cip, int &ban, int &clientV, std::string &clientSV, int &blocks, vector<CAddress>& vAddr) {
276   try {
277     CNode node(cip, vAddr);
278     bool ret = node.Run();
279     if (!ret) {
280       ban = node.GetBan();
281     } else {
282       ban = 0;
283     }
284     clientV = node.GetClientVersion();
285     clientSV = node.GetClientSubVersion();
286     blocks = node.GetStartingHeight();
287 //  printf("%s: %s!!!\n", cip.ToString().c_str(), ret ? "GOOD" : "BAD");
288     return ret;
289   } catch(std::ios_base::failure& e) {
290     ban = 0;
291     return false;
292   }
293 }
294
295 /*
296 int main(void) {
297   CService ip("bitcoin.sipa.be", 8333, true);
298   vector<CAddress> vAddr;
299   vAddr.clear();
300   int ban = 0;
301   bool ret = TestNode(ip, ban, vAddr);
302   printf("ret=%s ban=%i vAddr.size()=%i\n", ret ? "good" : "bad", ban, (int)vAddr.size());
303 }
304 */
305