3ade33dfb4d5f7a1846297d761dcd30e5c052390
[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 0
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     if (vSend.GetVersion() >= 209) {
56       uint256 hash = Hash(vSend.begin() + nMessageStart, vSend.end());
57       unsigned int nChecksum = 0;
58       memcpy(&nChecksum, &hash, sizeof(nChecksum));
59       assert(nMessageStart - nHeaderStart >= offsetof(CMessageHeader, nChecksum) + sizeof(nChecksum));
60       memcpy((char*)&vSend[nHeaderStart] + offsetof(CMessageHeader, nChecksum), &nChecksum, sizeof(nChecksum));
61     }
62     nHeaderStart = -1;
63     nMessageStart = -1;
64   }
65   
66   void Send() {
67     if (sock == INVALID_SOCKET) return;
68     if (vSend.empty()) return;
69     int nBytes = send(sock, &vSend[0], vSend.size(), 0);
70     if (nBytes > 0) {
71       vSend.erase(vSend.begin(), vSend.begin() + nBytes);
72     } else {
73       close(sock);
74       sock = INVALID_SOCKET;
75     }
76   }
77   
78   void PushVersion() {
79     int64 nTime = time(NULL);
80     uint64 nLocalNonce = BITCOIN_SEED_NONCE;
81     int64 nLocalServices = 0;
82     CAddress me(CService("0.0.0.0"));
83     BeginMessage("version");
84     int nBestHeight = REQUIRE_HEIGHT;
85     string ver = "/novacoin-seeder:0.01/";
86     vSend << PROTOCOL_VERSION << nLocalServices << nTime << you << me << nLocalNonce << ver << nBestHeight;
87     EndMessage();
88   }
89  
90   void GotVersion() {
91     // printf("\n%s: version %i\n", ToString(you).c_str(), nVersion);
92     BeginMessage("getaddr");
93     EndMessage();
94     doneAfter = time(NULL) + GetTimeout();
95   }
96
97   bool ProcessMessage(string strCommand, CDataStream& vRecv) {
98     //printf("%s: RECV %s\n", ToString(you).c_str(), strCommand.c_str());
99     if (strCommand == "version") {
100       int64 nTime;
101       CAddress addrMe;
102       CAddress addrFrom;
103       uint64 nNonce = 1;
104       vRecv >> nVersion >> you.nServices >> nTime >> addrMe;
105       if (nVersion == 10300) nVersion = 300;
106       if (nVersion >= 106 && !vRecv.empty())
107         vRecv >> addrFrom >> nNonce;
108       if (nVersion >= 106 && !vRecv.empty())
109         vRecv >> strSubVer;
110       if (nVersion >= 209 && !vRecv.empty())
111         vRecv >> nStartingHeight;
112       
113       if (nVersion >= 209) {
114         BeginMessage("verack");
115         EndMessage();
116       }
117       vSend.SetVersion(min(nVersion, PROTOCOL_VERSION));
118       if (nVersion < 209) {
119         this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION));
120         GotVersion();
121       }
122       return false;
123     }
124     
125     if (strCommand == "verack") {
126       this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION));
127       GotVersion();
128       return false;
129     }
130     
131     if (strCommand == "addr") {
132       vector<CAddress> vAddrNew;
133       vRecv >> vAddrNew;
134       // printf("%s: got %i addresses\n", ToString(you).c_str(), (int)vAddrNew.size());
135       int64 now = time(NULL);
136       vector<CAddress>::iterator it = vAddrNew.begin();
137       if (doneAfter == 0 || doneAfter > now + 1) doneAfter = now + 1;
138       while (it != vAddrNew.end()) {
139         CAddress &addr = *it;
140 //        printf("%s: got address %s\n", ToString(you).c_str(), addr.ToString().c_str(), (int)(vAddr->size()));
141         it++;
142         if (addr.nTime <= 100000000 || addr.nTime > now + 600)
143           addr.nTime = now - 5 * 86400;
144         if (addr.nTime > now - 604800)
145           vAddr->push_back(addr);
146 //        printf("%s: added address %s (#%i)\n", ToString(you).c_str(), addr.ToString().c_str(), (int)(vAddr->size()));
147         if (vAddr->size() > 1000) {doneAfter = 1; return true; }
148       }
149       return false;
150     }
151     
152     return false;
153   }
154   
155   bool ProcessMessages() {
156     if (vRecv.empty()) return false;
157     do {
158       CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart));
159       int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader());
160       if (vRecv.end() - pstart < nHeaderSize) {
161         if (vRecv.size() > nHeaderSize) {
162           vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize);
163         }
164         break;
165       }
166       vRecv.erase(vRecv.begin(), pstart);
167       vector<char> vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize);
168       CMessageHeader hdr;
169       vRecv >> hdr;
170       if (!hdr.IsValid()) { 
171         // printf("%s: BAD (invalid header)\n", ToString(you).c_str());
172         ban = 100000; return true;
173       }
174       string strCommand = hdr.GetCommand();
175       unsigned int nMessageSize = hdr.nMessageSize;
176       if (nMessageSize > MAX_SIZE) { 
177         // printf("%s: BAD (message too large)\n", ToString(you).c_str());
178         ban = 100000;
179         return true; 
180       }
181       if (nMessageSize > vRecv.size()) {
182         vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end());
183         break;
184       }
185       if (vRecv.GetVersion() >= 209) {
186         uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
187         unsigned int nChecksum = 0;
188         memcpy(&nChecksum, &hash, sizeof(nChecksum));
189         if (nChecksum != hdr.nChecksum) continue;
190       }
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