Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2014 The Bitcoin developers
3 : // Copyright (c) 2014-2015 The Dash developers
4 : // Copyright (c) 2015-2022 The PIVX Core developers
5 : // Distributed under the MIT software license, see the accompanying
6 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 :
8 : #if defined(HAVE_CONFIG_H)
9 : #include "config/pivx-config.h"
10 : #endif
11 :
12 : #include "util/system.h"
13 :
14 : #include "chainparamsbase.h"
15 : #include "random.h"
16 : #include "support/allocators/zeroafterfree.h"
17 : #include "utilstrencodings.h"
18 : #include "utiltime.h"
19 :
20 : #include <librustzcash.h>
21 :
22 : #include <stdarg.h>
23 : #include <thread>
24 :
25 : #ifndef WIN32
26 : // for posix_fallocate
27 : #ifdef __linux__
28 :
29 : #ifdef _POSIX_C_SOURCE
30 : #undef _POSIX_C_SOURCE
31 : #endif
32 :
33 : #define _POSIX_C_SOURCE 200112L
34 :
35 : #endif // __linux__
36 :
37 : #include <algorithm>
38 : #include <fcntl.h>
39 : #include <sched.h>
40 : #include <sys/resource.h>
41 : #include <sys/stat.h>
42 :
43 : #else
44 :
45 : #ifdef _MSC_VER
46 : #pragma warning(disable : 4786)
47 : #pragma warning(disable : 4804)
48 : #pragma warning(disable : 4805)
49 : #pragma warning(disable : 4717)
50 : #endif
51 :
52 : #ifdef _WIN32_WINNT
53 : #undef _WIN32_WINNT
54 : #endif
55 : #define _WIN32_WINNT 0x0501
56 :
57 : #ifdef _WIN32_IE
58 : #undef _WIN32_IE
59 : #endif
60 : #define _WIN32_IE 0x0501
61 :
62 : #define WIN32_LEAN_AND_MEAN 1
63 : #ifndef NOMINMAX
64 : #define NOMINMAX
65 : #endif
66 : #include <codecvt>
67 :
68 : #include <io.h> /* for _commit */
69 : #include <shellapi.h>
70 : #include <shlobj.h>
71 : #endif
72 :
73 : #ifdef HAVE_SYS_PRCTL_H
74 : #include <sys/prctl.h>
75 : #endif
76 :
77 : #ifdef MAC_OSX
78 : #include <CoreFoundation/CoreFoundation.h>
79 : #endif
80 :
81 : const char * const PIVX_CONF_FILENAME = "pivx.conf";
82 : const char * const PIVX_MASTERNODE_CONF_FILENAME = "masternode.conf";
83 :
84 :
85 : // PIVX only features
86 : // Masternode
87 : std::atomic<bool> fMasterNode{false};
88 :
89 : ArgsManager gArgs;
90 :
91 : CTranslationInterface translationInterface;
92 :
93 2826 : bool CheckDiskSpace(const fs::path& dir, uint64_t additional_bytes)
94 : {
95 2826 : constexpr uint64_t min_disk_space = 52428800; // 50 MiB
96 :
97 2826 : uint64_t free_bytes_available = fs::space(dir).available;
98 2826 : return free_bytes_available >= min_disk_space + additional_bytes;
99 : }
100 :
101 : /** A map that contains all the currently held directory locks. After
102 : * successful locking, these will be held here until the global destructor
103 : * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks
104 : * is called.
105 : */
106 : static std::map<std::string, std::unique_ptr<fsbridge::FileLock>> dir_locks;
107 : /** Mutex to protect dir_locks. */
108 : static std::mutex cs_dir_locks;
109 :
110 1151 : bool LockDirectory(const fs::path& directory, const std::string& lockfile_name, bool probe_only)
111 : {
112 2302 : std::lock_guard<std::mutex> ulock(cs_dir_locks);
113 3453 : fs::path pathLockFile = directory / lockfile_name;
114 :
115 : // If a lock for this directory already exists in the map, don't try to re-lock it
116 1151 : if (dir_locks.count(pathLockFile.string())) {
117 10 : return true;
118 : }
119 :
120 : // Create empty lock file if it doesn't exist.
121 1141 : FILE* file = fsbridge::fopen(pathLockFile, "a");
122 1141 : if (file) fclose(file);
123 1141 : auto lock = std::make_unique<fsbridge::FileLock>(pathLockFile);
124 1141 : if (!lock->TryLock()) {
125 18 : return error("Error while attempting to lock directory %s: %s", directory.string(), lock->GetReason());
126 : }
127 1135 : if (!probe_only) {
128 : // Lock successful and we're not just probing, put it into the map
129 1141 : dir_locks.emplace(pathLockFile.string(), std::move(lock));
130 : }
131 : return true;
132 : }
133 :
134 3 : void ReleaseDirectoryLocks()
135 : {
136 3 : std::lock_guard<std::mutex> ulock(cs_dir_locks);
137 3 : dir_locks.clear();
138 3 : }
139 :
140 760 : bool DirIsWritable(const fs::path& directory)
141 : {
142 2280 : fs::path tmpFile = directory / fs::unique_path();
143 :
144 760 : FILE* file = fsbridge::fopen(tmpFile, "a");
145 760 : if (!file) return false;
146 :
147 759 : fclose(file);
148 760 : remove(tmpFile);
149 :
150 : return true;
151 : }
152 :
153 : /**
154 : * Interpret a string argument as a boolean.
155 : *
156 : * The definition of atoi() requires that non-numeric string values like "foo",
157 : * return 0. This means that if a user unintentionally supplies a non-integer
158 : * argument here, the return value is always false. This means that -foo=false
159 : * does what the user probably expects, but -foo=true is well defined but does
160 : * not do what they probably expected.
161 : *
162 : * The return value of atoi() is undefined when given input not representable as
163 : * an int. On most systems this means string value between "-2147483648" and
164 : * "2147483647" are well defined (this method will return true). Setting
165 : * -txindex=2147483648 on most systems, however, is probably undefined.
166 : *
167 : * For a more extensive discussion of this topic (and a wide range of opinions
168 : * on the Right Way to change this code), see upstream PR12713.
169 : */
170 4368 : static bool InterpretBool(const std::string& strValue)
171 : {
172 4368 : if (strValue.empty())
173 : return true;
174 3752 : return (atoi(strValue) != 0);
175 : }
176 :
177 : /** Internal helper functions for ArgsManager */
178 : class ArgsManagerHelper {
179 : public:
180 : typedef std::map<std::string, std::vector<std::string>> MapArgs;
181 :
182 : /** Determine whether to use config settings in the default section,
183 : * See also comments around ArgsManager::ArgsManager() below. */
184 2782343 : static inline bool UseDefaultSection(const ArgsManager& am, const std::string& arg)
185 : {
186 5563931 : return (am.m_network == CBaseChainParams::MAIN || am.m_network_only_args.count(arg) == 0);
187 : }
188 :
189 : /** Convert regular argument into the network-specific setting */
190 5585708 : static inline std::string NetworkArg(const ArgsManager& am, const std::string& arg)
191 : {
192 5585708 : assert(arg.length() > 1 && arg[0] == '-');
193 13319536 : return "-" + am.m_network + "." + arg.substr(1);
194 : }
195 :
196 : /** Find arguments in a map and add them to a vector */
197 13706 : static inline void AddArgs(std::vector<std::string>& res, const MapArgs& map_args, const std::string& arg)
198 : {
199 13706 : auto it = map_args.find(arg);
200 13706 : if (it != map_args.end()) {
201 3203 : res.insert(res.end(), it->second.begin(), it->second.end());
202 : }
203 13706 : }
204 :
205 : /** Return true/false if an argument is set in a map, and also
206 : * return the first (or last) of the possibly multiple values it has
207 : */
208 8369842 : static inline std::pair<bool,std::string> GetArgHelper(const MapArgs& map_args, const std::string& arg, bool getLast = false)
209 : {
210 8369842 : auto it = map_args.find(arg);
211 :
212 8369842 : if (it == map_args.end() || it->second.empty()) {
213 16691284 : return std::make_pair(false, std::string());
214 : }
215 :
216 24194 : if (getLast) {
217 10080 : return std::make_pair(true, it->second.back());
218 : } else {
219 14114 : return std::make_pair(true, it->second.front());
220 : }
221 : }
222 :
223 : /* Get the string value of an argument, returning a pair of a boolean
224 : * indicating the argument was found, and the value for the argument
225 : * if it was found (or the empty string if not found).
226 : */
227 2799303 : static inline std::pair<bool,std::string> GetArg(const ArgsManager& am, const std::string& arg)
228 : {
229 5598616 : LOCK(am.cs_args);
230 5598616 : std::pair<bool,std::string> found_result(false, std::string());
231 :
232 : // We pass "true" to GetArgHelper in order to return the last
233 : // argument value seen from the command line (so "pivxd -foo=bar
234 : // -foo=baz" gives GetArg(am,"foo")=={true,"baz"}
235 5598616 : found_result = GetArgHelper(am.m_override_args, arg, true);
236 2799303 : if (found_result.first) {
237 : return found_result;
238 : }
239 :
240 : // But in contrast we return the first argument seen in a config file,
241 : // so "foo=bar \n foo=baz" in the config file gives
242 : // GetArg(am,"foo")={true,"bar"}
243 2789667 : if (!am.m_network.empty()) {
244 5575060 : found_result = GetArgHelper(am.m_config_args, NetworkArg(am, arg));
245 2787530 : if (found_result.first) {
246 : return found_result;
247 : }
248 : }
249 :
250 2777043 : if (UseDefaultSection(am, arg)) {
251 5551082 : found_result = GetArgHelper(am.m_config_args, arg);
252 2775541 : if (found_result.first) {
253 : return found_result;
254 : }
255 : }
256 :
257 : return found_result;
258 : }
259 :
260 : /* Special test for -testnet and -regtest args, because we
261 : * don't want to be confused by craziness like "[regtest] testnet=1"
262 : */
263 884 : static inline bool GetNetBoolArg(const ArgsManager &am, const std::string& net_arg)
264 : {
265 1768 : std::pair<bool,std::string> found_result(false,std::string());
266 1768 : found_result = GetArgHelper(am.m_override_args, net_arg, true);
267 884 : if (!found_result.first) {
268 1704 : found_result = GetArgHelper(am.m_config_args, net_arg, true);
269 852 : if (!found_result.first) {
270 : return false; // not set
271 : }
272 : }
273 440 : return InterpretBool(found_result.second); // is set, so evaluate
274 : }
275 : };
276 :
277 : /**
278 : * Interpret -nofoo as if the user supplied -foo=0.
279 : *
280 : * This method also tracks when the -no form was supplied, and if so,
281 : * checks whether there was a double-negative (-nofoo=0 -> -foo=1).
282 : *
283 : * If there was not a double negative, it removes the "no" from the key,
284 : * and returns true, indicating the caller should clear the args vector
285 : * to indicate a negated option.
286 : *
287 : * If there was a double negative, it removes "no" from the key, sets the
288 : * value to "1" and returns false.
289 : *
290 : * If there was no "no", it leaves key and value untouched and returns
291 : * false.
292 : *
293 : * Where an option was negated can be later checked using the
294 : * IsArgNegated() method. One use case for this is to have a way to disable
295 : * options that are not normally boolean (e.g. using -nodebuglogfile to request
296 : * that debug log output is not sent to any file at all).
297 : */
298 8196 : static bool InterpretNegatedOption(std::string& key, std::string& val)
299 : {
300 8196 : assert(key[0] == '-');
301 :
302 8196 : size_t option_index = key.find('.');
303 8196 : if (option_index == std::string::npos) {
304 : option_index = 1;
305 : } else {
306 3935 : ++option_index;
307 : }
308 8196 : if (key.substr(option_index, 2) == "no") {
309 51 : bool bool_val = InterpretBool(val);
310 51 : key.erase(option_index, 2);
311 51 : if (!bool_val ) {
312 : // Double negatives like -nofoo=0 are supported (but discouraged)
313 8 : LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val);
314 8 : val = "1";
315 : } else {
316 : return true;
317 : }
318 : }
319 : return false;
320 : }
321 :
322 516 : ArgsManager::ArgsManager() :
323 : /* These options would cause cross-contamination if values for
324 : * mainnet were used while running on regtest/testnet (or vice-versa).
325 : * Setting them as section_only_args ensures that sharing a config file
326 : * between mainnet and regtest/testnet won't cause problems due to these
327 : * parameters by accident. */
328 : m_network_only_args{
329 : "-addnode", "-connect",
330 : "-port", "-bind",
331 : "-rpcport", "-rpcbind",
332 : "-wallet",
333 4128 : }
334 : {
335 : // nothing to do
336 516 : }
337 :
338 357 : void ArgsManager::WarnForSectionOnlyArgs()
339 : {
340 : // if there's no section selected, don't worry
341 357 : if (m_network.empty()) return;
342 :
343 : // if it's okay to use the default section for this network, don't worry
344 357 : if (m_network == CBaseChainParams::MAIN) return;
345 :
346 2856 : for (const auto& arg : m_network_only_args) {
347 2499 : std::pair<bool, std::string> found_result;
348 :
349 : // if this option is overridden it's fine
350 4998 : found_result = ArgsManagerHelper::GetArgHelper(m_override_args, arg);
351 4998 : if (found_result.first) continue;
352 :
353 : // if there's a network-specific value for this option, it's fine
354 4292 : found_result = ArgsManagerHelper::GetArgHelper(m_config_args, ArgsManagerHelper::NetworkArg(*this, arg));
355 2146 : if (found_result.first) continue;
356 :
357 : // if there isn't a default value for this option, it's fine
358 2172 : found_result = ArgsManagerHelper::GetArgHelper(m_config_args, arg);
359 1086 : if (!found_result.first) continue;
360 :
361 : // otherwise, issue a warning
362 0 : LogPrintf("Warning: Config setting for %s only applied on %s network when in [%s] section.\n", arg, m_network, m_network);
363 : }
364 : }
365 :
366 957 : void ArgsManager::SelectConfigNetwork(const std::string& network)
367 : {
368 957 : m_network = network;
369 957 : }
370 :
371 486 : void ArgsManager::ParseParameters(int argc, const char* const argv[])
372 : {
373 486 : LOCK(cs_args);
374 486 : m_override_args.clear();
375 :
376 4281 : for (int i = 1; i < argc; i++) {
377 7613 : std::string key(argv[i]);
378 7613 : std::string val;
379 3818 : size_t is_index = key.find('=');
380 3818 : if (is_index != std::string::npos) {
381 2802 : val = key.substr(is_index + 1);
382 2802 : key.erase(is_index);
383 : }
384 : #ifdef WIN32
385 : std::transform(key.begin(), key.end(), key.begin(), ::tolower);
386 : if (key[0] == '/')
387 : key[0] = '-';
388 : #endif
389 :
390 3818 : if (key[0] != '-')
391 : break;
392 :
393 : // Transform --foo to -foo
394 3795 : if (key.length() > 1 && key[1] == '-')
395 6 : key.erase(0, 1);
396 :
397 : // Check for -nofoo
398 3795 : if (InterpretNegatedOption(key, val)) {
399 3795 : m_override_args[key].clear();
400 : } else {
401 3762 : m_override_args[key].push_back(val);
402 : }
403 : }
404 486 : }
405 :
406 5312 : std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const
407 : {
408 5312 : std::vector<std::string> result = {};
409 5312 : if (IsArgNegated(strArg)) return result; // special case
410 :
411 10615 : LOCK(cs_args);
412 :
413 5303 : ArgsManagerHelper::AddArgs(result, m_override_args, strArg);
414 5303 : if (!m_network.empty()) {
415 10534 : ArgsManagerHelper::AddArgs(result, m_config_args, ArgsManagerHelper::NetworkArg(*this, strArg));
416 : }
417 :
418 5303 : if (ArgsManagerHelper::UseDefaultSection(*this, strArg)) {
419 3136 : ArgsManagerHelper::AddArgs(result, m_config_args, strArg);
420 : }
421 :
422 5303 : return result;
423 : }
424 :
425 395971 : bool ArgsManager::IsArgSet(const std::string& strArg) const
426 : {
427 395971 : if (IsArgNegated(strArg)) return true; // special case
428 396457 : return ArgsManagerHelper::GetArg(*this, strArg).first;
429 : }
430 :
431 2805111 : bool ArgsManager::IsArgNegated(const std::string& strArg) const
432 : {
433 5610222 : LOCK(cs_args);
434 :
435 2805111 : const auto& ov = m_override_args.find(strArg);
436 2805111 : if (ov != m_override_args.end()) return ov->second.empty();
437 :
438 2792975 : if (!m_network.empty()) {
439 5581538 : const auto& cfs = m_config_args.find(ArgsManagerHelper::NetworkArg(*this, strArg));
440 2790764 : if (cfs != m_config_args.end()) return cfs->second.empty();
441 : }
442 :
443 2779611 : const auto& cf = m_config_args.find(strArg);
444 2779611 : if (cf != m_config_args.end()) return cf->second.empty();
445 :
446 : return false;
447 : }
448 :
449 403910 : std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const
450 : {
451 403910 : if (IsArgNegated(strArg)) return "0";
452 807803 : std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg);
453 403893 : if (found_res.first) return found_res.second;
454 804863 : return strDefault;
455 : }
456 :
457 1471990 : int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const
458 : {
459 1471990 : if (IsArgNegated(strArg)) return 0;
460 2943980 : std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg);
461 1471990 : if (found_res.first) return atoi64(found_res.second);
462 : return nDefault;
463 : }
464 :
465 527506 : bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const
466 : {
467 527506 : if (IsArgNegated(strArg)) return false;
468 1054964 : std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg);
469 527459 : if (found_res.first) return InterpretBool(found_res.second);
470 : return fDefault;
471 : }
472 :
473 1229 : bool ArgsManager::SoftSetArg(const std::string& strArg, const std::string& strValue)
474 : {
475 2458 : LOCK(cs_args);
476 1229 : if (IsArgSet(strArg)) return false;
477 734 : ForceSetArg(strArg, strValue);
478 : return true;
479 : }
480 :
481 853 : bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue)
482 : {
483 853 : if (fValue)
484 767 : return SoftSetArg(strArg, std::string("1"));
485 : else
486 86 : return SoftSetArg(strArg, std::string("0"));
487 : }
488 :
489 910 : void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue)
490 : {
491 910 : LOCK(cs_args);
492 2730 : m_override_args[strArg] = {strValue};
493 910 : }
494 :
495 : static const int screenWidth = 79;
496 : static const int optIndent = 2;
497 : static const int msgIndent = 7;
498 :
499 11 : std::string HelpMessageGroup(const std::string &message) {
500 33 : return std::string(message) + std::string("\n\n");
501 : }
502 :
503 119 : std::string HelpMessageOpt(const std::string &option, const std::string &message) {
504 411 : return std::string(optIndent,' ') + std::string(option) +
505 476 : std::string("\n") + std::string(msgIndent,' ') +
506 357 : FormatParagraph(message, screenWidth - msgIndent, msgIndent) +
507 357 : std::string("\n\n");
508 : }
509 :
510 3 : static std::string FormatException(const std::exception* pex, const char* pszThread)
511 : {
512 : #ifdef WIN32
513 : char pszModule[MAX_PATH] = "";
514 : GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule));
515 : #else
516 3 : const char* pszModule = "pivx";
517 : #endif
518 3 : if (pex)
519 3 : return strprintf(
520 3 : "EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread);
521 : else
522 0 : return strprintf(
523 0 : "UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread);
524 : }
525 :
526 3 : void PrintExceptionContinue(const std::exception* pex, const char* pszThread)
527 : {
528 3 : std::string message = FormatException(pex, pszThread);
529 3 : LogPrintf("\n\n************************\n%s\n", message);
530 3 : fprintf(stderr, "\n\n************************\n%s\n", message.c_str());
531 3 : }
532 :
533 376 : fs::path GetDefaultDataDir()
534 : {
535 : // Windows < Vista: C:\Documents and Settings\Username\Application Data\PIVX
536 : // Windows >= Vista: C:\Users\Username\AppData\Roaming\PIVX
537 : // Mac: ~/Library/Application Support/PIVX
538 : // Unix: ~/.pivx
539 : #ifdef WIN32
540 : // Windows
541 : return GetSpecialFolderPath(CSIDL_APPDATA) / "PIVX";
542 : #else
543 376 : fs::path pathRet;
544 376 : char* pszHome = getenv("HOME");
545 376 : if (pszHome == nullptr || strlen(pszHome) == 0)
546 0 : pathRet = fs::path("/");
547 : else
548 376 : pathRet = fs::path(pszHome);
549 : #ifdef MAC_OSX
550 : // Mac
551 : pathRet /= "Library/Application Support";
552 : TryCreateDirectories(pathRet);
553 : return pathRet / "PIVX";
554 : #else
555 : // Unix
556 752 : return pathRet / ".pivx";
557 : #endif
558 : #endif
559 : }
560 :
561 : static fs::path g_blocks_path_cache_net_specific;
562 : static fs::path pathCached;
563 : static fs::path pathCachedNetSpecific;
564 : static fs::path zc_paramsPathCached;
565 : static RecursiveMutex csPathCached;
566 :
567 387 : static fs::path ZC_GetBaseParamsDir()
568 : {
569 : // Copied from GetDefaultDataDir and adapter for zcash params.
570 : // Windows < Vista: C:\Documents and Settings\Username\Application Data\PIVXParams
571 : // Windows >= Vista: C:\Users\Username\AppData\Roaming\PIVXParams
572 : // Mac: ~/Library/Application Support/PIVXParams
573 : // Unix: ~/.pivx-params
574 : #ifdef WIN32
575 : // Windows
576 : return GetSpecialFolderPath(CSIDL_APPDATA) / "PIVXParams";
577 : #else
578 387 : fs::path pathRet;
579 387 : char* pszHome = getenv("HOME");
580 387 : if (pszHome == nullptr || strlen(pszHome) == 0)
581 0 : pathRet = fs::path("/");
582 : else
583 387 : pathRet = fs::path(pszHome);
584 : #ifdef MAC_OSX
585 : // Mac
586 : pathRet /= "Library/Application Support";
587 : TryCreateDirectories(pathRet);
588 : return pathRet / "PIVXParams";
589 : #else
590 : // Unix
591 774 : return pathRet / ".pivx-params";
592 : #endif
593 : #endif
594 : }
595 :
596 424 : const fs::path &ZC_GetParamsDir()
597 : {
598 848 : LOCK(csPathCached); // Reuse the same lock as upstream.
599 :
600 424 : fs::path &path = zc_paramsPathCached;
601 :
602 : // This can be called during exceptions by LogPrintf(), so we cache the
603 : // value so we don't have to do memory allocations after that.
604 424 : if (!path.empty())
605 : return path;
606 :
607 : #ifdef USE_CUSTOM_PARAMS
608 : path = fs::system_complete(PARAMS_DIR);
609 : #else
610 387 : if (gArgs.IsArgSet("-paramsdir")) {
611 0 : path = fs::system_complete(gArgs.GetArg("-paramsdir", ""));
612 0 : if (!fs::is_directory(path)) {
613 424 : path = "";
614 : return path;
615 : }
616 : } else {
617 774 : path = ZC_GetBaseParamsDir();
618 : }
619 : #endif
620 :
621 : return path;
622 : }
623 :
624 423 : void initZKSNARKS()
625 : {
626 423 : const fs::path& path = ZC_GetParamsDir();
627 423 : fs::path sapling_spend = path / "sapling-spend.params";
628 846 : fs::path sapling_output = path / "sapling-output.params";
629 :
630 423 : bool fParamsFound = false;
631 846 : if (fs::exists(sapling_spend) && fs::exists(sapling_output)) {
632 : fParamsFound = true;
633 : } else {
634 : #ifdef MAC_OSX
635 : // macOS fallback path for params located within the app bundle
636 : // This is a somewhat convoluted series of CoreFoundation calls
637 : // that will result in the full path to the app bundle's "Resources"
638 : // directory, which will contain the sapling params.
639 : LogPrintf("Attempting to find params in app bundle...\n");
640 : CFBundleRef mainBundle = CFBundleGetMainBundle();
641 : CFURLRef bundleURL = CFBundleCopyBundleURL(mainBundle);
642 :
643 : CFStringRef strBundlePath = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle);
644 : const char* pathBundle = CFStringGetCStringPtr(strBundlePath, CFStringGetSystemEncoding());
645 :
646 : fs::path bundle_path = fs::path(pathBundle);
647 : LogPrintf("App bundle Resources path: %s\n", bundle_path);
648 : sapling_spend = bundle_path / "Contents/Resources/sapling-spend.params";
649 : sapling_output = bundle_path / "Contents/Resources/sapling-output.params";
650 :
651 : // Release the CF objects
652 : CFRelease(strBundlePath);
653 : CFRelease(bundleURL);
654 : CFRelease(mainBundle);
655 : #else
656 : // Linux fallback path for debuild/ppa based installs
657 0 : sapling_spend = "/usr/share/pivx/sapling-spend.params";
658 0 : sapling_output = "/usr/share/pivx/sapling-output.params";
659 0 : if (fs::exists(sapling_spend) && fs::exists(sapling_output)) {
660 : fParamsFound = true;
661 : } else {
662 : // Linux fallback for local installs
663 0 : sapling_spend = "/usr/local/share/pivx/sapling-spend.params";
664 0 : sapling_output = "/usr/local/share/pivx/sapling-output.params";
665 : }
666 : #endif
667 0 : if (fs::exists(sapling_spend) && fs::exists(sapling_output))
668 0 : fParamsFound = true;
669 : }
670 423 : if (!fParamsFound)
671 0 : throw std::runtime_error("Sapling params don't exist");
672 :
673 423 : static_assert(
674 : sizeof(fs::path::value_type) == sizeof(codeunit),
675 : "librustzcash not configured correctly");
676 846 : auto sapling_spend_str = sapling_spend.native();
677 846 : auto sapling_output_str = sapling_output.native();
678 :
679 : //LogPrintf("Loading Sapling (Spend) parameters from %s\n", sapling_spend.string().c_str());
680 :
681 423 : librustzcash_init_zksnark_params(
682 423 : reinterpret_cast<const codeunit*>(sapling_spend_str.c_str()),
683 : sapling_spend_str.length(),
684 : "8270785a1a0d0bc77196f000ee6d221c9c9894f55307bd9357c3f0105d31ca63991ab91324160d8f53e2bbd3c2633a6eb8bdf5205d822e7f3f73edac51b2b70c",
685 423 : reinterpret_cast<const codeunit*>(sapling_output_str.c_str()),
686 : sapling_output_str.length(),
687 : "657e3d38dbb5cb5e7dd2970e8b03d69b4787dd907285b5a7f0790dcc8072f60bf593b32cc2d1c030e00ff5ae64bf84c5c3beb84ddc841d48264b4a171744d028",
688 : nullptr, // sprout_path
689 : 0, // sprout_path_len
690 : "" // sprout_hash
691 : );
692 :
693 : //std::cout << "### Sapling params initialized ###" << std::endl;
694 423 : }
695 :
696 457332 : const fs::path &GetBlocksDir()
697 : {
698 :
699 914664 : LOCK(csPathCached);
700 :
701 457332 : fs::path &path = g_blocks_path_cache_net_specific;
702 :
703 : // This can be called during exceptions by LogPrintf(), so we cache the
704 : // value so we don't have to do memory allocations after that.
705 457332 : if (!path.empty())
706 : return path;
707 :
708 505 : if (gArgs.IsArgSet("-blocksdir")) {
709 8 : path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
710 2 : if (!fs::is_directory(path)) {
711 1 : path = "";
712 : return path;
713 : }
714 : } else {
715 503 : path = GetDataDir(false);
716 : }
717 :
718 504 : path /= BaseParams().DataDir();
719 504 : path /= "blocks";
720 457332 : fs::create_directories(path);
721 : return path;
722 : }
723 :
724 18944 : const fs::path& GetDataDir(bool fNetSpecific)
725 : {
726 37888 : LOCK(csPathCached);
727 :
728 18944 : fs::path& path = fNetSpecific ? pathCachedNetSpecific : pathCached;
729 :
730 : // This can be called during exceptions by LogPrintf(), so we cache the
731 : // value so we don't have to do memory allocations after that.
732 18944 : if (!path.empty()) return path;
733 :
734 21744 : std::string datadir = gArgs.GetArg("-datadir", "");
735 1400 : if (!datadir.empty()) {
736 3354 : path = fs::system_complete(datadir);
737 1400 : if (!fs::is_directory(path)) {
738 1400 : path = "";
739 : return path;
740 : }
741 : } else {
742 0 : path = GetDefaultDataDir();
743 : }
744 1400 : if (fNetSpecific)
745 508 : path /= BaseParams().DataDir();
746 :
747 1400 : if (fs::create_directories(path)) {
748 : // This is the first run, create wallets subdirectory too
749 687 : fs::create_directories(path / "wallets");
750 : }
751 :
752 : return path;
753 : }
754 :
755 783 : bool CheckDataDirOption()
756 : {
757 1566 : std::string datadir = gArgs.GetArg("-datadir", "");
758 4686 : return datadir.empty() || fs::is_directory(fs::system_complete(datadir));
759 : }
760 :
761 509 : void ClearDatadirCache()
762 : {
763 509 : LOCK(csPathCached);
764 :
765 509 : pathCached = fs::path();
766 509 : pathCachedNetSpecific = fs::path();
767 509 : g_blocks_path_cache_net_specific = fs::path();
768 509 : }
769 :
770 767 : fs::path GetConfigFile(const std::string& confPath)
771 : {
772 767 : fs::path pathConfigFile(confPath);
773 1534 : return AbsPathForConfigVal(pathConfigFile, false);
774 : }
775 :
776 368 : fs::path GetMasternodeConfigFile()
777 : {
778 736 : fs::path pathConfigFile(gArgs.GetArg("-mnconf", PIVX_MASTERNODE_CONF_FILENAME));
779 736 : return AbsPathForConfigVal(pathConfigFile);
780 : }
781 :
782 13620 : static std::string TrimString(const std::string& str, const std::string& pattern)
783 : {
784 13620 : std::string::size_type front = str.find_first_not_of(pattern);
785 13620 : if (front == std::string::npos) {
786 4 : return std::string();
787 : }
788 13616 : std::string::size_type end = str.find_last_not_of(pattern);
789 13616 : return str.substr(front, end - front + 1);
790 : }
791 :
792 417 : static std::vector<std::pair<std::string, std::string>> GetConfigOptions(std::istream& stream)
793 : {
794 417 : std::vector<std::pair<std::string, std::string>> options;
795 834 : std::string str, prefix;
796 5235 : std::string::size_type pos;
797 5235 : while (std::getline(stream, str)) {
798 4818 : if ((pos = str.find('#')) != std::string::npos) {
799 0 : str = str.substr(0, pos);
800 : }
801 4818 : const static std::string pattern = " \t\r\n";
802 4818 : str = TrimString(str, pattern);
803 4818 : if (!str.empty()) {
804 4816 : if (*str.begin() == '[' && *str.rbegin() == ']') {
805 415 : prefix = str.substr(1, str.size() - 2) + '.';
806 4401 : } else if ((pos = str.find('=')) != std::string::npos) {
807 13203 : std::string name = prefix + TrimString(str.substr(0, pos), pattern);
808 8802 : std::string value = TrimString(str.substr(pos + 1), pattern);
809 4401 : options.emplace_back(name, value);
810 : }
811 : }
812 : }
813 834 : return options;
814 : }
815 :
816 417 : void ArgsManager::ReadConfigStream(std::istream& stream)
817 : {
818 417 : LOCK(cs_args);
819 :
820 4818 : for (const std::pair<std::string, std::string>& option : GetConfigOptions(stream)) {
821 8802 : std::string strKey = std::string("-") + option.first;
822 8802 : std::string strValue = option.second;
823 :
824 4401 : if (InterpretNegatedOption(strKey, strValue)) {
825 4401 : m_config_args[strKey].clear();
826 : } else {
827 4391 : m_config_args[strKey].push_back(strValue);
828 : }
829 : }
830 417 : }
831 :
832 391 : void ArgsManager::ReadConfigFile(const std::string& confPath)
833 : {
834 391 : {
835 391 : LOCK(cs_args);
836 391 : m_config_args.clear();
837 : }
838 :
839 782 : fsbridge::ifstream stream(GetConfigFile(confPath));
840 :
841 : // ok to not have a config file
842 391 : if (stream.good()) {
843 391 : ReadConfigStream(stream);
844 : }
845 :
846 : // If datadir is changed in .conf file:
847 391 : ClearDatadirCache();
848 391 : if (!CheckDataDirOption()) {
849 3 : throw std::runtime_error(strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str()));
850 : }
851 390 : }
852 :
853 3033 : fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific)
854 : {
855 3033 : if (path.is_absolute()) {
856 8 : return path;
857 : }
858 3025 : return fs::absolute(path, GetDataDir(net_specific));
859 : }
860 :
861 442 : std::string ArgsManager::GetChainName() const
862 : {
863 442 : bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest");
864 442 : bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet");
865 :
866 442 : if (fTestNet && fRegTest)
867 10 : throw std::runtime_error("Invalid combination of -regtest and -testnet.");
868 432 : if (fRegTest)
869 392 : return CBaseChainParams::REGTEST;
870 40 : if (fTestNet)
871 16 : return CBaseChainParams::TESTNET;
872 456 : return CBaseChainParams::MAIN;
873 : }
874 :
875 1270 : bool RenameOver(fs::path src, fs::path dest)
876 : {
877 : #ifdef WIN32
878 : return MoveFileExW(src.wstring().c_str(), dest.wstring().c_str(),
879 : MOVEFILE_REPLACE_EXISTING) != 0;
880 : #else
881 1270 : int rc = std::rename(src.string().c_str(), dest.string().c_str());
882 1270 : return (rc == 0);
883 : #endif /* WIN32 */
884 : }
885 :
886 : /**
887 : * Ignores exceptions thrown by Boost's create_directories if the requested directory exists.
888 : * Specifically handles case where path p exists, but it wasn't possible for the user to
889 : * write to the parent directory.
890 : */
891 3267 : bool TryCreateDirectories(const fs::path& p)
892 : {
893 3267 : try {
894 3267 : return fs::create_directories(p);
895 2 : } catch (const fs::filesystem_error&) {
896 2 : if (!fs::exists(p) || !fs::is_directory(p))
897 1 : throw;
898 : }
899 :
900 : // create_directories didn't create the directory, it had to have existed already
901 0 : return false;
902 : }
903 :
904 2888 : bool FileCommit(FILE* file)
905 : {
906 2888 : if (fflush(file) != 0) { // harmless if redundantly called
907 0 : LogPrintf("%s: fflush failed: %d\n", __func__, errno);
908 0 : return false;
909 : }
910 : #ifdef WIN32
911 : HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
912 : if (FlushFileBuffers(hFile) == 0) {
913 : LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError());
914 : return false;
915 : }
916 : #else
917 : #if defined(__linux__) || defined(__NetBSD__)
918 2888 : if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync
919 0 : LogPrintf("%s: fdatasync failed: %d\n", __func__, errno);
920 0 : return false;
921 : }
922 : #elif defined(MAC_OSX) && defined(F_FULLFSYNC)
923 : if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success
924 : LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno);
925 : return false;
926 : }
927 : #else
928 : if (fsync(fileno(file)) != 0 && errno != EINVAL) {
929 : LogPrintf("%s: fsync failed: %d\n", __func__, errno);
930 : return false;
931 : }
932 : #endif
933 : #endif
934 : return true;
935 : }
936 :
937 3 : bool TruncateFile(FILE* file, unsigned int length)
938 : {
939 : #if defined(WIN32)
940 : return _chsize(_fileno(file), length) == 0;
941 : #else
942 3 : return ftruncate(fileno(file), length) == 0;
943 : #endif
944 : }
945 :
946 : /**
947 : * this function tries to raise the file descriptor limit to the requested number.
948 : * It returns the actual file descriptor limit (which may be more or less than nMinFD)
949 : */
950 386 : int RaiseFileDescriptorLimit(int nMinFD)
951 : {
952 : #if defined(WIN32)
953 : return 2048;
954 : #else
955 386 : struct rlimit limitFD;
956 386 : if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) {
957 386 : if (limitFD.rlim_cur < (rlim_t)nMinFD) {
958 0 : limitFD.rlim_cur = nMinFD;
959 0 : if (limitFD.rlim_cur > limitFD.rlim_max)
960 0 : limitFD.rlim_cur = limitFD.rlim_max;
961 0 : setrlimit(RLIMIT_NOFILE, &limitFD);
962 0 : getrlimit(RLIMIT_NOFILE, &limitFD);
963 : }
964 386 : return limitFD.rlim_cur;
965 : }
966 : return nMinFD; // getrlimit failed, assume it's fine
967 : #endif
968 : }
969 :
970 : /**
971 : * this function tries to make a particular range of a file allocated (corresponding to disk space)
972 : * it is advisory, and the range specified in the arguments will never contain live data
973 : */
974 502 : void AllocateFileRange(FILE* file, unsigned int offset, unsigned int length)
975 : {
976 : #if defined(WIN32)
977 : // Windows-specific version
978 : HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file));
979 : LARGE_INTEGER nFileSize;
980 : int64_t nEndPos = (int64_t)offset + length;
981 : nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF;
982 : nFileSize.u.HighPart = nEndPos >> 32;
983 : SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN);
984 : SetEndOfFile(hFile);
985 : #elif defined(MAC_OSX)
986 : // OSX specific version
987 : fstore_t fst;
988 : fst.fst_flags = F_ALLOCATECONTIG;
989 : fst.fst_posmode = F_PEOFPOSMODE;
990 : fst.fst_offset = 0;
991 : fst.fst_length = (off_t)offset + length;
992 : fst.fst_bytesalloc = 0;
993 : if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) {
994 : fst.fst_flags = F_ALLOCATEALL;
995 : fcntl(fileno(file), F_PREALLOCATE, &fst);
996 : }
997 : ftruncate(fileno(file), fst.fst_length);
998 : #elif defined(__linux__)
999 : // Version using posix_fallocate
1000 502 : off_t nEndPos = (off_t)offset + length;
1001 502 : posix_fallocate(fileno(file), 0, nEndPos);
1002 : #else
1003 : // Fallback version
1004 : // TODO: just write one byte per block
1005 : static const char buf[65536] = {};
1006 : fseek(file, offset, SEEK_SET);
1007 : while (length > 0) {
1008 : unsigned int now = 65536;
1009 : if (length < now)
1010 : now = length;
1011 : fwrite(buf, 1, now, file); // allowed to fail; this function is advisory anyway
1012 : length -= now;
1013 : }
1014 : #endif
1015 502 : }
1016 :
1017 : #ifdef WIN32
1018 : fs::path GetSpecialFolderPath(int nFolder, bool fCreate)
1019 : {
1020 : WCHAR pszPath[MAX_PATH] = L"";
1021 :
1022 : if (SHGetSpecialFolderPathW(nullptr, pszPath, nFolder, fCreate)) {
1023 : return fs::path(pszPath);
1024 : }
1025 :
1026 : LogPrintf("SHGetSpecialFolderPathW() failed, could not obtain requested path.\n");
1027 : return fs::path("");
1028 : }
1029 : #endif
1030 :
1031 137 : void runCommand(std::string strCommand)
1032 : {
1033 137 : if (strCommand.empty()) return;
1034 : #ifndef WIN32
1035 137 : int nErr = ::system(strCommand.c_str());
1036 : #else
1037 : int nErr = ::_wsystem(std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().from_bytes(strCommand).c_str());
1038 : #endif
1039 137 : if (nErr)
1040 0 : LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr);
1041 : }
1042 :
1043 831 : void SetupEnvironment()
1044 : {
1045 : // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale
1046 : // may be invalid, in which case the "C" locale is used as fallback.
1047 : #if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
1048 831 : try {
1049 831 : std::locale(""); // Raises a runtime error if current locale is invalid
1050 0 : } catch (const std::runtime_error&) {
1051 0 : setenv("LC_ALL", "C", 1);
1052 : }
1053 : #elif defined(WIN32)
1054 : // Set the default input/output charset is utf-8
1055 : SetConsoleCP(CP_UTF8);
1056 : SetConsoleOutputCP(CP_UTF8);
1057 : #endif
1058 : // The path locale is lazy initialized and to avoid deinitialization errors
1059 : // in multithreading environments, it is set explicitly by the main thread.
1060 : // A dummy locale is used to extract the internal default locale, used by
1061 : // fs::path, which is then used to explicitly imbue the path.
1062 831 : std::locale loc = fs::path::imbue(std::locale::classic());
1063 : #ifndef WIN32
1064 831 : fs::path::imbue(loc);
1065 : #else
1066 : fs::path::imbue(std::locale(loc, new std::codecvt_utf8_utf16<wchar_t>()));
1067 : #endif
1068 831 : }
1069 :
1070 390 : bool SetupNetworking()
1071 : {
1072 : #ifdef WIN32
1073 : // Initialize Windows Sockets
1074 : WSADATA wsadata;
1075 : int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
1076 : if (ret != NO_ERROR || LOBYTE(wsadata.wVersion ) != 2 || HIBYTE(wsadata.wVersion) != 2)
1077 : return false;
1078 : #endif
1079 390 : return true;
1080 : }
1081 :
1082 0 : void SetThreadPriority(int nPriority)
1083 : {
1084 : #ifdef WIN32
1085 : SetThreadPriority(GetCurrentThread(), nPriority);
1086 : #else // WIN32
1087 : #ifdef PRIO_THREAD
1088 : setpriority(PRIO_THREAD, 0, nPriority);
1089 : #else // PRIO_THREAD
1090 0 : setpriority(PRIO_PROCESS, 0, nPriority);
1091 : #endif // PRIO_THREAD
1092 : #endif // WIN32
1093 0 : }
1094 :
1095 735 : int GetNumCores()
1096 : {
1097 735 : return std::thread::hardware_concurrency();
1098 : }
1099 :
1100 :
1101 355 : int ScheduleBatchPriority(void)
1102 : {
1103 : #ifdef SCHED_BATCH
1104 355 : const static sched_param param{0};
1105 355 : if (int ret = pthread_setschedparam(pthread_self(), SCHED_BATCH, ¶m)) {
1106 0 : LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(errno));
1107 0 : return ret;
1108 : }
1109 : return 0;
1110 : #else
1111 : return 1;
1112 : #endif
1113 : }
1114 :
1115 : namespace util {
1116 : #ifdef WIN32
1117 : WinCmdLineArgs::WinCmdLineArgs()
1118 : {
1119 : wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(), &argc);
1120 : std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> utf8_cvt;
1121 : argv = new char*[argc];
1122 : args.resize(argc);
1123 : for (int i = 0; i < argc; i++) {
1124 : args[i] = utf8_cvt.to_bytes(wargv[i]);
1125 : argv[i] = &*args[i].begin();
1126 : }
1127 : LocalFree(wargv);
1128 : }
1129 :
1130 : WinCmdLineArgs::~WinCmdLineArgs()
1131 : {
1132 : delete[] argv;
1133 : }
1134 :
1135 : std::pair<int, char**> WinCmdLineArgs::get()
1136 : {
1137 : return std::make_pair(argc, argv);
1138 : }
1139 : #endif
1140 : } // namespace util
1141 :
|