Merge remote branch 'refs/remotes/svn/trunk' into svn
[bitcoinplatinum.git] / util.cpp
blob607dc3f11e23e01f7bf7dae229784301626bd9d4
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
5 #include "headers.h"
8 map<string, string> mapArgs;
9 map<string, vector<string> > mapMultiArgs;
10 bool fDebug = false;
11 bool fPrintToConsole = false;
12 bool fPrintToDebugger = false;
13 char pszSetDataDir[MAX_PATH] = "";
14 bool fRequestShutdown = false;
15 bool fShutdown = false;
16 bool fDaemon = false;
17 bool fCommandLine = false;
18 string strMiscWarning;
19 bool fTestNet = false;
25 // Workaround for "multiple definition of `_tls_used'"
26 // http://svn.boost.org/trac/boost/ticket/4258
27 extern "C" void tss_cleanup_implemented() { }
33 // Init openssl library multithreading support
34 static boost::interprocess::interprocess_mutex** ppmutexOpenSSL;
35 void locking_callback(int mode, int i, const char* file, int line)
37 if (mode & CRYPTO_LOCK)
38 ppmutexOpenSSL[i]->lock();
39 else
40 ppmutexOpenSSL[i]->unlock();
43 // Init
44 class CInit
46 public:
47 CInit()
49 // Init openssl library multithreading support
50 ppmutexOpenSSL = (boost::interprocess::interprocess_mutex**)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(boost::interprocess::interprocess_mutex*));
51 for (int i = 0; i < CRYPTO_num_locks(); i++)
52 ppmutexOpenSSL[i] = new boost::interprocess::interprocess_mutex();
53 CRYPTO_set_locking_callback(locking_callback);
55 #ifdef __WXMSW__
56 // Seed random number generator with screen scrape and other hardware sources
57 RAND_screen();
58 #endif
60 // Seed random number generator with performance counter
61 RandAddSeed();
63 ~CInit()
65 // Shutdown openssl library multithreading support
66 CRYPTO_set_locking_callback(NULL);
67 for (int i = 0; i < CRYPTO_num_locks(); i++)
68 delete ppmutexOpenSSL[i];
69 OPENSSL_free(ppmutexOpenSSL);
72 instance_of_cinit;
81 void RandAddSeed()
83 // Seed with CPU performance counter
84 int64 nCounter = GetPerformanceCounter();
85 RAND_add(&nCounter, sizeof(nCounter), 1.5);
86 memset(&nCounter, 0, sizeof(nCounter));
89 void RandAddSeedPerfmon()
91 RandAddSeed();
93 // This can take up to 2 seconds, so only do it every 10 minutes
94 static int64 nLastPerfmon;
95 if (GetTime() < nLastPerfmon + 10 * 60)
96 return;
97 nLastPerfmon = GetTime();
99 #ifdef __WXMSW__
100 // Don't need this on Linux, OpenSSL automatically uses /dev/urandom
101 // Seed with the entire set of perfmon data
102 unsigned char pdata[250000];
103 memset(pdata, 0, sizeof(pdata));
104 unsigned long nSize = sizeof(pdata);
105 long ret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize);
106 RegCloseKey(HKEY_PERFORMANCE_DATA);
107 if (ret == ERROR_SUCCESS)
109 RAND_add(pdata, nSize, nSize/100.0);
110 memset(pdata, 0, nSize);
111 printf("%s RandAddSeed() %d bytes\n", DateTimeStrFormat("%x %H:%M", GetTime()).c_str(), nSize);
113 #endif
116 uint64 GetRand(uint64 nMax)
118 if (nMax == 0)
119 return 0;
121 // The range of the random source must be a multiple of the modulus
122 // to give every possible output value an equal possibility
123 uint64 nRange = (UINT64_MAX / nMax) * nMax;
124 uint64 nRand = 0;
126 RAND_bytes((unsigned char*)&nRand, sizeof(nRand));
127 while (nRand >= nRange);
128 return (nRand % nMax);
131 int GetRandInt(int nMax)
133 return GetRand(nMax);
146 inline int OutputDebugStringF(const char* pszFormat, ...)
148 int ret = 0;
149 if (fPrintToConsole)
151 // print to console
152 va_list arg_ptr;
153 va_start(arg_ptr, pszFormat);
154 ret = vprintf(pszFormat, arg_ptr);
155 va_end(arg_ptr);
157 else
159 // print to debug.log
160 static FILE* fileout = NULL;
162 if (!fileout)
164 char pszFile[MAX_PATH+100];
165 GetDataDir(pszFile);
166 strlcat(pszFile, "/debug.log", sizeof(pszFile));
167 fileout = fopen(pszFile, "a");
168 setbuf(fileout, NULL); // unbuffered
170 if (fileout)
172 //// Debug print useful for profiling
173 //fprintf(fileout, " %"PRI64d" ", GetTimeMillis());
174 va_list arg_ptr;
175 va_start(arg_ptr, pszFormat);
176 ret = vfprintf(fileout, pszFormat, arg_ptr);
177 va_end(arg_ptr);
178 fflush(fileout);
182 #ifdef __WXMSW__
183 if (fPrintToDebugger)
185 static CCriticalSection cs_OutputDebugStringF;
187 // accumulate a line at a time
188 CRITICAL_BLOCK(cs_OutputDebugStringF)
190 static char pszBuffer[50000];
191 static char* pend;
192 if (pend == NULL)
193 pend = pszBuffer;
194 va_list arg_ptr;
195 va_start(arg_ptr, pszFormat);
196 int limit = END(pszBuffer) - pend - 2;
197 int ret = _vsnprintf(pend, limit, pszFormat, arg_ptr);
198 va_end(arg_ptr);
199 if (ret < 0 || ret >= limit)
201 pend = END(pszBuffer) - 2;
202 *pend++ = '\n';
204 else
205 pend += ret;
206 *pend = '\0';
207 char* p1 = pszBuffer;
208 char* p2;
209 while (p2 = strchr(p1, '\n'))
211 p2++;
212 char c = *p2;
213 *p2 = '\0';
214 OutputDebugStringA(p1);
215 *p2 = c;
216 p1 = p2;
218 if (p1 != pszBuffer)
219 memmove(pszBuffer, p1, pend - p1 + 1);
220 pend -= (p1 - pszBuffer);
223 #endif
224 return ret;
228 // Safer snprintf
229 // - prints up to limit-1 characters
230 // - output string is always null terminated even if limit reached
231 // - return value is the number of characters actually printed
232 int my_snprintf(char* buffer, size_t limit, const char* format, ...)
234 if (limit == 0)
235 return 0;
236 va_list arg_ptr;
237 va_start(arg_ptr, format);
238 int ret = _vsnprintf(buffer, limit, format, arg_ptr);
239 va_end(arg_ptr);
240 if (ret < 0 || ret >= limit)
242 ret = limit - 1;
243 buffer[limit-1] = 0;
245 return ret;
249 string strprintf(const char* format, ...)
251 char buffer[50000];
252 char* p = buffer;
253 int limit = sizeof(buffer);
254 int ret;
255 loop
257 va_list arg_ptr;
258 va_start(arg_ptr, format);
259 ret = _vsnprintf(p, limit, format, arg_ptr);
260 va_end(arg_ptr);
261 if (ret >= 0 && ret < limit)
262 break;
263 if (p != buffer)
264 delete p;
265 limit *= 2;
266 p = new char[limit];
267 if (p == NULL)
268 throw std::bad_alloc();
270 string str(p, p+ret);
271 if (p != buffer)
272 delete p;
273 return str;
277 bool error(const char* format, ...)
279 char buffer[50000];
280 int limit = sizeof(buffer);
281 va_list arg_ptr;
282 va_start(arg_ptr, format);
283 int ret = _vsnprintf(buffer, limit, format, arg_ptr);
284 va_end(arg_ptr);
285 if (ret < 0 || ret >= limit)
287 ret = limit - 1;
288 buffer[limit-1] = 0;
290 printf("ERROR: %s\n", buffer);
291 return false;
295 void ParseString(const string& str, char c, vector<string>& v)
297 if (str.empty())
298 return;
299 string::size_type i1 = 0;
300 string::size_type i2;
301 loop
303 i2 = str.find(c, i1);
304 if (i2 == str.npos)
306 v.push_back(str.substr(i1));
307 return;
309 v.push_back(str.substr(i1, i2-i1));
310 i1 = i2+1;
315 string FormatMoney(int64 n, bool fPlus)
317 n /= CENT;
318 string str = strprintf("%"PRI64d".%02"PRI64d, (n > 0 ? n : -n)/100, (n > 0 ? n : -n)%100);
319 for (int i = 6; i < str.size(); i += 4)
320 if (isdigit(str[str.size() - i - 1]))
321 str.insert(str.size() - i, 1, ',');
322 if (n < 0)
323 str.insert((unsigned int)0, 1, '-');
324 else if (fPlus && n > 0)
325 str.insert((unsigned int)0, 1, '+');
326 return str;
330 bool ParseMoney(const string& str, int64& nRet)
332 return ParseMoney(str.c_str(), nRet);
335 bool ParseMoney(const char* pszIn, int64& nRet)
337 string strWhole;
338 int64 nCents = 0;
339 const char* p = pszIn;
340 while (isspace(*p))
341 p++;
342 for (; *p; p++)
344 if (*p == ',' && p > pszIn && isdigit(p[-1]) && isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3]) && !isdigit(p[4]))
345 continue;
346 if (*p == '.')
348 p++;
349 if (isdigit(*p))
351 nCents = 10 * (*p++ - '0');
352 if (isdigit(*p))
353 nCents += (*p++ - '0');
355 break;
357 if (isspace(*p))
358 break;
359 if (!isdigit(*p))
360 return false;
361 strWhole.insert(strWhole.end(), *p);
363 for (; *p; p++)
364 if (!isspace(*p))
365 return false;
366 if (strWhole.size() > 14)
367 return false;
368 if (nCents < 0 || nCents > 99)
369 return false;
370 int64 nWhole = atoi64(strWhole);
371 int64 nPreValue = nWhole * 100 + nCents;
372 int64 nValue = nPreValue * CENT;
373 if (nValue / CENT != nPreValue)
374 return false;
375 if (nValue / COIN != nWhole)
376 return false;
377 nRet = nValue;
378 return true;
382 vector<unsigned char> ParseHex(const char* psz)
384 static char phexdigit[256] =
385 { -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
386 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
387 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
388 0,1,2,3,4,5,6,7,8,9,-1,-1,-1,-1,-1,-1,
389 -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1,
390 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
391 -1,0xa,0xb,0xc,0xd,0xe,0xf,-1,-1,-1,-1,-1,-1,-1,-1,-1
392 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
393 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
394 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
395 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
396 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
397 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
398 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
399 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
400 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, };
402 // convert hex dump to vector
403 vector<unsigned char> vch;
404 loop
406 while (isspace(*psz))
407 psz++;
408 char c = phexdigit[(unsigned char)*psz++];
409 if (c == -1)
410 break;
411 unsigned char n = (c << 4);
412 c = phexdigit[(unsigned char)*psz++];
413 if (c == -1)
414 break;
415 n |= c;
416 vch.push_back(n);
418 return vch;
421 vector<unsigned char> ParseHex(const string& str)
423 return ParseHex(str.c_str());
427 void ParseParameters(int argc, char* argv[])
429 mapArgs.clear();
430 mapMultiArgs.clear();
431 for (int i = 1; i < argc; i++)
433 char psz[10000];
434 strlcpy(psz, argv[i], sizeof(psz));
435 char* pszValue = (char*)"";
436 if (strchr(psz, '='))
438 pszValue = strchr(psz, '=');
439 *pszValue++ = '\0';
441 #ifdef __WXMSW__
442 _strlwr(psz);
443 if (psz[0] == '/')
444 psz[0] = '-';
445 #endif
446 if (psz[0] != '-')
447 break;
448 mapArgs[psz] = pszValue;
449 mapMultiArgs[psz].push_back(pszValue);
454 const char* wxGetTranslation(const char* pszEnglish)
456 #ifdef GUI
457 // Wrapper of wxGetTranslation returning the same const char* type as was passed in
458 static CCriticalSection cs;
459 CRITICAL_BLOCK(cs)
461 // Look in cache
462 static map<string, char*> mapCache;
463 map<string, char*>::iterator mi = mapCache.find(pszEnglish);
464 if (mi != mapCache.end())
465 return (*mi).second;
467 // wxWidgets translation
468 wxString strTranslated = wxGetTranslation(wxString(pszEnglish, wxConvUTF8));
470 // We don't cache unknown strings because caller might be passing in a
471 // dynamic string and we would keep allocating memory for each variation.
472 if (strcmp(pszEnglish, strTranslated.utf8_str()) == 0)
473 return pszEnglish;
475 // Add to cache, memory doesn't need to be freed. We only cache because
476 // we must pass back a pointer to permanently allocated memory.
477 char* pszCached = new char[strlen(strTranslated.utf8_str())+1];
478 strcpy(pszCached, strTranslated.utf8_str());
479 mapCache[pszEnglish] = pszCached;
480 return pszCached;
482 return NULL;
483 #else
484 return pszEnglish;
485 #endif
489 bool WildcardMatch(const char* psz, const char* mask)
491 loop
493 switch (*mask)
495 case '\0':
496 return (*psz == '\0');
497 case '*':
498 return WildcardMatch(psz, mask+1) || (*psz && WildcardMatch(psz+1, mask));
499 case '?':
500 if (*psz == '\0')
501 return false;
502 break;
503 default:
504 if (*psz != *mask)
505 return false;
506 break;
508 psz++;
509 mask++;
513 bool WildcardMatch(const string& str, const string& mask)
515 return WildcardMatch(str.c_str(), mask.c_str());
525 void FormatException(char* pszMessage, std::exception* pex, const char* pszThread)
527 #ifdef __WXMSW__
528 char pszModule[MAX_PATH];
529 pszModule[0] = '\0';
530 GetModuleFileNameA(NULL, pszModule, sizeof(pszModule));
531 #else
532 const char* pszModule = "bitcoin";
533 #endif
534 if (pex)
535 snprintf(pszMessage, 1000,
536 "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
537 else
538 snprintf(pszMessage, 1000,
539 "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread);
542 void LogException(std::exception* pex, const char* pszThread)
544 char pszMessage[10000];
545 FormatException(pszMessage, pex, pszThread);
546 printf("\n%s", pszMessage);
549 void PrintException(std::exception* pex, const char* pszThread)
551 char pszMessage[10000];
552 FormatException(pszMessage, pex, pszThread);
553 printf("\n\n************************\n%s\n", pszMessage);
554 fprintf(stderr, "\n\n************************\n%s\n", pszMessage);
555 strMiscWarning = pszMessage;
556 #ifdef GUI
557 if (wxTheApp && !fDaemon)
558 MyMessageBox(pszMessage, "Bitcoin", wxOK | wxICON_ERROR);
559 #endif
560 throw;
563 void ThreadOneMessageBox(string strMessage)
565 // Skip message boxes if one is already open
566 static bool fMessageBoxOpen;
567 if (fMessageBoxOpen)
568 return;
569 fMessageBoxOpen = true;
570 ThreadSafeMessageBox(strMessage, "Bitcoin", wxOK | wxICON_EXCLAMATION);
571 fMessageBoxOpen = false;
574 void PrintExceptionContinue(std::exception* pex, const char* pszThread)
576 char pszMessage[10000];
577 FormatException(pszMessage, pex, pszThread);
578 printf("\n\n************************\n%s\n", pszMessage);
579 fprintf(stderr, "\n\n************************\n%s\n", pszMessage);
580 strMiscWarning = pszMessage;
581 #ifdef GUI
582 if (wxTheApp && !fDaemon)
583 boost::thread(boost::bind(ThreadOneMessageBox, string(pszMessage)));
584 #endif
594 #ifdef __WXMSW__
595 typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
597 string MyGetSpecialFolderPath(int nFolder, bool fCreate)
599 char pszPath[MAX_PATH+100] = "";
601 // SHGetSpecialFolderPath isn't always available on old Windows versions
602 HMODULE hShell32 = LoadLibraryA("shell32.dll");
603 if (hShell32)
605 PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath =
606 (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA");
607 if (pSHGetSpecialFolderPath)
608 (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate);
609 FreeModule(hShell32);
612 // Backup option
613 if (pszPath[0] == '\0')
615 if (nFolder == CSIDL_STARTUP)
617 strcpy(pszPath, getenv("USERPROFILE"));
618 strcat(pszPath, "\\Start Menu\\Programs\\Startup");
620 else if (nFolder == CSIDL_APPDATA)
622 strcpy(pszPath, getenv("APPDATA"));
626 return pszPath;
628 #endif
630 string GetDefaultDataDir()
632 // Windows: C:\Documents and Settings\username\Application Data\Bitcoin
633 // Mac: ~/Library/Application Support/Bitcoin
634 // Unix: ~/.bitcoin
635 #ifdef __WXMSW__
636 // Windows
637 return MyGetSpecialFolderPath(CSIDL_APPDATA, true) + "\\Bitcoin";
638 #else
639 char* pszHome = getenv("HOME");
640 if (pszHome == NULL || strlen(pszHome) == 0)
641 pszHome = (char*)"/";
642 string strHome = pszHome;
643 if (strHome[strHome.size()-1] != '/')
644 strHome += '/';
645 #ifdef __WXMAC_OSX__
646 // Mac
647 strHome += "Library/Application Support/";
648 filesystem::create_directory(strHome.c_str());
649 return strHome + "Bitcoin";
650 #else
651 // Unix
652 return strHome + ".bitcoin";
653 #endif
654 #endif
657 void GetDataDir(char* pszDir)
659 // pszDir must be at least MAX_PATH length.
660 int nVariation;
661 if (pszSetDataDir[0] != 0)
663 strlcpy(pszDir, pszSetDataDir, MAX_PATH);
664 nVariation = 0;
666 else
668 // This can be called during exceptions by printf, so we cache the
669 // value so we don't have to do memory allocations after that.
670 static char pszCachedDir[MAX_PATH];
671 if (pszCachedDir[0] == 0)
672 strlcpy(pszCachedDir, GetDefaultDataDir().c_str(), sizeof(pszCachedDir));
673 strlcpy(pszDir, pszCachedDir, MAX_PATH);
674 nVariation = 1;
676 if (fTestNet)
678 char* p = pszDir + strlen(pszDir);
679 if (p > pszDir && p[-1] != '/' && p[-1] != '\\')
680 *p++ = '/';
681 strcpy(p, "testnet");
682 nVariation += 2;
684 static bool pfMkdir[4];
685 if (!pfMkdir[nVariation])
687 pfMkdir[nVariation] = true;
688 filesystem::create_directory(pszDir);
692 string GetDataDir()
694 char pszDir[MAX_PATH];
695 GetDataDir(pszDir);
696 return pszDir;
699 string GetConfigFile()
701 namespace fs = boost::filesystem;
702 fs::path pathConfig(GetArg("-conf", "bitcoin.conf"));
703 if (!pathConfig.is_complete())
704 pathConfig = fs::path(GetDataDir()) / pathConfig;
705 return pathConfig.string();
708 void ReadConfigFile(map<string, string>& mapSettingsRet,
709 map<string, vector<string> >& mapMultiSettingsRet)
711 namespace fs = boost::filesystem;
712 namespace pod = boost::program_options::detail;
714 fs::ifstream streamConfig(GetConfigFile());
715 if (!streamConfig.good())
716 return;
718 set<string> setOptions;
719 setOptions.insert("*");
721 for (pod::config_file_iterator it(streamConfig, setOptions), end; it != end; ++it)
723 // Don't overwrite existing settings so command line settings override bitcoin.conf
724 string strKey = string("-") + it->string_key;
725 if (mapSettingsRet.count(strKey) == 0)
726 mapSettingsRet[strKey] = it->value[0];
727 mapMultiSettingsRet[strKey].push_back(it->value[0]);
731 int GetFilesize(FILE* file)
733 int nSavePos = ftell(file);
734 int nFilesize = -1;
735 if (fseek(file, 0, SEEK_END) == 0)
736 nFilesize = ftell(file);
737 fseek(file, nSavePos, SEEK_SET);
738 return nFilesize;
741 void ShrinkDebugFile()
743 // Scroll debug.log if it's getting too big
744 string strFile = GetDataDir() + "/debug.log";
745 FILE* file = fopen(strFile.c_str(), "r");
746 if (file && GetFilesize(file) > 10 * 1000000)
748 // Restart the file with some of the end
749 char pch[200000];
750 fseek(file, -sizeof(pch), SEEK_END);
751 int nBytes = fread(pch, 1, sizeof(pch), file);
752 fclose(file);
753 if (file = fopen(strFile.c_str(), "w"))
755 fwrite(pch, 1, nBytes, file);
756 fclose(file);
769 // "Never go to sea with two chronometers; take one or three."
770 // Our three time sources are:
771 // - System clock
772 // - Median of other nodes's clocks
773 // - The user (asking the user to fix the system clock if the first two disagree)
775 int64 GetTime()
777 return time(NULL);
780 static int64 nTimeOffset = 0;
782 int64 GetAdjustedTime()
784 return GetTime() + nTimeOffset;
787 void AddTimeData(unsigned int ip, int64 nTime)
789 int64 nOffsetSample = nTime - GetTime();
791 // Ignore duplicates
792 static set<unsigned int> setKnown;
793 if (!setKnown.insert(ip).second)
794 return;
796 // Add data
797 static vector<int64> vTimeOffsets;
798 if (vTimeOffsets.empty())
799 vTimeOffsets.push_back(0);
800 vTimeOffsets.push_back(nOffsetSample);
801 printf("Added time data, samples %d, offset %+"PRI64d" (%+"PRI64d" minutes)\n", vTimeOffsets.size(), vTimeOffsets.back(), vTimeOffsets.back()/60);
802 if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
804 sort(vTimeOffsets.begin(), vTimeOffsets.end());
805 int64 nMedian = vTimeOffsets[vTimeOffsets.size()/2];
806 // Only let other nodes change our time by so much
807 if (abs64(nMedian) < 70 * 60)
809 nTimeOffset = nMedian;
811 else
813 nTimeOffset = 0;
814 // If nobody else has the same time as us, give a warning
815 bool fMatch = false;
816 foreach(int64 nOffset, vTimeOffsets)
817 if (nOffset != 0 && abs64(nOffset) < 5 * 60)
818 fMatch = true;
819 static bool fDone;
820 if (!fMatch && !fDone)
822 fDone = true;
823 string strMessage = _("Warning: Please check that your computer's date and time are correct. If your clock is wrong Bitcoin will not work properly.");
824 strMiscWarning = strMessage;
825 printf("*** %s\n", strMessage.c_str());
826 boost::thread(boost::bind(ThreadSafeMessageBox, strMessage+" ", string("Bitcoin"), wxOK | wxICON_EXCLAMATION, (wxWindow*)NULL, -1, -1));
829 foreach(int64 n, vTimeOffsets)
830 printf("%+"PRI64d" ", n);
831 printf("| nTimeOffset = %+"PRI64d" (%+"PRI64d" minutes)\n", nTimeOffset, nTimeOffset/60);