diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c7cc645c77..d4e8353219 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -3,6 +3,8 @@ // Distributed under the MIT software license, see the accompanying // file LICENSE or http://www.opensource.org/licenses/mit-license.php. +#include + #include #include #include @@ -1067,3 +1069,38 @@ void SelectParams(const std::string& network) void ClearCheckpoints(CChainParams ¶ms) { params.checkpointData = {}; } + +Res UpdateCheckpointsFromFile(CChainParams ¶ms, const std::string &fileName) { + std::ifstream file(fileName); + if (!file.good()) { + return Res::Err("Could not read %s. Ensure it exists and has read permissions", fileName); + } + + ClearCheckpoints(params); + + std::string line; + while (std::getline(file, line)) { + auto trimmed = trim_ws(line); + if (trimmed.rfind('#', 0) == 0 || trimmed.find_first_not_of(" \n\r\t") == std::string::npos) + continue; + + std::istringstream iss(trimmed); + std::string hashStr, heightStr; + if (!(iss >> heightStr >> hashStr)) { + return Res::Err("Error parsing line %s", trimmed); + } + + uint256 hash; + if (!ParseHashStr(hashStr, hash)) { + return Res::Err("Invalid hash: %s", hashStr); + } + + int32_t height; + if (!ParseInt32(heightStr, &height)) { + return Res::Err("Invalid height: %s", heightStr); + } + + params.checkpointData.mapCheckpoints[height] = hash; + } + return Res::Ok(); +} diff --git a/src/chainparams.h b/src/chainparams.h index 77f0cecdee..3a9c419412 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -123,6 +123,7 @@ class CChainParams std::set genesisTeam; friend void ClearCheckpoints(CChainParams ¶ms); + friend Res UpdateCheckpointsFromFile(CChainParams ¶ms, const std::string &fileName); }; const auto SMART_CONTRACT_DFIP_2201 = "DFIP2201"; diff --git a/src/init.cpp b/src/init.cpp index 0c922d905a..45f268400f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -394,7 +394,8 @@ void SetupServerArgs() std::vector hidden_args = { "-dbcrashratio", "-forcecompactdb", "-interrupt-block=", "-stop-block=", - "-mocknet", "-mocknet-blocktime=", "-mocknet-key=" + "-mocknet", "-mocknet-blocktime=", "-mocknet-key=", + "-checkpoints-file", // GUI args. These will be overwritten by SetupUIArgs for the GUI "-choosedatadir", "-lang=", "-min", "-resetguisettings", "-splash"}; @@ -1119,6 +1120,14 @@ bool AppInitParameterInteraction() mempool.setSanityCheck(1.0 / ratio); } fCheckBlockIndex = gArgs.GetBoolArg("-checkblockindex", chainparams.DefaultConsistencyChecks()); + + auto checkpoints_file = gArgs.GetArg("-checkpoints-file", ""); + if (!checkpoints_file.empty()) { + auto res = UpdateCheckpointsFromFile(const_cast(chainparams), checkpoints_file); + if (!res) + return InitError(strprintf(_("Error in checkpoints file : %s").translated, res.msg)); + } + if (!gArgs.GetBoolArg("-checkpoints", DEFAULT_CHECKPOINTS_ENABLED)) { LogPrintf("conf: checkpoints disabled.\n"); // Safe to const_cast, as we know it's always allocated, and is always in the global var diff --git a/src/masternodes/tokens.cpp b/src/masternodes/tokens.cpp index c2616277f0..4f6ba8ec11 100644 --- a/src/masternodes/tokens.cpp +++ b/src/masternodes/tokens.cpp @@ -8,6 +8,7 @@ #include // Params() #include #include +#include #include @@ -15,18 +16,6 @@ const DCT_ID CTokensView::DCT_ID_START = DCT_ID{128}; extern const std::string CURRENCY_UNIT; -std::string trim_ws(std::string const & str) -{ - std::string const ws = " \n\r\t"; - size_t first = str.find_first_not_of(ws); - if (std::string::npos == first) - { - return str; - } - size_t last = str.find_last_not_of(ws); - return str.substr(first, (last - first + 1)); -} - std::optional CTokensView::GetToken(DCT_ID id) const { return ReadBy(id); diff --git a/src/masternodes/tokens.h b/src/masternodes/tokens.h index b677fcb494..fb5c2afa3e 100644 --- a/src/masternodes/tokens.h +++ b/src/masternodes/tokens.h @@ -16,8 +16,6 @@ class CTransaction; class UniValue; -std::string trim_ws(std::string const & str); - class CToken { public: diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index 4cdd990694..03ad0fd56b 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -566,3 +566,15 @@ std::string Capitalize(std::string str) str[0] = ToUpper(str.front()); return str; } + +std::string trim_ws(std::string const & str) +{ + std::string const ws = " \n\r\t"; + size_t first = str.find_first_not_of(ws); + if (std::string::npos == first) + { + return str; + } + size_t last = str.find_last_not_of(ws); + return str.substr(first, (last - first + 1)); +} diff --git a/src/util/strencodings.h b/src/util/strencodings.h index 2678df2d87..39e179638f 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -39,6 +39,7 @@ std::string SanitizeString(const std::string& str, int rule = SAFE_CHARS_DEFAULT std::vector ParseHex(const char* psz); std::vector ParseHex(const std::string& str); signed char HexDigit(char c); +std::string trim_ws(std::string const & str); /* Returns true if each character in str is a hex character, and has an even * number of hex digits.*/ bool IsHex(const std::string& str); diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py index 3f1b15729c..76a2487748 100755 --- a/test/lint/check-doc.py +++ b/test/lint/check-doc.py @@ -23,7 +23,7 @@ CMD_GREP_WALLET_HIDDEN_ARGS = r"git grep --function-context 'void DummyWalletInit::AddWalletOptions' -- {}".format(CMD_ROOT_DIR) CMD_GREP_DOCS = r"git grep --perl-regexp '{}' {}".format(REGEX_DOC, CMD_ROOT_DIR) # list unsupported, deprecated and duplicate args as they need no documentation -SET_DOC_OPTIONAL = set(['-h', '-help', '-dbcrashratio', '-forcecompactdb', '-interrupt-block', '-stop-block', '-mocknet', '-mocknet-key', '-mocknet-blocktime']) +SET_DOC_OPTIONAL = set(['-h', '-help', '-dbcrashratio', '-forcecompactdb', '-interrupt-block', '-stop-block', '-mocknet', '-mocknet-key', '-mocknet-blocktime', '-checkpoints-file']) def lint_missing_argument_documentation():