upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/util.cpp')
-rw-r--r--src/util.cpp504
1 files changed, 504 insertions, 0 deletions
diff --git a/src/util.cpp b/src/util.cpp
new file mode 100644
index 0000000..d52ca88
--- /dev/null
+++ b/src/util.cpp
@@ -0,0 +1,504 @@
1// Copyright (c) 2009 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.
4
5#include "headers.h"
6
7
8
9bool fDebug = false;
10
11// ---------------------------------------------------------------------------
12// Categorized, burst-collapsing logging -- see the LogPrint doc comment in
13// util.h for the problem this solves.
14// ---------------------------------------------------------------------------
15
16// Categories that explain the pool/worker/payout pipeline are on unconditionally:
17// silence there is exactly the "worker stuck, nothing then happens" complaint
18// this exists to fix, so it can't be opted out of by forgetting -debug.
19// "net" and "nostr" cover the per-address connection/discovery chatter that
20// used to dominate debug.log; they're gated behind fDebug like the printf()
21// calls they replace, so default output stays quiet unless requested.
22static bool LogCategoryDefaultOn(const char* category)
23{
24 return strcmp(category, "pool") == 0 ||
25 strcmp(category, "worker") == 0 ||
26 strcmp(category, "payout") == 0;
27}
28
29bool LogAcceptsCategory(const char* category)
30{
31 if (LogCategoryDefaultOn(category))
32 return true;
33 return fDebug;
34}
35
36// Only collapse bursts from the exact same call site within this window;
37// a different LogPrint() call (different file/line) is never merged with
38// this one, so unrelated messages never get mixed into one summary.
39static const int64 DEDUP_WINDOW_SECS = 3;
40
41struct LogDedupState {
42 int64 windowStart; // GetTime() when the current window opened
43 int suppressed; // calls swallowed so far this window
44 std::string lastMsg; // most recent message text, for the summary line
45};
46
47static std::map<std::string, LogDedupState> g_logDedup;
48static CCriticalSection cs_logDedup;
49
50void LogPrintDedup(const char* category, const char* file, int line, const std::string& msgIn)
51{
52 // Call sites follow the existing printf() convention of ending their
53 // format string with \n; strip it here so the single \n this function
54 // adds below doesn't turn into a blank line.
55 std::string msg = msgIn;
56 while (!msg.empty() && (msg.back() == '\n' || msg.back() == '\r'))
57 msg.pop_back();
58
59 char key[512];
60 my_snprintf(key, sizeof(key), "%s|%s:%d", category, file, line);
61
62 int64 now = GetTime();
63 bool fEmitNow = false;
64 int suppressedToFlush = 0;
65 std::string flushedMsg;
66
67 CRITICAL_BLOCK(cs_logDedup)
68 {
69 std::map<std::string, LogDedupState>::iterator it = g_logDedup.find(key);
70 if (it == g_logDedup.end())
71 {
72 // First time this call site has fired (or its last window is long
73 // gone) -- print it directly and open a fresh window.
74 g_logDedup[key] = LogDedupState{now, 0, msg};
75 fEmitNow = true;
76 }
77 else if (now - it->second.windowStart >= DEDUP_WINDOW_SECS)
78 {
79 // Window rolled over: flush what was suppressed during it (if
80 // anything), then start a new window with this call printed.
81 suppressedToFlush = it->second.suppressed;
82 flushedMsg = it->second.lastMsg;
83 it->second.windowStart = now;
84 it->second.suppressed = 0;
85 it->second.lastMsg = msg;
86 fEmitNow = true;
87 }
88 else
89 {
90 // Still inside the window and already printed one line for it:
91 // count this occurrence instead of writing another near-duplicate.
92 it->second.suppressed++;
93 it->second.lastMsg = msg;
94 }
95 }
96
97 if (suppressedToFlush > 0)
98 OutputDebugStringF("[%s] (%d similar in %llds, last: %s)\n",
99 category, suppressedToFlush, (long long)DEDUP_WINDOW_SECS, flushedMsg.c_str());
100 if (fEmitNow)
101 OutputDebugStringF("[%s] %s\n", category, msg.c_str());
102}
103
104
105
106
107#ifdef _WIN32
108// Init openssl library multithreading support (OpenSSL < 1.1 needed callbacks;
109// OpenSSL 1.1+/3 is internally thread-safe, so the Linux build skips this).
110static HANDLE* lock_cs;
111
112void win32_locking_callback(int mode, int type, const char* file, int line)
113{
114 if (mode & CRYPTO_LOCK)
115 WaitForSingleObject(lock_cs[type], INFINITE);
116 else
117 ReleaseMutex(lock_cs[type]);
118}
119#endif
120
121// Init
122class CInit
123{
124public:
125 CInit()
126 {
127#ifdef _WIN32
128 // Init openssl library multithreading support
129 lock_cs = (HANDLE*)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(HANDLE));
130 for (int i = 0; i < CRYPTO_num_locks(); i++)
131 lock_cs[i] = CreateMutex(NULL,FALSE,NULL);
132 CRYPTO_set_locking_callback(win32_locking_callback);
133
134 // Seed random number generator with screen scrape and other hardware sources
135 RAND_screen();
136#endif
137 // Seed random number generator with perfmon / high-res timer data
138 RandAddSeed(true);
139 }
140 ~CInit()
141 {
142#ifdef _WIN32
143 // Shutdown openssl library multithreading support
144 CRYPTO_set_locking_callback(NULL);
145 for (int i =0 ; i < CRYPTO_num_locks(); i++)
146 CloseHandle(lock_cs[i]);
147 OPENSSL_free(lock_cs);
148#endif
149 }
150}
151instance_of_cinit;
152
153
154
155
156void RandAddSeed(bool fPerfmon)
157{
158#ifdef _WIN32
159 // Seed with CPU performance counter
160 LARGE_INTEGER PerformanceCount;
161 QueryPerformanceCounter(&PerformanceCount);
162 RAND_add(&PerformanceCount, sizeof(PerformanceCount), 1.5);
163 memset(&PerformanceCount, 0, sizeof(PerformanceCount));
164
165 static int64 nLastPerfmon;
166 if (fPerfmon || GetTime() > nLastPerfmon + 5 * 60)
167 {
168 nLastPerfmon = GetTime();
169
170 // Seed with the entire set of perfmon data
171 unsigned char pdata[250000];
172 memset(pdata, 0, sizeof(pdata));
173 unsigned long nSize = sizeof(pdata);
174 long ret = RegQueryValueEx(HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, pdata, &nSize);
175 RegCloseKey(HKEY_PERFORMANCE_DATA);
176 if (ret == ERROR_SUCCESS)
177 {
178 uint256 hash;
179 SHA256(pdata, nSize, (unsigned char*)&hash);
180 RAND_add(&hash, sizeof(hash), min(nSize/500.0, (double)sizeof(hash)));
181 hash = 0;
182 memset(pdata, 0, nSize);
183 printf("RandAddSeed() got %lu bytes of performance data\n", nSize);
184 }
185 }
186#else
187 // Seed with a high-resolution timer; OpenSSL also seeds itself from
188 // /dev/urandom, so this is just extra entropy. No perfmon on Linux.
189 (void)fPerfmon;
190 struct timespec ts;
191 clock_gettime(CLOCK_MONOTONIC, &ts);
192 RAND_add(&ts, sizeof(ts), 1.5);
193 memset(&ts, 0, sizeof(ts));
194#endif
195}
196
197
198
199
200
201
202
203
204
205
206// Safer snprintf
207// - prints up to limit-1 characters
208// - output string is always null terminated even if limit reached
209// - return value is the number of characters actually printed
210int my_snprintf(char* buffer, size_t limit, const char* format, ...)
211{
212 if (limit == 0)
213 return 0;
214 va_list arg_ptr;
215 va_start(arg_ptr, format);
216 int ret = _vsnprintf(buffer, limit, format, arg_ptr);
217 va_end(arg_ptr);
218 if (ret < 0 || ret >= limit)
219 {
220 ret = limit - 1;
221 buffer[limit-1] = 0;
222 }
223 return ret;
224}
225
226
227string strprintf(const char* format, ...)
228{
229 char buffer[50000];
230 char* p = buffer;
231 int limit = sizeof(buffer);
232 int ret;
233 loop
234 {
235 va_list arg_ptr;
236 va_start(arg_ptr, format);
237 ret = _vsnprintf(p, limit, format, arg_ptr);
238 va_end(arg_ptr);
239 if (ret >= 0 && ret < limit)
240 break;
241 if (p != buffer)
242 delete p;
243 limit *= 2;
244 p = new char[limit];
245 if (p == NULL)
246 throw std::bad_alloc();
247 }
248#ifdef _MSC_VER
249 // msvc optimisation
250 if (p == buffer)
251 return string(p, p+ret);
252#endif
253 string str(p, p+ret);
254 if (p != buffer)
255 delete p;
256 return str;
257}
258
259
260bool error(const char* format, ...)
261{
262 char buffer[50000];
263 int limit = sizeof(buffer);
264 va_list arg_ptr;
265 va_start(arg_ptr, format);
266 int ret = _vsnprintf(buffer, limit, format, arg_ptr);
267 va_end(arg_ptr);
268 if (ret < 0 || ret >= limit)
269 {
270 ret = limit - 1;
271 buffer[limit-1] = 0;
272 }
273 printf("ERROR: %s\n", buffer);
274 return false;
275}
276
277
278void PrintException(std::exception* pex, const char* pszThread)
279{
280 char pszModule[260];
281 pszModule[0] = '\0';
282#ifdef _WIN32
283 GetModuleFileName(NULL, pszModule, sizeof(pszModule));
284 _strlwr(pszModule);
285#else
286 strncpy(pszModule, "bitflash-node", sizeof(pszModule) - 1);
287#endif
288 char pszMessage[1000];
289 if (pex)
290 snprintf(pszMessage, sizeof(pszMessage),
291 "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
292 else
293 snprintf(pszMessage, sizeof(pszMessage),
294 "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread);
295 printf("\n\n************************\n%s", pszMessage);
296 // Do NOT rethrow here. Every caller of PrintException (via
297 // CATCH_PRINT_EXCEPTION) is a thread wrapped in its own
298 // "try { ThreadFoo2() } catch(...) { log, sleep, loop again }" self-healing
299 // retry loop -- that's the whole point of the wrapper. Rethrowing from
300 // inside that catch block escapes the retry loop entirely (nothing above
301 // it in the thread catches it) and takes down the whole process with
302 // std::terminate() on the very first transient error -- a flaky relay
303 // connection, a malformed message, anything -- instead of letting the
304 // thread log it, sleep 5s, and try again like it's designed to.
305 // Show error in GUI via repaint signal -- GUI polls g_errorMessage
306}
307
308
309void ParseString(const string& str, char c, vector<string>& v)
310{
311 unsigned int i1 = 0;
312 unsigned int i2;
313 do
314 {
315 i2 = str.find(c, i1);
316 v.push_back(str.substr(i1, i2-i1));
317 i1 = i2+1;
318 }
319 while (i2 != str.npos);
320}
321
322
323string FormatMoney(int64 n, bool fPlus)
324{
325 n /= CENT;
326 string str = strprintf("%lld.%02lld", (n > 0 ? n : -n)/100, (n > 0 ? n : -n)%100);
327 for (int i = 6; i < str.size(); i += 4)
328 if (isdigit(str[str.size() - i - 1]))
329 str.insert(str.size() - i, 1, ',');
330 if (n < 0)
331 str.insert((unsigned int)0, 1, '-');
332 else if (fPlus && n > 0)
333 str.insert((unsigned int)0, 1, '+');
334 return str;
335}
336
337bool ParseMoney(const char* pszIn, int64& nRet)
338{
339 string strWhole;
340 int64 nCents = 0;
341 const char* p = pszIn;
342 while (isspace(*p))
343 p++;
344 for (; *p; p++)
345 {
346 if (*p == ',' && p > pszIn && isdigit(p[-1]) && isdigit(p[1]) && isdigit(p[2]) && isdigit(p[3]) && !isdigit(p[4]))
347 continue;
348 if (*p == '.')
349 {
350 p++;
351 if (!isdigit(p[0]) || !isdigit(p[1]))
352 return false;
353 nCents = atoi64(p);
354 if (nCents < 0 || nCents > 99)
355 return false;
356 p += 2;
357 break;
358 }
359 if (isspace(*p))
360 break;
361 if (!isdigit(*p))
362 return false;
363 strWhole.insert(strWhole.end(), *p);
364 }
365 for (; *p; p++)
366 if (!isspace(*p))
367 return false;
368 if (strWhole.size() > 17)
369 return false;
370 int64 nWhole = atoi64(strWhole);
371 int64 nValue = nWhole * 100 + nCents;
372 if (nValue / 100 != nWhole)
373 return false;
374 nValue *= CENT;
375 nRet = nValue;
376 return true;
377}
378
379
380
381
382
383
384
385
386
387
388bool FileExists(const char* psz)
389{
390#ifdef WIN32
391 return GetFileAttributes(psz) != -1;
392#else
393 return access(psz, 0) != -1;
394#endif
395}
396
397int GetFilesize(FILE* file)
398{
399 int nSavePos = ftell(file);
400 int nFilesize = -1;
401 if (fseek(file, 0, SEEK_END) == 0)
402 nFilesize = ftell(file);
403 fseek(file, nSavePos, SEEK_SET);
404 return nFilesize;
405}
406
407
408
409
410
411
412
413
414uint64 GetRand(uint64 nMax)
415{
416 if (nMax == 0)
417 return 0;
418
419 // The range of the random source must be a multiple of the modulus
420 // to give every possible output value an equal possibility
421 uint64 nRange = (_UI64_MAX / nMax) * nMax;
422 uint64 nRand = 0;
423 do
424 RAND_bytes((unsigned char*)&nRand, sizeof(nRand));
425 while (nRand >= nRange);
426 return (nRand % nMax);
427}
428
429
430
431
432
433
434
435
436
437
438//
439// "Never go to sea with two chronometers; take one or three."
440// Our three chronometers are:
441// - System clock
442// - Median of other server's clocks
443// - NTP servers
444//
445// note: NTP isn't implemented yet, so until then we just use the median
446// of other nodes clocks to correct ours.
447//
448
449int64 GetTime()
450{
451 return time(NULL);
452}
453
454static int64 nTimeOffset = 0;
455
456int64 GetAdjustedTime()
457{
458 return GetTime() + nTimeOffset;
459}
460
461void AddTimeData(unsigned int ip, int64 nTime)
462{
463 int64 nOffsetSample = nTime - GetTime();
464
465 // Ignore duplicates
466 static set<unsigned int> setKnown;
467 if (!setKnown.insert(ip).second)
468 return;
469
470 // Add data
471 static vector<int64> vTimeOffsets;
472 if (vTimeOffsets.empty())
473 vTimeOffsets.push_back(0);
474 vTimeOffsets.push_back(nOffsetSample);
475 printf("Added time data, samples %d, ip %08x, offset %+lld (%+lld minutes)\n", (int)vTimeOffsets.size(), ip, vTimeOffsets.back(), vTimeOffsets.back()/60);
476 if (vTimeOffsets.size() >= 5 && vTimeOffsets.size() % 2 == 1)
477 {
478 sort(vTimeOffsets.begin(), vTimeOffsets.end());
479 int64 nMedian = vTimeOffsets[vTimeOffsets.size()/2];
480 nTimeOffset = nMedian;
481 if ((nMedian > 0 ? nMedian : -nMedian) > 5 * 60)
482 {
483 // Only let other nodes change our clock so far before we
484 // go to the NTP servers
485 /// todo: Get time from NTP servers, then set a flag
486 /// to make sure it doesn't get changed again
487 }
488 foreach(int64 n, vTimeOffsets)
489 printf("%+lld ", n);
490 printf("| nTimeOffset = %+lld (%+lld minutes)\n", nTimeOffset, nTimeOffset/60);
491 }
492}
493
494// Formats a block or transaction timestamp for logs and for the wallet view.
495// It lived in gui.cpp, but main.cpp logs block times with it, so a headless
496// build needed it too -- and nothing about formatting a date is GUI work.
497string DateTimeStr(int64 nTime)
498{
499 time_t t = (time_t)nTime;
500 struct tm* p = localtime(&t);
501 if (!p) return "";
502 char buf[32]; strftime(buf, sizeof(buf), "%m/%d/%y %H:%M", p);
503 return buf;
504}