Protocol update, set version to 60011
[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     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       
111       if (nVersion >= 209) {
112         BeginMessage("verack");
113         EndMessage();
114       }
115       vSend.SetVersion(min(nVersion, PROTOCOL_VERSION));
116       if (nVersion < 209) {
117         this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION));
118         GotVersion();
119       }
120       return false;
121     }
122     
123     if (strCommand == "verack") {
124       this->vRecv.SetVersion(min(nVersion, PROTOCOL_VERSION));
125       GotVersion();
126       return false;
127     }
128     
129     if (strCommand == "addr") {
130       vector<CAddress> vAddrNew;
131       vRecv >> vAddrNew;
132       // printf("%s: got %i addresses\n", ToString(you).c_str(), (int)vAddrNew.size());
133       int64 now = time(NULL);
134       vector<CAddress>::iterator it = vAddrNew.begin();
135       if (doneAfter == 0 || doneAfter > now + 1) doneAfter = now + 1;
136       while (it != vAddrNew.end()) {
137         CAddress &addr = *it;
138 //        printf("%s: got address %s\n", ToString(you).c_str(), addr.ToString().c_str(), (int)(vAddr->size()));
139         it++;
140         if (addr.nTime <= 100000000 || addr.nTime > now + 600)
141           addr.nTime = now - 5 * 86400;
142         if (addr.nTime > now - 604800)
143           vAddr->push_back(addr);
144 //        printf("%s: added address %s (#%i)\n", ToString(you).c_str(), addr.ToString().c_str(), (int)(vAddr->size()));
145         if (vAddr->size() > 1000) {doneAfter = 1; return true; }
146       }
147       return false;
148     }
149     
150     return false;
151   }
152   
153   bool ProcessMessages() {
154     if (vRecv.empty()) return false;
155     do {
156       CDataStream::iterator pstart = search(vRecv.begin(), vRecv.end(), BEGIN(pchMessageStart), END(pchMessageStart));
157       int nHeaderSize = vRecv.GetSerializeSize(CMessageHeader());
158       if (vRecv.end() - pstart < nHeaderSize) {
159         if (vRecv.size() > nHeaderSize) {
160           vRecv.erase(vRecv.begin(), vRecv.end() - nHeaderSize);
161         }
162         break;
163       }
164       vRecv.erase(vRecv.begin(), pstart);
165       vector<char> vHeaderSave(vRecv.begin(), vRecv.begin() + nHeaderSize);
166       CMessageHeader hdr;
167       vRecv >> hdr;
168       if (!hdr.IsValid()) { 
169         // printf("%s: BAD (invalid header)\n", ToString(you).c_str());
170         ban = 100000; return true;
171       }
172       string strCommand = hdr.GetCommand();
173       unsigned int nMessageSize = hdr.nMessageSize;
174       if (nMessageSize > MAX_SIZE) { 
175         // printf("%s: BAD (message too large)\n", ToString(you).c_str());
176         ban = 100000;
177         return true; 
178       }
179       if (nMessageSize > vRecv.size()) {
180         vRecv.insert(vRecv.begin(), vHeaderSave.begin(), vHeaderSave.end());
181         break;
182       }
183       uint256 hash = Hash(vRecv.begin(), vRecv.begin() + nMessageSize);
184       unsigned int nChecksum = 0;
185       memcpy(&nChecksum, &hash, sizeof(nChecksum));
186       if (nChecksum != hdr.nChecksum) continue;
187       CDataStream vMsg(vRecv.begin(), vRecv.begin() + nMessageSize, vRecv.nType, vRecv.nVersion);
188       vRecv.ignore(nMessageSize);
189       if (ProcessMessage(strCommand, vMsg))
190         return true;
191 //      printf("%s: done processing %s\n", ToString(you).c_str(), strCommand.c_str());
192     } while(1);
193     return false;
194   }
195   
196 public:
197   CNode(const CService& ip, vector<CAddress>& vAddrIn) : you(ip), nHeaderStart(-1), nMessageStart(-1), vAddr(&vAddrIn), ban(0), doneAfter(0), nVersion(0) {
198     vSend.SetType(SER_NETWORK);
199     vSend.SetVersion(0);
200     vRecv.SetType(SER_NETWORK);
201     vRecv.SetVersion(0);
202     if (time(NULL) > 1329696000) {
203       vSend.SetVersion(209);
204       vRecv.SetVersion(209);
205     }
206   }
207   bool Run() {
208     bool res = true;
209     if (!ConnectSocket(you, sock)) return false;
210     PushVersion();
211     Send();
212     int64 now;
213     while (now = time(NULL), ban == 0 && (doneAfter == 0 || doneAfter > now) && sock != INVALID_SOCKET) {
214       char pchBuf[0x10000];
215       fd_set set;
216       FD_ZERO(&set);
217       FD_SET(sock,&set);
218       struct timeval wa;
219       if (doneAfter) {
220         wa.tv_sec = doneAfter - now;
221         wa.tv_usec = 0;
222       } else {
223         wa.tv_sec = GetTimeout();
224         wa.tv_usec = 0;
225       }
226       int ret = select(sock+1, &set, NULL, &set, &wa);
227       if (ret != 1) {
228         if (!doneAfter) res = false;
229         break;
230       }
231       int nBytes = recv(sock, pchBuf, sizeof(pchBuf), 0);
232       int nPos = vRecv.size();
233       if (nBytes > 0) {
234         vRecv.resize(nPos + nBytes);
235         memcpy(&vRecv[nPos], pchBuf, nBytes);
236       } else if (nBytes == 0) {
237         // printf("%s: BAD (connection closed prematurely)\n", ToString(you).c_str());
238         res = false;
239         break;
240       } else {
241         // printf("%s: BAD (connection error)\n", ToString(you).c_str());
242         res = false;
243         break;
244       }
245       ProcessMessages();
246       Send();
247     }
248     if (sock == INVALID_SOCKET) res = false;
249     close(sock);
250     sock = INVALID_SOCKET;
251     return (ban == 0) && res;
252   }
253   
254   int GetBan() {
255     return ban;
256   }
257   
258   int GetClientVersion() {
259     return nVersion;
260   }
261   
262   std::string GetClientSubVersion() {
263     return strSubVer;
264   }
265   
266   int GetStartingHeight() {
267     return nStartingHeight;
268   }
269 };
270
271 bool TestNode(const CService &cip, int &ban, int &clientV, std::string &clientSV, int &blocks, vector<CAddress>& vAddr) {
272   try {
273     CNode node(cip, vAddr);
274     bool ret = node.Run();
275     if (!ret) {
276       ban = node.GetBan();
277     } else {
278       ban = 0;
279     }
280     clientV = node.GetClientVersion();
281     clientSV = node.GetClientSubVersion();
282     blocks = node.GetStartingHeight();
283 //  printf("%s: %s!!!\n", cip.ToString().c_str(), ret ? "GOOD" : "BAD");
284     return ret;
285   } catch(std::ios_base::failure& e) {
286     ban = 0;
287     return false;
288   }
289 }
290
291 /*
292 int main(void) {
293   CService ip("bitcoin.sipa.be", 8333, true);
294   vector<CAddress> vAddr;
295   vAddr.clear();
296   int ban = 0;
297   bool ret = TestNode(ip, ban, vAddr);
298   printf("ret=%s ban=%i vAddr.size()=%i\n", ret ? "good" : "bad", ban, (int)vAddr.size());
299 }
300 */
301