diff options
author | fanquake <fanquake@gmail.com> | 2021-08-24 09:55:35 +0800 |
---|---|---|
committer | fanquake <fanquake@gmail.com> | 2021-08-24 10:43:38 +0800 |
commit | 61a843e43bf113df7fa6062c84257c76ee40141f (patch) | |
tree | cbf88957727f1baa93a67b5c58630461c71c123f | |
parent | d3203a99d886177eee9d1f9cd8411e215118a4e6 (diff) | |
parent | f7752adba5dd35fccd3f2144cfcf03538ebf275b (diff) |
Merge bitcoin/bitcoin#22220: util: make ParseMoney return a std::optional<CAmount>
f7752adba5dd35fccd3f2144cfcf03538ebf275b util: check MoneyRange() inside ParseMoney() (fanquake)
5ef2738089efd396186775ad23aaec71ea44ebb1 util: make ParseMoney return a std::optional<CAmount> (fanquake)
Pull request description:
Related discussion in #22193.
ACKs for top commit:
MarcoFalke:
review ACK f7752adba5dd35fccd3f2144cfcf03538ebf275b 📄
Tree-SHA512: 88453f9e28f668deff4290d4bc0b2468cbd54699a3be1bfeac63a512276d309354672e7ea7deefa01466c3d9d826e837cc1ea244d4d74b4fa9c11c56f074e098
-rw-r--r-- | src/bitcoin-tx.cpp | 7 | ||||
-rw-r--r-- | src/init.cpp | 26 | ||||
-rw-r--r-- | src/miner.cpp | 8 | ||||
-rw-r--r-- | src/test/fuzz/integer.cpp | 10 | ||||
-rw-r--r-- | src/test/fuzz/parse_numbers.cpp | 3 | ||||
-rw-r--r-- | src/test/util_tests.cpp | 113 | ||||
-rw-r--r-- | src/util/moneystr.cpp | 29 | ||||
-rw-r--r-- | src/util/moneystr.h | 3 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 70 |
9 files changed, 126 insertions, 143 deletions
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 3fc87ae1ff..cd2cdf01f2 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -188,10 +188,11 @@ static void RegisterLoad(const std::string& strInput) static CAmount ExtractAndValidateValue(const std::string& strValue) { - CAmount value; - if (!ParseMoney(strValue, value)) + if (std::optional<CAmount> parsed = ParseMoney(strValue)) { + return parsed.value(); + } else { throw std::runtime_error("invalid TX output value"); - return value; + } } static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal) diff --git a/src/init.cpp b/src/init.cpp index bf027b2879..d4a3c891b1 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -921,10 +921,11 @@ bool AppInitParameterInteraction(const ArgsManager& args) // incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool // and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting. if (args.IsArgSet("-incrementalrelayfee")) { - CAmount n = 0; - if (!ParseMoney(args.GetArg("-incrementalrelayfee", ""), n)) + if (std::optional<CAmount> inc_relay_fee = ParseMoney(args.GetArg("-incrementalrelayfee", ""))) { + ::incrementalRelayFee = CFeeRate{inc_relay_fee.value()}; + } else { return InitError(AmountErrMsg("incrementalrelayfee", args.GetArg("-incrementalrelayfee", ""))); - incrementalRelayFee = CFeeRate(n); + } } // block pruning; get the amount of disk space (in MiB) to allot for block & undo files @@ -956,12 +957,12 @@ bool AppInitParameterInteraction(const ArgsManager& args) } if (args.IsArgSet("-minrelaytxfee")) { - CAmount n = 0; - if (!ParseMoney(args.GetArg("-minrelaytxfee", ""), n)) { + if (std::optional<CAmount> min_relay_fee = ParseMoney(args.GetArg("-minrelaytxfee", ""))) { + // High fee check is done afterward in CWallet::Create() + ::minRelayTxFee = CFeeRate{min_relay_fee.value()}; + } else { return InitError(AmountErrMsg("minrelaytxfee", args.GetArg("-minrelaytxfee", ""))); } - // High fee check is done afterward in CWallet::Create() - ::minRelayTxFee = CFeeRate(n); } else if (incrementalRelayFee > ::minRelayTxFee) { // Allow only setting incrementalRelayFee to control both ::minRelayTxFee = incrementalRelayFee; @@ -971,18 +972,19 @@ bool AppInitParameterInteraction(const ArgsManager& args) // Sanity check argument for min fee for including tx in block // TODO: Harmonize which arguments need sanity checking and where that happens if (args.IsArgSet("-blockmintxfee")) { - CAmount n = 0; - if (!ParseMoney(args.GetArg("-blockmintxfee", ""), n)) + if (!ParseMoney(args.GetArg("-blockmintxfee", ""))) { return InitError(AmountErrMsg("blockmintxfee", args.GetArg("-blockmintxfee", ""))); + } } // Feerate used to define dust. Shouldn't be changed lightly as old // implementations may inadvertently create non-standard transactions if (args.IsArgSet("-dustrelayfee")) { - CAmount n = 0; - if (!ParseMoney(args.GetArg("-dustrelayfee", ""), n)) + if (std::optional<CAmount> parsed = ParseMoney(args.GetArg("-dustrelayfee", ""))) { + dustRelayFee = CFeeRate{parsed.value()}; + } else { return InitError(AmountErrMsg("dustrelayfee", args.GetArg("-dustrelayfee", ""))); - dustRelayFee = CFeeRate(n); + } } fRequireStandard = !args.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); diff --git a/src/miner.cpp b/src/miner.cpp index d9186a5d6d..168ade5507 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -73,11 +73,11 @@ static BlockAssembler::Options DefaultOptions() // If -blockmaxweight is not given, limit to DEFAULT_BLOCK_MAX_WEIGHT BlockAssembler::Options options; options.nBlockMaxWeight = gArgs.GetArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT); - CAmount n = 0; - if (gArgs.IsArgSet("-blockmintxfee") && ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) { - options.blockMinFeeRate = CFeeRate(n); + if (gArgs.IsArgSet("-blockmintxfee")) { + std::optional<CAmount> parsed = ParseMoney(gArgs.GetArg("-blockmintxfee", "")); + options.blockMinFeeRate = CFeeRate{parsed.value_or(DEFAULT_BLOCK_MIN_TX_FEE)}; } else { - options.blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE); + options.blockMinFeeRate = CFeeRate{DEFAULT_BLOCK_MIN_TX_FEE}; } return options; } diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp index e28e2feb0a..5d26529837 100644 --- a/src/test/fuzz/integer.cpp +++ b/src/test/fuzz/integer.cpp @@ -83,9 +83,8 @@ FUZZ_TARGET_INIT(integer, initialize_integer) (void)FormatISO8601Date(i64); (void)FormatISO8601DateTime(i64); { - int64_t parsed_money; - if (ParseMoney(FormatMoney(i64), parsed_money)) { - assert(parsed_money == i64); + if (std::optional<CAmount> parsed = ParseMoney(FormatMoney(i64))) { + assert(parsed.value() == i64); } } (void)GetSizeOfCompactSize(u64); @@ -126,9 +125,8 @@ FUZZ_TARGET_INIT(integer, initialize_integer) (void)ToLower(ch); (void)ToUpper(ch); { - int64_t parsed_money; - if (ParseMoney(ValueFromAmount(i64).getValStr(), parsed_money)) { - assert(parsed_money == i64); + if (std::optional<CAmount> parsed = ParseMoney(ValueFromAmount(i64).getValStr())) { + assert(parsed.value() == i64); } } if (i32 >= 0 && i32 <= 16) { diff --git a/src/test/fuzz/parse_numbers.cpp b/src/test/fuzz/parse_numbers.cpp index 2c546e9b4a..69e58c3f63 100644 --- a/src/test/fuzz/parse_numbers.cpp +++ b/src/test/fuzz/parse_numbers.cpp @@ -12,8 +12,7 @@ FUZZ_TARGET(parse_numbers) { const std::string random_string(buffer.begin(), buffer.end()); - CAmount amount; - (void)ParseMoney(random_string, amount); + (void)ParseMoney(random_string); double d; (void)ParseDouble(random_string, &d); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 7ce38519cf..4f9d10cf1d 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -1222,86 +1222,59 @@ BOOST_AUTO_TEST_CASE(util_FormatMoney) BOOST_AUTO_TEST_CASE(util_ParseMoney) { - CAmount ret = 0; - BOOST_CHECK(ParseMoney("0.0", ret)); - BOOST_CHECK_EQUAL(ret, 0); - - BOOST_CHECK(ParseMoney("12345.6789", ret)); - BOOST_CHECK_EQUAL(ret, (COIN/10000)*123456789); - - BOOST_CHECK(ParseMoney("100000000.00", ret)); - BOOST_CHECK_EQUAL(ret, COIN*100000000); - BOOST_CHECK(ParseMoney("10000000.00", ret)); - BOOST_CHECK_EQUAL(ret, COIN*10000000); - BOOST_CHECK(ParseMoney("1000000.00", ret)); - BOOST_CHECK_EQUAL(ret, COIN*1000000); - BOOST_CHECK(ParseMoney("100000.00", ret)); - BOOST_CHECK_EQUAL(ret, COIN*100000); - BOOST_CHECK(ParseMoney("10000.00", ret)); - BOOST_CHECK_EQUAL(ret, COIN*10000); - BOOST_CHECK(ParseMoney("1000.00", ret)); - BOOST_CHECK_EQUAL(ret, COIN*1000); - BOOST_CHECK(ParseMoney("100.00", ret)); - BOOST_CHECK_EQUAL(ret, COIN*100); - BOOST_CHECK(ParseMoney("10.00", ret)); - BOOST_CHECK_EQUAL(ret, COIN*10); - BOOST_CHECK(ParseMoney("1.00", ret)); - BOOST_CHECK_EQUAL(ret, COIN); - BOOST_CHECK(ParseMoney("1", ret)); - BOOST_CHECK_EQUAL(ret, COIN); - BOOST_CHECK(ParseMoney(" 1", ret)); - BOOST_CHECK_EQUAL(ret, COIN); - BOOST_CHECK(ParseMoney("1 ", ret)); - BOOST_CHECK_EQUAL(ret, COIN); - BOOST_CHECK(ParseMoney(" 1 ", ret)); - BOOST_CHECK_EQUAL(ret, COIN); - BOOST_CHECK(ParseMoney("0.1", ret)); - BOOST_CHECK_EQUAL(ret, COIN/10); - BOOST_CHECK(ParseMoney("0.01", ret)); - BOOST_CHECK_EQUAL(ret, COIN/100); - BOOST_CHECK(ParseMoney("0.001", ret)); - BOOST_CHECK_EQUAL(ret, COIN/1000); - BOOST_CHECK(ParseMoney("0.0001", ret)); - BOOST_CHECK_EQUAL(ret, COIN/10000); - BOOST_CHECK(ParseMoney("0.00001", ret)); - BOOST_CHECK_EQUAL(ret, COIN/100000); - BOOST_CHECK(ParseMoney("0.000001", ret)); - BOOST_CHECK_EQUAL(ret, COIN/1000000); - BOOST_CHECK(ParseMoney("0.0000001", ret)); - BOOST_CHECK_EQUAL(ret, COIN/10000000); - BOOST_CHECK(ParseMoney("0.00000001", ret)); - BOOST_CHECK_EQUAL(ret, COIN/100000000); - BOOST_CHECK(ParseMoney(" 0.00000001 ", ret)); - BOOST_CHECK_EQUAL(ret, COIN/100000000); - BOOST_CHECK(ParseMoney("0.00000001 ", ret)); - BOOST_CHECK_EQUAL(ret, COIN/100000000); - BOOST_CHECK(ParseMoney(" 0.00000001", ret)); - BOOST_CHECK_EQUAL(ret, COIN/100000000); - - // Parsing amount that can not be represented in ret should fail - BOOST_CHECK(!ParseMoney("0.000000001", ret)); + BOOST_CHECK_EQUAL(ParseMoney("0.0").value(), 0); + + BOOST_CHECK_EQUAL(ParseMoney("12345.6789").value(), (COIN/10000)*123456789); + + BOOST_CHECK_EQUAL(ParseMoney("10000000.00").value(), COIN*10000000); + BOOST_CHECK_EQUAL(ParseMoney("1000000.00").value(), COIN*1000000); + BOOST_CHECK_EQUAL(ParseMoney("100000.00").value(), COIN*100000); + BOOST_CHECK_EQUAL(ParseMoney("10000.00").value(), COIN*10000); + BOOST_CHECK_EQUAL(ParseMoney("1000.00").value(), COIN*1000); + BOOST_CHECK_EQUAL(ParseMoney("100.00").value(), COIN*100); + BOOST_CHECK_EQUAL(ParseMoney("10.00").value(), COIN*10); + BOOST_CHECK_EQUAL(ParseMoney("1.00").value(), COIN); + BOOST_CHECK_EQUAL(ParseMoney("1").value(), COIN); + BOOST_CHECK_EQUAL(ParseMoney(" 1").value(), COIN); + BOOST_CHECK_EQUAL(ParseMoney("1 ").value(), COIN); + BOOST_CHECK_EQUAL(ParseMoney(" 1 ").value(), COIN); + BOOST_CHECK_EQUAL(ParseMoney("0.1").value(), COIN/10); + BOOST_CHECK_EQUAL(ParseMoney("0.01").value(), COIN/100); + BOOST_CHECK_EQUAL(ParseMoney("0.001").value(), COIN/1000); + BOOST_CHECK_EQUAL(ParseMoney("0.0001").value(), COIN/10000); + BOOST_CHECK_EQUAL(ParseMoney("0.00001").value(), COIN/100000); + BOOST_CHECK_EQUAL(ParseMoney("0.000001").value(), COIN/1000000); + BOOST_CHECK_EQUAL(ParseMoney("0.0000001").value(), COIN/10000000); + BOOST_CHECK_EQUAL(ParseMoney("0.00000001").value(), COIN/100000000); + BOOST_CHECK_EQUAL(ParseMoney(" 0.00000001 ").value(), COIN/100000000); + BOOST_CHECK_EQUAL(ParseMoney("0.00000001 ").value(), COIN/100000000); + BOOST_CHECK_EQUAL(ParseMoney(" 0.00000001").value(), COIN/100000000); + + // Parsing amount that can not be represented should fail + BOOST_CHECK(!ParseMoney("100000000.00")); + BOOST_CHECK(!ParseMoney("0.000000001")); // Parsing empty string should fail - BOOST_CHECK(!ParseMoney("", ret)); - BOOST_CHECK(!ParseMoney(" ", ret)); - BOOST_CHECK(!ParseMoney(" ", ret)); + BOOST_CHECK(!ParseMoney("")); + BOOST_CHECK(!ParseMoney(" ")); + BOOST_CHECK(!ParseMoney(" ")); // Parsing two numbers should fail - BOOST_CHECK(!ParseMoney("1 2", ret)); - BOOST_CHECK(!ParseMoney(" 1 2 ", ret)); - BOOST_CHECK(!ParseMoney(" 1.2 3 ", ret)); - BOOST_CHECK(!ParseMoney(" 1 2.3 ", ret)); + BOOST_CHECK(!ParseMoney("1 2")); + BOOST_CHECK(!ParseMoney(" 1 2 ")); + BOOST_CHECK(!ParseMoney(" 1.2 3 ")); + BOOST_CHECK(!ParseMoney(" 1 2.3 ")); // Attempted 63 bit overflow should fail - BOOST_CHECK(!ParseMoney("92233720368.54775808", ret)); + BOOST_CHECK(!ParseMoney("92233720368.54775808")); // Parsing negative amounts must fail - BOOST_CHECK(!ParseMoney("-1", ret)); + BOOST_CHECK(!ParseMoney("-1")); // Parsing strings with embedded NUL characters should fail - BOOST_CHECK(!ParseMoney("\0-1"s, ret)); - BOOST_CHECK(!ParseMoney(STRING_WITH_EMBEDDED_NULL_CHAR, ret)); - BOOST_CHECK(!ParseMoney("1\0"s, ret)); + BOOST_CHECK(!ParseMoney("\0-1"s)); + BOOST_CHECK(!ParseMoney(STRING_WITH_EMBEDDED_NULL_CHAR)); + BOOST_CHECK(!ParseMoney("1\0"s)); } BOOST_AUTO_TEST_CASE(util_IsHex) diff --git a/src/util/moneystr.cpp b/src/util/moneystr.cpp index 3f9ce7dce4..d3f4029607 100644 --- a/src/util/moneystr.cpp +++ b/src/util/moneystr.cpp @@ -5,10 +5,13 @@ #include <util/moneystr.h> +#include <amount.h> #include <tinyformat.h> #include <util/strencodings.h> #include <util/string.h> +#include <optional> + std::string FormatMoney(const CAmount n) { // Note: not using straight sprintf here because we do NOT want @@ -35,14 +38,14 @@ std::string FormatMoney(const CAmount n) } -bool ParseMoney(const std::string& money_string, CAmount& nRet) +std::optional<CAmount> ParseMoney(const std::string& money_string) { if (!ValidAsCString(money_string)) { - return false; + return std::nullopt; } const std::string str = TrimString(money_string); if (str.empty()) { - return false; + return std::nullopt; } std::string strWhole; @@ -62,21 +65,25 @@ bool ParseMoney(const std::string& money_string, CAmount& nRet) break; } if (IsSpace(*p)) - return false; + return std::nullopt; if (!IsDigit(*p)) - return false; + return std::nullopt; strWhole.insert(strWhole.end(), *p); } if (*p) { - return false; + return std::nullopt; } if (strWhole.size() > 10) // guard against 63 bit overflow - return false; + return std::nullopt; if (nUnits < 0 || nUnits > COIN) - return false; + return std::nullopt; int64_t nWhole = atoi64(strWhole); - CAmount nValue = nWhole*COIN + nUnits; - nRet = nValue; - return true; + CAmount value = nWhole * COIN + nUnits; + + if (!MoneyRange(value)) { + return std::nullopt; + } + + return value; } diff --git a/src/util/moneystr.h b/src/util/moneystr.h index 2aedbee358..b71dffd0db 100644 --- a/src/util/moneystr.h +++ b/src/util/moneystr.h @@ -12,6 +12,7 @@ #include <amount.h> #include <attributes.h> +#include <optional> #include <string> /* Do not use these functions to represent or parse monetary amounts to or from @@ -19,6 +20,6 @@ */ std::string FormatMoney(const CAmount n); /** Parse an amount denoted in full coins. E.g. "0.0034" supplied on the command line. **/ -[[nodiscard]] bool ParseMoney(const std::string& str, CAmount& nRet); +std::optional<CAmount> ParseMoney(const std::string& str); #endif // BITCOIN_UTIL_MONEYSTR_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index cf869fac0c..dbcfc4dac3 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2609,72 +2609,73 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri } if (gArgs.IsArgSet("-mintxfee")) { - CAmount n = 0; - if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) { + std::optional<CAmount> min_tx_fee = ParseMoney(gArgs.GetArg("-mintxfee", "")); + if (!min_tx_fee || min_tx_fee.value() == 0) { error = AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")); return nullptr; - } - if (n > HIGH_TX_FEE_PER_KB) { + } else if (min_tx_fee.value() > HIGH_TX_FEE_PER_KB) { warnings.push_back(AmountHighWarn("-mintxfee") + Untranslated(" ") + _("This is the minimum transaction fee you pay on every transaction.")); } - walletInstance->m_min_fee = CFeeRate(n); + + walletInstance->m_min_fee = CFeeRate{min_tx_fee.value()}; } if (gArgs.IsArgSet("-maxapsfee")) { const std::string max_aps_fee{gArgs.GetArg("-maxapsfee", "")}; - CAmount n = 0; if (max_aps_fee == "-1") { - n = -1; - } else if (!ParseMoney(max_aps_fee, n)) { + walletInstance->m_max_aps_fee = -1; + } else if (std::optional<CAmount> max_fee = ParseMoney(max_aps_fee)) { + if (max_fee.value() > HIGH_APS_FEE) { + warnings.push_back(AmountHighWarn("-maxapsfee") + Untranslated(" ") + + _("This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.")); + } + walletInstance->m_max_aps_fee = max_fee.value(); + } else { error = AmountErrMsg("maxapsfee", max_aps_fee); return nullptr; } - if (n > HIGH_APS_FEE) { - warnings.push_back(AmountHighWarn("-maxapsfee") + Untranslated(" ") + - _("This is the maximum transaction fee you pay (in addition to the normal fee) to prioritize partial spend avoidance over regular coin selection.")); - } - walletInstance->m_max_aps_fee = n; } if (gArgs.IsArgSet("-fallbackfee")) { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) { + std::optional<CAmount> fallback_fee = ParseMoney(gArgs.GetArg("-fallbackfee", "")); + if (!fallback_fee) { error = strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", "")); return nullptr; - } - if (nFeePerK > HIGH_TX_FEE_PER_KB) { + } else if (fallback_fee.value() > HIGH_TX_FEE_PER_KB) { warnings.push_back(AmountHighWarn("-fallbackfee") + Untranslated(" ") + _("This is the transaction fee you may pay when fee estimates are not available.")); } - walletInstance->m_fallback_fee = CFeeRate(nFeePerK); + walletInstance->m_fallback_fee = CFeeRate{fallback_fee.value()}; } + // Disable fallback fee in case value was set to 0, enable if non-null value walletInstance->m_allow_fallback_fee = walletInstance->m_fallback_fee.GetFeePerK() != 0; if (gArgs.IsArgSet("-discardfee")) { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) { + std::optional<CAmount> discard_fee = ParseMoney(gArgs.GetArg("-discardfee", "")); + if (!discard_fee) { error = strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")); return nullptr; - } - if (nFeePerK > HIGH_TX_FEE_PER_KB) { + } else if (discard_fee.value() > HIGH_TX_FEE_PER_KB) { warnings.push_back(AmountHighWarn("-discardfee") + Untranslated(" ") + _("This is the transaction fee you may discard if change is smaller than dust at this level")); } - walletInstance->m_discard_rate = CFeeRate(nFeePerK); + walletInstance->m_discard_rate = CFeeRate{discard_fee.value()}; } + if (gArgs.IsArgSet("-paytxfee")) { - CAmount nFeePerK = 0; - if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) { + std::optional<CAmount> pay_tx_fee = ParseMoney(gArgs.GetArg("-paytxfee", "")); + if (!pay_tx_fee) { error = AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")); return nullptr; - } - if (nFeePerK > HIGH_TX_FEE_PER_KB) { + } else if (pay_tx_fee.value() > HIGH_TX_FEE_PER_KB) { warnings.push_back(AmountHighWarn("-paytxfee") + Untranslated(" ") + _("This is the transaction fee you will pay if you send a transaction.")); } - walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000); + + walletInstance->m_pay_tx_fee = CFeeRate{pay_tx_fee.value(), 1000}; + if (chain && walletInstance->m_pay_tx_fee < chain->relayMinFee()) { error = strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"), gArgs.GetArg("-paytxfee", ""), chain->relayMinFee().ToString()); @@ -2683,20 +2684,21 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri } if (gArgs.IsArgSet("-maxtxfee")) { - CAmount nMaxFee = 0; - if (!ParseMoney(gArgs.GetArg("-maxtxfee", ""), nMaxFee)) { + std::optional<CAmount> max_fee = ParseMoney(gArgs.GetArg("-maxtxfee", "")); + if (!max_fee) { error = AmountErrMsg("maxtxfee", gArgs.GetArg("-maxtxfee", "")); return nullptr; - } - if (nMaxFee > HIGH_MAX_TX_FEE) { + } else if (max_fee.value() > HIGH_MAX_TX_FEE) { warnings.push_back(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction.")); } - if (chain && CFeeRate(nMaxFee, 1000) < chain->relayMinFee()) { + + if (chain && CFeeRate{max_fee.value(), 1000} < chain->relayMinFee()) { error = strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"), gArgs.GetArg("-maxtxfee", ""), chain->relayMinFee().ToString()); return nullptr; } - walletInstance->m_default_max_tx_fee = nMaxFee; + + walletInstance->m_default_max_tx_fee = max_fee.value(); } if (chain && chain->relayMinFee().GetFeePerK() > HIGH_TX_FEE_PER_KB) { |