diff --git a/doc/developer-notes.md b/doc/developer-notes.md index f9ad26651a8..4759146a8fc 100644 --- a/doc/developer-notes.md +++ b/doc/developer-notes.md @@ -1009,10 +1009,6 @@ Strings and formatting buffer overflows, and surprises with `\0` characters. Also, some C string manipulations tend to act differently depending on platform, or even the user locale. -- Use `ToIntegral` from [`strencodings.h`](/src/util/strencodings.h) for number parsing. In legacy code you might also find `ParseInt*` family of functions, `ParseDouble` or `LocaleIndependentAtoi`. - - - *Rationale*: These functions do overflow checking and avoid pesky locale issues. - - For `strprintf`, `LogInfo`, `LogDebug`, etc formatting characters don't need size specifiers. - *Rationale*: Bitcoin Core uses tinyformat, which is type safe. Leave them out to avoid confusion. diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 03104b81361..14a0cb245fa 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -1,5 +1,5 @@ // Copyright (c) 2009-2010 Satoshi Nakamoto -// Copyright (c) 2009-2022 The Bitcoin Core developers +// Copyright (c) 2009-present The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -472,7 +472,8 @@ class NetinfoRequestHandler : public BaseRequestHandler { if (!args.empty()) { uint8_t n{0}; - if (ParseUInt8(args.at(0), &n)) { + if (const auto res{ToIntegral(args.at(0))}) { + n = *res; m_details_level = std::min(n, NETINFO_MAX_LEVEL); } else { throw std::runtime_error(strprintf("invalid -netinfo level argument: %s\nFor more information, run: bitcoin-cli -netinfo help", args.at(0))); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index e2b3a3b5545..937db68e5af 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -211,35 +211,33 @@ static CAmount ExtractAndValidateValue(const std::string& strValue) static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal) { - uint32_t newVersion; - if (!ParseUInt32(cmdVal, &newVersion) || newVersion < 1 || newVersion > TX_MAX_STANDARD_VERSION) { + const auto ver{ToIntegral(cmdVal)}; + if (!ver || *ver < 1 || *ver > TX_MAX_STANDARD_VERSION) { throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'"); } - - tx.version = newVersion; + tx.version = *ver; } static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal) { - int64_t newLocktime; - if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL) + const auto locktime{ToIntegral(cmdVal)}; + if (!locktime) { throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'"); - - tx.nLockTime = (unsigned int) newLocktime; + } + tx.nLockTime = *locktime; } static void MutateTxRBFOptIn(CMutableTransaction& tx, const std::string& strInIdx) { - // parse requested index - int64_t inIdx = -1; - if (strInIdx != "" && (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast(tx.vin.size()))) { + const auto idx{ToIntegral(strInIdx)}; + if (strInIdx != "" && (!idx || *idx >= tx.vin.size())) { throw std::runtime_error("Invalid TX input index '" + strInIdx + "'"); } // set the nSequence to MAX_INT - 2 (= RBF opt in flag) - int cnt = 0; + uint32_t cnt{0}; for (CTxIn& txin : tx.vin) { - if (strInIdx == "" || cnt == inIdx) { + if (strInIdx == "" || cnt == *idx) { if (txin.nSequence > MAX_BIP125_RBF_SEQUENCE) { txin.nSequence = MAX_BIP125_RBF_SEQUENCE; } @@ -277,9 +275,10 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu // extract and validate vout const std::string& strVout = vStrInputParts[1]; - int64_t vout; - if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast(maxVout)) + const auto vout{ToIntegral(strVout)}; + if (!vout || *vout > maxVout) { throw std::runtime_error("invalid TX input vout '" + strVout + "'"); + } // extract the optional sequence number uint32_t nSequenceIn = CTxIn::SEQUENCE_FINAL; @@ -288,7 +287,7 @@ static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInpu } // append to transaction input list - CTxIn txin(*txid, vout, CScript(), nSequenceIn); + CTxIn txin{*txid, *vout, CScript{}, nSequenceIn}; tx.vin.push_back(txin); } @@ -508,26 +507,20 @@ static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& str static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx) { - // parse requested deletion index - int64_t inIdx; - if (!ParseInt64(strInIdx, &inIdx) || inIdx < 0 || inIdx >= static_cast(tx.vin.size())) { + const auto idx{ToIntegral(strInIdx)}; + if (!idx || idx >= tx.vin.size()) { throw std::runtime_error("Invalid TX input index '" + strInIdx + "'"); } - - // delete input from transaction - tx.vin.erase(tx.vin.begin() + inIdx); + tx.vin.erase(tx.vin.begin() + *idx); } static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx) { - // parse requested deletion index - int64_t outIdx; - if (!ParseInt64(strOutIdx, &outIdx) || outIdx < 0 || outIdx >= static_cast(tx.vout.size())) { + const auto idx{ToIntegral(strOutIdx)}; + if (!idx || idx >= tx.vout.size()) { throw std::runtime_error("Invalid TX output index '" + strOutIdx + "'"); } - - // delete output from transaction - tx.vout.erase(tx.vout.begin() + outIdx); + tx.vout.erase(tx.vout.begin() + *idx); } static const unsigned int N_SIGHASH_OPTS = 7; diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 7cf28d69b42..6be2a63f4e5 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -53,14 +53,14 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti } const auto value{arg.substr(found + 1)}; - int32_t height; - if (!ParseInt32(value, &height) || height < 0 || height >= std::numeric_limits::max()) { + const auto height{ToIntegral(value)}; + if (!height || *height < 0 || *height >= std::numeric_limits::max()) { throw std::runtime_error(strprintf("Invalid height value (%s) for -testactivationheight=name@height.", arg)); } const auto deployment_name{arg.substr(0, found)}; if (const auto buried_deployment = GetBuriedDeployment(deployment_name)) { - options.activation_heights[*buried_deployment] = height; + options.activation_heights[*buried_deployment] = *height; } else { throw std::runtime_error(strprintf("Invalid name (%s) for -testactivationheight=name@height.", arg)); } @@ -72,16 +72,22 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height]"); } CChainParams::VersionBitsParameters vbparams{}; - if (!ParseInt64(vDeploymentParams[1], &vbparams.start_time)) { + const auto start_time{ToIntegral(vDeploymentParams[1])}; + if (!start_time) { throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1])); } - if (!ParseInt64(vDeploymentParams[2], &vbparams.timeout)) { + vbparams.start_time = *start_time; + const auto timeout{ToIntegral(vDeploymentParams[2])}; + if (!timeout) { throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2])); } + vbparams.timeout = *timeout; if (vDeploymentParams.size() >= 4) { - if (!ParseInt32(vDeploymentParams[3], &vbparams.min_activation_height)) { + const auto min_activation_height{ToIntegral(vDeploymentParams[3])}; + if (!min_activation_height) { throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3])); } + vbparams.min_activation_height = *min_activation_height; } else { vbparams.min_activation_height = 0; } diff --git a/src/init.cpp b/src/init.cpp index 78e4a23dec4..a3588ad4125 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1151,8 +1151,8 @@ bool CheckHostPortOptions(const ArgsManager& args) { "-rpcport", }) { if (const auto port{args.GetArg(port_option)}) { - uint16_t n; - if (!ParseUInt16(*port, &n) || n == 0) { + const auto n{ToIntegral(*port)}; + if (!n || *n == 0) { return InitError(InvalidPortErrMsg(port_option, *port)); } } diff --git a/src/interfaces/node.h b/src/interfaces/node.h index a17aa4afefc..745e602cdd9 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -205,6 +206,9 @@ class Node //! List rpc commands. virtual std::vector listRpcCommands() = 0; + //! Load and activate a snapshot file + virtual bool loadSnapshot(AutoFile& coins_file, const node::SnapshotMetadata& metadata, bool in_memory) = 0; + //! Set RPC timer interface if unset. virtual void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) = 0; @@ -240,6 +244,10 @@ class Node using ShowProgressFn = std::function; virtual std::unique_ptr handleShowProgress(ShowProgressFn fn) = 0; + //! Register handler for snapshot load progress. + using SnapshotLoadProgressFn = std::function; + virtual std::unique_ptr handleSnapshotLoadProgress(SnapshotLoadProgressFn fn) = 0; + //! Register handler for wallet loader constructed messages. using InitWalletFn = std::function; virtual std::unique_ptr handleInitWallet(InitWalletFn fn) = 0; diff --git a/src/ipc/process.cpp b/src/ipc/process.cpp index bdc541b6544..33667883b76 100644 --- a/src/ipc/process.cpp +++ b/src/ipc/process.cpp @@ -55,9 +55,11 @@ class ProcessImpl : public Process // in combination with other arguments because the parent process // should be able to control the child process through the IPC protocol // without passing information out of band. - if (!ParseInt32(argv[2], &fd)) { + const auto maybe_fd{ToIntegral(argv[2])}; + if (!maybe_fd) { throw std::runtime_error(strprintf("Invalid -ipcfd number '%s'", argv[2])); } + fd = *maybe_fd; return true; } int connect(const fs::path& data_dir, diff --git a/src/kernel/notifications_interface.h b/src/kernel/notifications_interface.h index ef72d9bdb6e..6fcd1243806 100644 --- a/src/kernel/notifications_interface.h +++ b/src/kernel/notifications_interface.h @@ -40,6 +40,7 @@ class Notifications [[nodiscard]] virtual InterruptResult blockTip(SynchronizationState state, CBlockIndex& index) { return {}; } virtual void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {} virtual void progress(const bilingual_str& title, int progress_percent, bool resume_possible) {} + virtual void snapshotLoadProgress(double progress) {} virtual void warningSet(Warning id, const bilingual_str& message) {} virtual void warningUnset(Warning id) {} diff --git a/src/netbase.cpp b/src/netbase.cpp index a5da1c9dcef..1f3faa18643 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -829,10 +829,9 @@ CSubNet LookupSubNet(const std::string& subnet_str) addr = static_cast(MaybeFlipIPv6toCJDNS(CService{addr.value(), /*port=*/0})); if (slash_pos != subnet_str.npos) { const std::string netmask_str{subnet_str.substr(slash_pos + 1)}; - uint8_t netmask; - if (ParseUInt8(netmask_str, &netmask)) { + if (const auto netmask{ToIntegral(netmask_str)}) { // Valid number; assume CIDR variable-length subnet masking. - subnet = CSubNet{addr.value(), netmask}; + subnet = CSubNet{addr.value(), *netmask}; } else { // Invalid number; try full netmask syntax. Never allow lookup for netmask. const std::optional full_netmask{LookupHost(netmask_str, /*fAllowLookup=*/false)}; diff --git a/src/node/interface_ui.cpp b/src/node/interface_ui.cpp index c9dcd7a7fb9..6a0b1df2058 100644 --- a/src/node/interface_ui.cpp +++ b/src/node/interface_ui.cpp @@ -23,6 +23,7 @@ struct UISignals { boost::signals2::signal NotifyNetworkActiveChanged; boost::signals2::signal NotifyAlertChanged; boost::signals2::signal ShowProgress; + boost::signals2::signal SnapshotLoadProgress; boost::signals2::signal NotifyBlockTip; boost::signals2::signal NotifyHeaderTip; boost::signals2::signal BannedListChanged; @@ -43,6 +44,7 @@ ADD_SIGNALS_IMPL_WRAPPER(NotifyNumConnectionsChanged); ADD_SIGNALS_IMPL_WRAPPER(NotifyNetworkActiveChanged); ADD_SIGNALS_IMPL_WRAPPER(NotifyAlertChanged); ADD_SIGNALS_IMPL_WRAPPER(ShowProgress); +ADD_SIGNALS_IMPL_WRAPPER(SnapshotLoadProgress); ADD_SIGNALS_IMPL_WRAPPER(NotifyBlockTip); ADD_SIGNALS_IMPL_WRAPPER(NotifyHeaderTip); ADD_SIGNALS_IMPL_WRAPPER(BannedListChanged); @@ -55,6 +57,7 @@ void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { re void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); } void CClientUIInterface::NotifyAlertChanged() { return g_ui_signals.NotifyAlertChanged(); } void CClientUIInterface::ShowProgress(const std::string& title, int nProgress, bool resume_possible) { return g_ui_signals.ShowProgress(title, nProgress, resume_possible); } +void CClientUIInterface::SnapshotLoadProgress(double progress) { return g_ui_signals.SnapshotLoadProgress(progress); } void CClientUIInterface::NotifyBlockTip(SynchronizationState s, const CBlockIndex* i) { return g_ui_signals.NotifyBlockTip(s, i); } void CClientUIInterface::NotifyHeaderTip(SynchronizationState s, int64_t height, int64_t timestamp, bool presync) { return g_ui_signals.NotifyHeaderTip(s, height, timestamp, presync); } void CClientUIInterface::BannedListChanged() { return g_ui_signals.BannedListChanged(); } diff --git a/src/node/interface_ui.h b/src/node/interface_ui.h index 85c34f58342..5d12612692b 100644 --- a/src/node/interface_ui.h +++ b/src/node/interface_ui.h @@ -102,6 +102,9 @@ class CClientUIInterface */ ADD_SIGNALS_DECL_WRAPPER(ShowProgress, void, const std::string& title, int nProgress, bool resume_possible); + /** Snapshot load progress. */ + ADD_SIGNALS_DECL_WRAPPER(SnapshotLoadProgress, void, double progress); + /** New block has been accepted */ ADD_SIGNALS_DECL_WRAPPER(NotifyBlockTip, void, SynchronizationState, const CBlockIndex*); diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index b5c74a97f6d..21670e7ba92 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -355,6 +355,11 @@ class NodeImpl : public Node return ::tableRPC.execute(req); } std::vector listRpcCommands() override { return ::tableRPC.listCommands(); } + bool loadSnapshot(AutoFile& coins_file, const SnapshotMetadata& metadata, bool in_memory) override + { + auto activation_result{chainman().ActivateSnapshot(coins_file, metadata, in_memory)}; + return activation_result.has_value(); + } void rpcSetTimerInterfaceIfUnset(RPCTimerInterface* iface) override { RPCSetTimerInterfaceIfUnset(iface); } void rpcUnsetTimerInterface(RPCTimerInterface* iface) override { RPCUnsetTimerInterface(iface); } std::optional getUnspentOutput(const COutPoint& output) override @@ -386,6 +391,10 @@ class NodeImpl : public Node { return MakeSignalHandler(::uiInterface.ShowProgress_connect(fn)); } + std::unique_ptr handleSnapshotLoadProgress(SnapshotLoadProgressFn fn) override + { + return MakeSignalHandler(::uiInterface.SnapshotLoadProgress_connect(fn)); + } std::unique_ptr handleInitWallet(InitWalletFn fn) override { return MakeSignalHandler(::uiInterface.InitWallet_connect(fn)); diff --git a/src/node/kernel_notifications.cpp b/src/node/kernel_notifications.cpp index 550ffe74c4a..ea3ca3e6135 100644 --- a/src/node/kernel_notifications.cpp +++ b/src/node/kernel_notifications.cpp @@ -77,6 +77,11 @@ void KernelNotifications::progress(const bilingual_str& title, int progress_perc uiInterface.ShowProgress(title.translated, progress_percent, resume_possible); } +void KernelNotifications::snapshotLoadProgress(double progress) +{ + uiInterface.SnapshotLoadProgress(progress); +} + void KernelNotifications::warningSet(kernel::Warning id, const bilingual_str& message) { if (m_warnings.Set(id, message)) { diff --git a/src/node/kernel_notifications.h b/src/node/kernel_notifications.h index f4174381daf..4708c0bfecd 100644 --- a/src/node/kernel_notifications.h +++ b/src/node/kernel_notifications.h @@ -41,6 +41,8 @@ class KernelNotifications : public kernel::Notifications void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override; + void snapshotLoadProgress(double progress) override; + void warningSet(kernel::Warning id, const bilingual_str& message) override; void warningUnset(kernel::Warning id) override; diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 6c853cbf2f8..d373b4a4f87 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -116,6 +116,8 @@ add_library(bitcoinqt STATIC EXCLUDE_FROM_ALL qvaluecombobox.h rpcconsole.cpp rpcconsole.h + snapshotmodel.cpp + snapshotmodel.h splashscreen.cpp splashscreen.h trafficgraphwidget.cpp diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index 0a0e44f6f5b..853e4339cd0 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -25,12 +26,16 @@ #include #include #include +#include #include #include #include #include #include #include +#include + +#include int setFontChoice(QComboBox* cb, const OptionsModel::FontChoice& fc) { @@ -121,6 +126,10 @@ OptionsDialog::OptionsDialog(QWidget* parent, bool enableWallet) connect(ui->connectSocksTor, &QPushButton::toggled, ui->proxyPortTor, &QWidget::setEnabled); connect(ui->connectSocksTor, &QPushButton::toggled, this, &OptionsDialog::updateProxyValidationState); + QPushButton* loadSnapshotButton = new QPushButton(tr("Load Snapshot..."), this); + ui->verticalLayout_Main->insertWidget(ui->verticalLayout_Main->indexOf(ui->enableServer) + 1, loadSnapshotButton); + connect(loadSnapshotButton, &QPushButton::clicked, this, &OptionsDialog::on_loadSnapshotButton_clicked); + /* Window elements init */ #ifdef Q_OS_MACOS /* remove Window tab on Mac */ @@ -400,6 +409,51 @@ void OptionsDialog::on_showTrayIcon_stateChanged(int state) } } +void OptionsDialog::on_loadSnapshotButton_clicked() +{ + QString filename = QFileDialog::getOpenFileName(this, + tr("Load Snapshot"), + tr("Bitcoin Snapshot Files (*.dat);;")); + + if (filename.isEmpty()) return; + + QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm snapshot load"), + tr("Are you sure you want to load this snapshot? This will delete your current blockchain data."), + QMessageBox::Yes | QMessageBox::Cancel, + QMessageBox::Cancel); + + if (retval != QMessageBox::Yes) return; + + QProgressDialog* progress = new QProgressDialog(tr("Loading snapshot..."), tr("Cancel"), 0, 100, this); + progress->setWindowModality(Qt::WindowModal); + progress->setMinimumDuration(0); + progress->setValue(0); + progress->show(); + + // Store the handler in the member variable to keep it alive + m_snapshot_load_handler = model->node().handleSnapshotLoadProgress( + [progress](double p) { + progress->setValue(static_cast(p * 100)); + QApplication::processEvents(); + }); + + SnapshotModel snapshotModel(model->node(), filename); + bool success = snapshotModel.processPath(); + + // Clean up the progress dialog + progress->close(); + progress->deleteLater(); + + // Clean up the handler + m_snapshot_load_handler.reset(); + + if (success) { + QMessageBox::information(this, tr("Success"), tr("Snapshot loaded successfully")); + } else { + QMessageBox::critical(this, tr("Error"), tr("Error loading snapshot")); + } +} + void OptionsDialog::togglePruneWarning(bool enabled) { ui->pruneWarning->setVisible(!ui->pruneWarning->isVisible()); diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index 031e4d31638..6eb0a29196c 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -20,6 +20,10 @@ namespace Ui { class OptionsDialog; } +namespace interfaces { +class Handler; +} + /** Proxy address widget validator, checks for a valid proxy address. */ class ProxyAddressValidator : public QValidator @@ -58,6 +62,8 @@ private Q_SLOTS: void on_openBitcoinConfButton_clicked(); void on_okButton_clicked(); void on_cancelButton_clicked(); + void on_loadSnapshotButton_clicked(); + void on_showTrayIcon_stateChanged(int state); @@ -77,6 +83,7 @@ private Q_SLOTS: ClientModel* m_client_model{nullptr}; OptionsModel* model{nullptr}; QDataWidgetMapper* mapper{nullptr}; + std::unique_ptr m_snapshot_load_handler; }; #endif // BITCOIN_QT_OPTIONSDIALOG_H diff --git a/src/qt/snapshotmodel.cpp b/src/qt/snapshotmodel.cpp new file mode 100644 index 00000000000..f2ea450753d --- /dev/null +++ b/src/qt/snapshotmodel.cpp @@ -0,0 +1,45 @@ +// Copyright (c) 2024 - present The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include +#include +#include + +SnapshotModel::SnapshotModel(interfaces::Node& node, QString path) + : m_node(node), m_path(path) {} + +bool SnapshotModel::processPath() +{ + ChainstateManager& chainman = *m_node.context()->chainman; + const fs::path snapshot_path = fs::u8path(m_path.toStdString()); + if (!fs::exists(snapshot_path)) { + return false; + } + + FILE* snapshot_file{fsbridge::fopen(snapshot_path, "rb")}; + AutoFile coins_file{snapshot_file}; + if (coins_file.IsNull()) { + return false; + } + + node::SnapshotMetadata metadata{chainman.GetParams().MessageStart()}; + try { + coins_file >> metadata; + } catch (const std::exception& e) { + return false; + } + + bool result = m_node.loadSnapshot(coins_file, metadata, false); + if (!result) { + return false; + } + + return true; +} diff --git a/src/qt/snapshotmodel.h b/src/qt/snapshotmodel.h new file mode 100644 index 00000000000..c7d1adf34ce --- /dev/null +++ b/src/qt/snapshotmodel.h @@ -0,0 +1,28 @@ +// Copyright (c) 2024 - present The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_SNAPSHOTMODEL_H +#define BITCOIN_QT_SNAPSHOTMODEL_H + +#include +#include +#include +#include + +/** Model for snapshot operations. */ +class SnapshotModel : public QObject +{ + Q_OBJECT + +public: + SnapshotModel(interfaces::Node& node, QString path); + + bool processPath(); + +private: + interfaces::Node& m_node; + QString m_path; +}; + +#endif // BITCOIN_QT_SNAPSHOTMODEL_H diff --git a/src/rest.cpp b/src/rest.cpp index 44984b360ff..464880224c8 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -962,9 +962,9 @@ static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req, std::string height_str; const RESTResponseFormat rf = ParseDataFormat(height_str, str_uri_part); - int32_t blockheight = -1; // Initialization done only to prevent valgrind false positive, see https://github.com/bitcoin/bitcoin/pull/18785 - if (!ParseInt32(height_str, &blockheight) || blockheight < 0) { - return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str)); + const auto blockheight{ToIntegral(height_str)}; + if (!blockheight || *blockheight < 0) { + return RESTERR(req, HTTP_BAD_REQUEST, "Invalid height: " + SanitizeString(height_str, SAFE_CHARS_URI)); } CBlockIndex* pblockindex = nullptr; @@ -974,10 +974,10 @@ static bool rest_blockhash_by_height(const std::any& context, HTTPRequest* req, ChainstateManager& chainman = *maybe_chainman; LOCK(cs_main); const CChain& active_chain = chainman.ActiveChain(); - if (blockheight > active_chain.Height()) { + if (*blockheight > active_chain.Height()) { return RESTERR(req, HTTP_NOT_FOUND, "Block height out of range"); } - pblockindex = active_chain[blockheight]; + pblockindex = active_chain[*blockheight]; } switch (rf) { case RESTResponseFormat::BINARY: { diff --git a/src/test/fuzz/locale.cpp b/src/test/fuzz/locale.cpp index 68db8422477..c0289172696 100644 --- a/src/test/fuzz/locale.cpp +++ b/src/test/fuzz/locale.cpp @@ -46,16 +46,8 @@ FUZZ_TARGET(locale) assert(c_locale != nullptr); const std::string random_string = fuzzed_data_provider.ConsumeRandomLengthString(5); - int32_t parseint32_out_without_locale; - const bool parseint32_without_locale = ParseInt32(random_string, &parseint32_out_without_locale); - int64_t parseint64_out_without_locale; - const bool parseint64_without_locale = ParseInt64(random_string, &parseint64_out_without_locale); const int64_t random_int64 = fuzzed_data_provider.ConsumeIntegral(); const std::string tostring_without_locale = util::ToString(random_int64); - // The variable `random_int32` is no longer used, but the harness still needs to - // consume the same data that it did previously to not invalidate existing seeds. - const int32_t random_int32 = fuzzed_data_provider.ConsumeIntegral(); - (void)random_int32; const std::string strprintf_int_without_locale = strprintf("%d", random_int64); const double random_double = fuzzed_data_provider.ConsumeFloatingPoint(); const std::string strprintf_double_without_locale = strprintf("%f", random_double); @@ -63,18 +55,6 @@ FUZZ_TARGET(locale) const char* new_locale = std::setlocale(LC_ALL, locale_identifier.c_str()); assert(new_locale != nullptr); - int32_t parseint32_out_with_locale; - const bool parseint32_with_locale = ParseInt32(random_string, &parseint32_out_with_locale); - assert(parseint32_without_locale == parseint32_with_locale); - if (parseint32_without_locale) { - assert(parseint32_out_without_locale == parseint32_out_with_locale); - } - int64_t parseint64_out_with_locale; - const bool parseint64_with_locale = ParseInt64(random_string, &parseint64_out_with_locale); - assert(parseint64_without_locale == parseint64_with_locale); - if (parseint64_without_locale) { - assert(parseint64_out_without_locale == parseint64_out_with_locale); - } const std::string tostring_with_locale = util::ToString(random_int64); assert(tostring_without_locale == tostring_with_locale); const std::string strprintf_int_with_locale = strprintf("%d", random_int64); diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp index 2cd31466790..6102647d695 100644 --- a/src/test/fuzz/parse_numbers.cpp +++ b/src/test/fuzz/parse_numbers.cpp @@ -5,33 +5,59 @@ #include #include #include +#include +#include +#include +#include #include FUZZ_TARGET(parse_numbers) { const std::string random_string(buffer.begin(), buffer.end()); + { + const auto i8{ToIntegral(random_string)}; + const auto u8{ToIntegral(random_string)}; + const auto i16{ToIntegral(random_string)}; + const auto u16{ToIntegral(random_string)}; + const auto i32{ToIntegral(random_string)}; + const auto u32{ToIntegral(random_string)}; + const auto i64{ToIntegral(random_string)}; + const auto u64{ToIntegral(random_string)}; + // Dont check any values, just that each success result must fit into + // the one with the largest bit-width. + if (i8) { + assert(i8 == i64); + } + if (u8) { + assert(u8 == u64); + } + if (i16) { + assert(i16 == i64); + } + if (u16) { + assert(u16 == u64); + } + if (i32) { + assert(i32 == i64); + } + if (u32) { + assert(u32 == u64); + } + constexpr auto digits{"0123456789"}; + if (i64) { + assert(util::RemovePrefixView(random_string, "-").find_first_not_of(digits) == std::string::npos); + } + if (u64) { + assert(random_string.find_first_not_of(digits) == std::string::npos); + } + } (void)ParseMoney(random_string); - uint8_t u8; - (void)ParseUInt8(random_string, &u8); - - uint16_t u16; - (void)ParseUInt16(random_string, &u16); - - int32_t i32; - (void)ParseInt32(random_string, &i32); (void)LocaleIndependentAtoi(random_string); - uint32_t u32; - (void)ParseUInt32(random_string, &u32); - int64_t i64; (void)LocaleIndependentAtoi(random_string); (void)ParseFixedPoint(random_string, 3, &i64); - (void)ParseInt64(random_string, &i64); - - uint64_t u64; - (void)ParseUInt64(random_string, &u64); } diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index 3422cb80233..7da87bd17fa 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -150,7 +150,6 @@ BOOST_AUTO_TEST_CASE(embedded_test) BOOST_AUTO_TEST_CASE(subnet_test) { - BOOST_CHECK(LookupSubNet("1.2.3.0/24") == LookupSubNet("1.2.3.0/255.255.255.0")); BOOST_CHECK(LookupSubNet("1.2.3.0/24") != LookupSubNet("1.2.4.0/255.255.255.0")); BOOST_CHECK(LookupSubNet("1.2.3.0/24").Match(ResolveIP("1.2.3.4"))); @@ -185,6 +184,7 @@ BOOST_AUTO_TEST_CASE(subnet_test) // Check valid/invalid BOOST_CHECK(LookupSubNet("1.2.3.0/0").IsValid()); BOOST_CHECK(!LookupSubNet("1.2.3.0/-1").IsValid()); + BOOST_CHECK(!LookupSubNet("1.2.3.0/+24").IsValid()); BOOST_CHECK(LookupSubNet("1.2.3.0/32").IsValid()); BOOST_CHECK(!LookupSubNet("1.2.3.0/33").IsValid()); BOOST_CHECK(!LookupSubNet("1.2.3.0/300").IsValid()); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index df481ff3f57..05d2c914c6a 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -648,40 +648,6 @@ BOOST_AUTO_TEST_CASE(util_overflow) TestAddMatrix(); } -BOOST_AUTO_TEST_CASE(test_ParseInt32) -{ - int32_t n; - // Valid values - BOOST_CHECK(ParseInt32("1234", nullptr)); - BOOST_CHECK(ParseInt32("0", &n) && n == 0); - BOOST_CHECK(ParseInt32("1234", &n) && n == 1234); - BOOST_CHECK(ParseInt32("01234", &n) && n == 1234); // no octal - BOOST_CHECK(ParseInt32("2147483647", &n) && n == 2147483647); - BOOST_CHECK(ParseInt32("-2147483648", &n) && n == (-2147483647 - 1)); // (-2147483647 - 1) equals INT_MIN - BOOST_CHECK(ParseInt32("-1234", &n) && n == -1234); - BOOST_CHECK(ParseInt32("00000000000000001234", &n) && n == 1234); - BOOST_CHECK(ParseInt32("-00000000000000001234", &n) && n == -1234); - BOOST_CHECK(ParseInt32("00000000000000000000", &n) && n == 0); - BOOST_CHECK(ParseInt32("-00000000000000000000", &n) && n == 0); - // Invalid values - BOOST_CHECK(!ParseInt32("", &n)); - BOOST_CHECK(!ParseInt32(" 1", &n)); // no padding inside - BOOST_CHECK(!ParseInt32("1 ", &n)); - BOOST_CHECK(!ParseInt32("++1", &n)); - BOOST_CHECK(!ParseInt32("+-1", &n)); - BOOST_CHECK(!ParseInt32("-+1", &n)); - BOOST_CHECK(!ParseInt32("--1", &n)); - BOOST_CHECK(!ParseInt32("1a", &n)); - BOOST_CHECK(!ParseInt32("aap", &n)); - BOOST_CHECK(!ParseInt32("0x1", &n)); // no hex - BOOST_CHECK(!ParseInt32(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); - // Overflow and underflow - BOOST_CHECK(!ParseInt32("-2147483649", nullptr)); - BOOST_CHECK(!ParseInt32("2147483648", nullptr)); - BOOST_CHECK(!ParseInt32("-32482348723847471234", nullptr)); - BOOST_CHECK(!ParseInt32("32482348723847471234", nullptr)); -} - template static void RunToIntegralTests() { @@ -869,171 +835,6 @@ BOOST_AUTO_TEST_CASE(test_LocaleIndependentAtoi) BOOST_CHECK_EQUAL(LocaleIndependentAtoi("256"), 255U); } -BOOST_AUTO_TEST_CASE(test_ParseInt64) -{ - int64_t n; - // Valid values - BOOST_CHECK(ParseInt64("1234", nullptr)); - BOOST_CHECK(ParseInt64("0", &n) && n == 0LL); - BOOST_CHECK(ParseInt64("1234", &n) && n == 1234LL); - BOOST_CHECK(ParseInt64("01234", &n) && n == 1234LL); // no octal - BOOST_CHECK(ParseInt64("2147483647", &n) && n == 2147483647LL); - BOOST_CHECK(ParseInt64("-2147483648", &n) && n == -2147483648LL); - BOOST_CHECK(ParseInt64("9223372036854775807", &n) && n == int64_t{9223372036854775807}); - BOOST_CHECK(ParseInt64("-9223372036854775808", &n) && n == int64_t{-9223372036854775807-1}); - BOOST_CHECK(ParseInt64("-1234", &n) && n == -1234LL); - // Invalid values - BOOST_CHECK(!ParseInt64("", &n)); - BOOST_CHECK(!ParseInt64(" 1", &n)); // no padding inside - BOOST_CHECK(!ParseInt64("1 ", &n)); - BOOST_CHECK(!ParseInt64("1a", &n)); - BOOST_CHECK(!ParseInt64("aap", &n)); - BOOST_CHECK(!ParseInt64("0x1", &n)); // no hex - BOOST_CHECK(!ParseInt64(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); - // Overflow and underflow - BOOST_CHECK(!ParseInt64("-9223372036854775809", nullptr)); - BOOST_CHECK(!ParseInt64("9223372036854775808", nullptr)); - BOOST_CHECK(!ParseInt64("-32482348723847471234", nullptr)); - BOOST_CHECK(!ParseInt64("32482348723847471234", nullptr)); -} - -BOOST_AUTO_TEST_CASE(test_ParseUInt8) -{ - uint8_t n; - // Valid values - BOOST_CHECK(ParseUInt8("255", nullptr)); - BOOST_CHECK(ParseUInt8("0", &n) && n == 0); - BOOST_CHECK(ParseUInt8("255", &n) && n == 255); - BOOST_CHECK(ParseUInt8("0255", &n) && n == 255); // no octal - BOOST_CHECK(ParseUInt8("255", &n) && n == static_cast(255)); - BOOST_CHECK(ParseUInt8("+255", &n) && n == 255); - BOOST_CHECK(ParseUInt8("00000000000000000012", &n) && n == 12); - BOOST_CHECK(ParseUInt8("00000000000000000000", &n) && n == 0); - // Invalid values - BOOST_CHECK(!ParseUInt8("-00000000000000000000", &n)); - BOOST_CHECK(!ParseUInt8("", &n)); - BOOST_CHECK(!ParseUInt8(" 1", &n)); // no padding inside - BOOST_CHECK(!ParseUInt8(" -1", &n)); - BOOST_CHECK(!ParseUInt8("++1", &n)); - BOOST_CHECK(!ParseUInt8("+-1", &n)); - BOOST_CHECK(!ParseUInt8("-+1", &n)); - BOOST_CHECK(!ParseUInt8("--1", &n)); - BOOST_CHECK(!ParseUInt8("-1", &n)); - BOOST_CHECK(!ParseUInt8("1 ", &n)); - BOOST_CHECK(!ParseUInt8("1a", &n)); - BOOST_CHECK(!ParseUInt8("aap", &n)); - BOOST_CHECK(!ParseUInt8("0x1", &n)); // no hex - BOOST_CHECK(!ParseUInt8(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); - // Overflow and underflow - BOOST_CHECK(!ParseUInt8("-255", &n)); - BOOST_CHECK(!ParseUInt8("256", &n)); - BOOST_CHECK(!ParseUInt8("-123", &n)); - BOOST_CHECK(!ParseUInt8("-123", nullptr)); - BOOST_CHECK(!ParseUInt8("256", nullptr)); -} - -BOOST_AUTO_TEST_CASE(test_ParseUInt16) -{ - uint16_t n; - // Valid values - BOOST_CHECK(ParseUInt16("1234", nullptr)); - BOOST_CHECK(ParseUInt16("0", &n) && n == 0); - BOOST_CHECK(ParseUInt16("1234", &n) && n == 1234); - BOOST_CHECK(ParseUInt16("01234", &n) && n == 1234); // no octal - BOOST_CHECK(ParseUInt16("65535", &n) && n == static_cast(65535)); - BOOST_CHECK(ParseUInt16("+65535", &n) && n == 65535); - BOOST_CHECK(ParseUInt16("00000000000000000012", &n) && n == 12); - BOOST_CHECK(ParseUInt16("00000000000000000000", &n) && n == 0); - // Invalid values - BOOST_CHECK(!ParseUInt16("-00000000000000000000", &n)); - BOOST_CHECK(!ParseUInt16("", &n)); - BOOST_CHECK(!ParseUInt16(" 1", &n)); // no padding inside - BOOST_CHECK(!ParseUInt16(" -1", &n)); - BOOST_CHECK(!ParseUInt16("++1", &n)); - BOOST_CHECK(!ParseUInt16("+-1", &n)); - BOOST_CHECK(!ParseUInt16("-+1", &n)); - BOOST_CHECK(!ParseUInt16("--1", &n)); - BOOST_CHECK(!ParseUInt16("-1", &n)); - BOOST_CHECK(!ParseUInt16("1 ", &n)); - BOOST_CHECK(!ParseUInt16("1a", &n)); - BOOST_CHECK(!ParseUInt16("aap", &n)); - BOOST_CHECK(!ParseUInt16("0x1", &n)); // no hex - BOOST_CHECK(!ParseUInt16(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); - // Overflow and underflow - BOOST_CHECK(!ParseUInt16("-65535", &n)); - BOOST_CHECK(!ParseUInt16("65536", &n)); - BOOST_CHECK(!ParseUInt16("-123", &n)); - BOOST_CHECK(!ParseUInt16("-123", nullptr)); - BOOST_CHECK(!ParseUInt16("65536", nullptr)); -} - -BOOST_AUTO_TEST_CASE(test_ParseUInt32) -{ - uint32_t n; - // Valid values - BOOST_CHECK(ParseUInt32("1234", nullptr)); - BOOST_CHECK(ParseUInt32("0", &n) && n == 0); - BOOST_CHECK(ParseUInt32("1234", &n) && n == 1234); - BOOST_CHECK(ParseUInt32("01234", &n) && n == 1234); // no octal - BOOST_CHECK(ParseUInt32("2147483647", &n) && n == 2147483647); - BOOST_CHECK(ParseUInt32("2147483648", &n) && n == uint32_t{2147483648}); - BOOST_CHECK(ParseUInt32("4294967295", &n) && n == uint32_t{4294967295}); - BOOST_CHECK(ParseUInt32("+1234", &n) && n == 1234); - BOOST_CHECK(ParseUInt32("00000000000000001234", &n) && n == 1234); - BOOST_CHECK(ParseUInt32("00000000000000000000", &n) && n == 0); - // Invalid values - BOOST_CHECK(!ParseUInt32("-00000000000000000000", &n)); - BOOST_CHECK(!ParseUInt32("", &n)); - BOOST_CHECK(!ParseUInt32(" 1", &n)); // no padding inside - BOOST_CHECK(!ParseUInt32(" -1", &n)); - BOOST_CHECK(!ParseUInt32("++1", &n)); - BOOST_CHECK(!ParseUInt32("+-1", &n)); - BOOST_CHECK(!ParseUInt32("-+1", &n)); - BOOST_CHECK(!ParseUInt32("--1", &n)); - BOOST_CHECK(!ParseUInt32("-1", &n)); - BOOST_CHECK(!ParseUInt32("1 ", &n)); - BOOST_CHECK(!ParseUInt32("1a", &n)); - BOOST_CHECK(!ParseUInt32("aap", &n)); - BOOST_CHECK(!ParseUInt32("0x1", &n)); // no hex - BOOST_CHECK(!ParseUInt32(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); - // Overflow and underflow - BOOST_CHECK(!ParseUInt32("-2147483648", &n)); - BOOST_CHECK(!ParseUInt32("4294967296", &n)); - BOOST_CHECK(!ParseUInt32("-1234", &n)); - BOOST_CHECK(!ParseUInt32("-32482348723847471234", nullptr)); - BOOST_CHECK(!ParseUInt32("32482348723847471234", nullptr)); -} - -BOOST_AUTO_TEST_CASE(test_ParseUInt64) -{ - uint64_t n; - // Valid values - BOOST_CHECK(ParseUInt64("1234", nullptr)); - BOOST_CHECK(ParseUInt64("0", &n) && n == 0LL); - BOOST_CHECK(ParseUInt64("1234", &n) && n == 1234LL); - BOOST_CHECK(ParseUInt64("01234", &n) && n == 1234LL); // no octal - BOOST_CHECK(ParseUInt64("2147483647", &n) && n == 2147483647LL); - BOOST_CHECK(ParseUInt64("9223372036854775807", &n) && n == 9223372036854775807ULL); - BOOST_CHECK(ParseUInt64("9223372036854775808", &n) && n == 9223372036854775808ULL); - BOOST_CHECK(ParseUInt64("18446744073709551615", &n) && n == 18446744073709551615ULL); - // Invalid values - BOOST_CHECK(!ParseUInt64("", &n)); - BOOST_CHECK(!ParseUInt64(" 1", &n)); // no padding inside - BOOST_CHECK(!ParseUInt64(" -1", &n)); - BOOST_CHECK(!ParseUInt64("1 ", &n)); - BOOST_CHECK(!ParseUInt64("1a", &n)); - BOOST_CHECK(!ParseUInt64("aap", &n)); - BOOST_CHECK(!ParseUInt64("0x1", &n)); // no hex - BOOST_CHECK(!ParseUInt64(STRING_WITH_EMBEDDED_NULL_CHAR, &n)); - // Overflow and underflow - BOOST_CHECK(!ParseUInt64("-9223372036854775809", nullptr)); - BOOST_CHECK(!ParseUInt64("18446744073709551616", nullptr)); - BOOST_CHECK(!ParseUInt64("-32482348723847471234", nullptr)); - BOOST_CHECK(!ParseUInt64("-2147483648", &n)); - BOOST_CHECK(!ParseUInt64("-9223372036854775808", &n)); - BOOST_CHECK(!ParseUInt64("-1234", &n)); -} - BOOST_AUTO_TEST_CASE(test_FormatParagraph) { BOOST_CHECK_EQUAL(FormatParagraph("", 79, 0), ""); diff --git a/src/util/bip32.cpp b/src/util/bip32.cpp index 4ab14bd7041..db40bfb5b9e 100644 --- a/src/util/bip32.cpp +++ b/src/util/bip32.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2019-2022 The Bitcoin Core developers +// Copyright (c) 2019-present The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -36,14 +36,11 @@ bool ParseHDKeypath(const std::string& keypath_str, std::vector& keypa } // Ensure this is only numbers - if (item.find_first_not_of( "0123456789" ) != std::string::npos) { + const auto number{ToIntegral(item)}; + if (!number) { return false; } - uint32_t number; - if (!ParseUInt32(item, &number)) { - return false; - } - path |= number; + path |= *number; keypath.push_back(path); first = false; diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp index fbccebad942..289f49d46a7 100644 --- a/src/util/strencodings.cpp +++ b/src/util/strencodings.cpp @@ -78,10 +78,9 @@ bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut) bool fBracketed = fHaveColon && (in[0] == '[' && in[colon - 1] == ']'); // if there is a colon, and in[0]=='[', colon is not 0, so in[colon-1] is safe bool fMultiColon{fHaveColon && colon != 0 && (in.find_last_of(':', colon - 1) != in.npos)}; if (fHaveColon && (colon == 0 || fBracketed || !fMultiColon)) { - uint16_t n; - if (ParseUInt16(in.substr(colon + 1), &n)) { + if (const auto n{ToIntegral(in.substr(colon + 1))}) { in = in.substr(0, colon); - portOut = n; + portOut = *n; valid = (portOut != 0); } } else { @@ -200,57 +199,6 @@ std::optional> DecodeBase32(std::string_view str) return ret; } -namespace { -template -bool ParseIntegral(std::string_view str, T* out) -{ - static_assert(std::is_integral_v); - // Replicate the exact behavior of strtol/strtoll/strtoul/strtoull when - // handling leading +/- for backwards compatibility. - if (str.length() >= 2 && str[0] == '+' && str[1] == '-') { - return false; - } - const std::optional opt_int = ToIntegral((!str.empty() && str[0] == '+') ? str.substr(1) : str); - if (!opt_int) { - return false; - } - if (out != nullptr) { - *out = *opt_int; - } - return true; -} -}; // namespace - -bool ParseInt32(std::string_view str, int32_t* out) -{ - return ParseIntegral(str, out); -} - -bool ParseInt64(std::string_view str, int64_t* out) -{ - return ParseIntegral(str, out); -} - -bool ParseUInt8(std::string_view str, uint8_t* out) -{ - return ParseIntegral(str, out); -} - -bool ParseUInt16(std::string_view str, uint16_t* out) -{ - return ParseIntegral(str, out); -} - -bool ParseUInt32(std::string_view str, uint32_t* out) -{ - return ParseIntegral(str, out); -} - -bool ParseUInt64(std::string_view str, uint64_t* out) -{ - return ParseIntegral(str, out); -} - std::string FormatParagraph(std::string_view in, size_t width, size_t indent) { assert(width >= indent); diff --git a/src/util/strencodings.h b/src/util/strencodings.h index fd713a555c7..01063858047 100644 --- a/src/util/strencodings.h +++ b/src/util/strencodings.h @@ -105,8 +105,7 @@ bool SplitHostPort(std::string_view in, uint16_t& portOut, std::string& hostOut) // LocaleIndependentAtoi is provided for backwards compatibility reasons. // -// New code should use ToIntegral or the ParseInt* functions -// which provide parse error feedback. +// New code should use ToIntegral. // // The goal of LocaleIndependentAtoi is to replicate the defined behaviour of // std::atoi as it behaves under the "C" locale, and remove some undefined @@ -187,48 +186,6 @@ std::optional ToIntegral(std::string_view str) return result; } -/** - * Convert string to signed 32-bit integer with strict parse error feedback. - * @returns true if the entire string could be parsed as valid integer, - * false if not the entire string could be parsed or when overflow or underflow occurred. - */ -[[nodiscard]] bool ParseInt32(std::string_view str, int32_t *out); - -/** - * Convert string to signed 64-bit integer with strict parse error feedback. - * @returns true if the entire string could be parsed as valid integer, - * false if not the entire string could be parsed or when overflow or underflow occurred. - */ -[[nodiscard]] bool ParseInt64(std::string_view str, int64_t *out); - -/** - * Convert decimal string to unsigned 8-bit integer with strict parse error feedback. - * @returns true if the entire string could be parsed as valid integer, - * false if not the entire string could be parsed or when overflow or underflow occurred. - */ -[[nodiscard]] bool ParseUInt8(std::string_view str, uint8_t *out); - -/** - * Convert decimal string to unsigned 16-bit integer with strict parse error feedback. - * @returns true if the entire string could be parsed as valid integer, - * false if the entire string could not be parsed or if overflow or underflow occurred. - */ -[[nodiscard]] bool ParseUInt16(std::string_view str, uint16_t* out); - -/** - * Convert decimal string to unsigned 32-bit integer with strict parse error feedback. - * @returns true if the entire string could be parsed as valid integer, - * false if not the entire string could be parsed or when overflow or underflow occurred. - */ -[[nodiscard]] bool ParseUInt32(std::string_view str, uint32_t *out); - -/** - * Convert decimal string to unsigned 64-bit integer with strict parse error feedback. - * @returns true if the entire string could be parsed as valid integer, - * false if not the entire string could be parsed or when overflow or underflow occurred. - */ -[[nodiscard]] bool ParseUInt64(std::string_view str, uint64_t *out); - /** * Format a paragraph of text to a fixed width, adding spaces for * indentation to any added line. diff --git a/src/validation.cpp b/src/validation.cpp index 13dbe83f806..a9d5ed0a740 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -5909,6 +5909,10 @@ util::Result ChainstateManager::PopulateAndValidateSnapshot( --coins_left; ++coins_processed; + // Show Snapshot Loading progress + double progress = static_cast(coins_processed) / static_cast(coins_count); + GetNotifications().snapshotLoadProgress(progress); + if (coins_processed % 1000000 == 0) { LogPrintf("[snapshot] %d coins loaded (%.2f%%, %.2f MB)\n", coins_processed, diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp index ceed5b6b52b..fc904de973b 100644 --- a/src/wallet/dump.cpp +++ b/src/wallet/dump.cpp @@ -151,13 +151,13 @@ bool CreateFromDump(const ArgsManager& args, const std::string& name, const fs:: return false; } // Check the version number (value of first record) - uint32_t ver; - if (!ParseUInt32(version_value, &ver)) { - error =strprintf(_("Error: Unable to parse version %u as a uint32_t"), version_value); + const auto ver{ToIntegral(version_value)}; + if (!ver) { + error = strprintf(_("Error: Unable to parse version %u as a uint32_t"), version_value); dump_file.close(); return false; } - if (ver != DUMP_VERSION) { + if (*ver != DUMP_VERSION) { error = strprintf(_("Error: Dumpfile version is not supported. This version of bitcoin-wallet only supports version 1 dumpfiles. Got dumpfile with version %s"), version_value); dump_file.close(); return false; diff --git a/test/functional/interface_rest.py b/test/functional/interface_rest.py index 0e294696b0e..a853b2ec0ec 100755 --- a/test/functional/interface_rest.py +++ b/test/functional/interface_rest.py @@ -271,6 +271,8 @@ def run_test(self): # Check invalid blockhashbyheight requests resp = self.test_rest_request(f"/blockhashbyheight/{INVALID_PARAM}", ret_type=RetType.OBJ, status=400) assert_equal(resp.read().decode('utf-8').rstrip(), f"Invalid height: {INVALID_PARAM}") + resp = self.test_rest_request("/blockhashbyheight/+1", ret_type=RetType.OBJ, status=400) + assert_equal(resp.read().decode('utf-8').rstrip(), "Invalid height: +1") resp = self.test_rest_request("/blockhashbyheight/1000000", ret_type=RetType.OBJ, status=404) assert_equal(resp.read().decode('utf-8').rstrip(), "Block height out of range") resp = self.test_rest_request("/blockhashbyheight/-1", ret_type=RetType.OBJ, status=400) diff --git a/test/util/data/bitcoin-util-test.json b/test/util/data/bitcoin-util-test.json index 83b3c430d53..896decc4e3e 100644 --- a/test/util/data/bitcoin-util-test.json +++ b/test/util/data/bitcoin-util-test.json @@ -56,6 +56,12 @@ "output_cmp": "blanktxv2.json", "description": "Creates a blank transaction when nothing is piped into bitcoin-tx (output in json)" }, + { "exec": "./bitcoin-tx", + "args": ["-create", "nversion=+1"], + "return_code": 1, + "error_txt": "error: Invalid TX version requested", + "description": "Tests the check for invalid nversion value" + }, { "exec": "./bitcoin-tx", "args": ["-create", "nversion=1foo"], "return_code": 1, @@ -126,6 +132,12 @@ "output_cmp": "tt-locktime317000-out.json", "description": "Adds an nlocktime to a transaction (output in json)" }, + { "exec": "./bitcoin-tx", + "args": ["-create", "locktime=+317"], + "return_code": 1, + "error_txt": "error: Invalid TX locktime requested", + "description": "Tests the check for invalid locktime value" + }, { "exec": "./bitcoin-tx", "args": ["-create", "locktime=317000foo"], "return_code": 1, @@ -165,6 +177,15 @@ "error_txt": "error: Invalid TX input index", "description": "Tests the check for an invalid string input index with replaceable" }, + { "exec": "./bitcoin-tx", + "args": + ["-create", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0", + "replaceable=+0"], + "return_code": 1, + "error_txt": "error: Invalid TX input index", + "description": "Tests the check for an invalid string input index with replaceable" + }, { "exec": "./bitcoin-tx", "args": @@ -243,6 +264,14 @@ "error_txt": "error: invalid TX sequence id 'abcdef00'", "description": "Try to make invalid input replaceable" }, + { "exec": "./bitcoin-tx", + "args": + ["-create", + "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:+0"], + "return_code": 1, + "error_txt": "error: invalid TX input vout", + "description": "Tests the check for an invalid vout value when adding an input" + }, { "exec": "./bitcoin-tx", "args": ["-create",