diff --git a/src/amount.h b/src/amount.h --- a/src/amount.h +++ b/src/amount.h @@ -27,8 +27,9 @@ constexpr Amount(const Amount &other) : amount(other.amount) {} /** - * Convenient implicit UniValue conversion operator + * Implicit conversions, to and from UniValue, for convenience. */ + Amount(const UniValue &value); operator UniValue() const; /** diff --git a/src/amount.cpp b/src/amount.cpp --- a/src/amount.cpp +++ b/src/amount.cpp @@ -7,7 +7,10 @@ #include #include +#include +#include #include +#include #include @@ -16,6 +19,20 @@ CURRENCY_UNIT); } +Amount::Amount(const UniValue &value) { + if (!value.isNum() && !value.isStr()) { + throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string"); + } + + if (!ParseFixedPoint(value.getValStr(), 8, &amount)) { + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); + } + + if (!MoneyRange(*this)) { + throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); + } +} + Amount::operator UniValue() const { bool sign = *this < Amount::zero(); Amount n_abs(sign ? -amount : amount); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -559,24 +559,6 @@ return false; } -static Amount AmountFromValue(const UniValue &value) { - if (!value.isNum() && !value.isStr()) { - throw std::runtime_error("Amount is not a number or string"); - } - - int64_t n; - if (!ParseFixedPoint(value.getValStr(), 8, &n)) { - throw std::runtime_error("Invalid amount"); - } - Amount amount = n * SATOSHI; - - if (!MoneyRange(amount)) { - throw std::runtime_error("Amount out of range"); - } - - return amount; -} - static void MutateTxSign(CMutableTransaction &tx, const std::string &flagStr) { SigHashType sigHashType = SigHashType().withForkId(); @@ -664,7 +646,7 @@ txout.scriptPubKey = scriptPubKey; txout.nValue = Amount::zero(); if (prevOut.exists("amount")) { - txout.nValue = AmountFromValue(prevOut["amount"]); + txout.nValue = prevOut["amount"]; } view.AddCoin(out, Coin(txout, 1, false), true); @@ -865,6 +847,10 @@ } catch (const std::exception &e) { strPrint = std::string("error: ") + e.what(); nRet = EXIT_FAILURE; + } catch (const UniValue &e) { + strPrint = std::string("error code: ") + e["code"].getValStr() + + " message: " + e["message"].getValStr(); + nRet = EXIT_FAILURE; } catch (...) { PrintExceptionContinue(nullptr, "CommandLineRawTx()"); throw; diff --git a/src/rpc/avalanche.cpp b/src/rpc/avalanche.cpp --- a/src/rpc/avalanche.cpp +++ b/src/rpc/avalanche.cpp @@ -187,7 +187,7 @@ throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing amount"); } - const Amount amount = AmountFromValue(find_value(stake, "amount")); + const Amount amount = find_value(stake, "amount"); const UniValue &iscbparam = find_value(stake, "iscoinbase"); const bool iscoinbase = diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -988,7 +988,7 @@ RPCTypeCheck(request.params, { UniValue::VSTR, - // VNUM or VSTR, checked inside AmountFromValue() + // VNUM or VSTR, checked inside Amount::Amount(UniValue T) UniValueType(), }); @@ -1000,10 +1000,9 @@ CTransactionRef tx(MakeTransactionRef(std::move(mtx))); - const CFeeRate max_raw_tx_fee_rate = - request.params[1].isNull() - ? DEFAULT_MAX_RAW_TX_FEE_RATE - : CFeeRate(AmountFromValue(request.params[1])); + const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() + ? DEFAULT_MAX_RAW_TX_FEE_RATE + : CFeeRate(request.params[1]); int64_t virtual_size = GetVirtualTransactionSize(*tx); Amount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size); @@ -1087,7 +1086,7 @@ RPCTypeCheck(request.params, { UniValue::VARR, - // VNUM or VSTR, checked inside AmountFromValue() + // VNUM or VSTR, checked inside Amount::Amount(UniValue T) UniValueType(), }); @@ -1104,10 +1103,9 @@ CTransactionRef tx(MakeTransactionRef(std::move(mtx))); const TxId &txid = tx->GetId(); - const CFeeRate max_raw_tx_fee_rate = - request.params[1].isNull() - ? DEFAULT_MAX_RAW_TX_FEE_RATE - : CFeeRate(AmountFromValue(request.params[1])); + const CFeeRate max_raw_tx_fee_rate = request.params[1].isNull() + ? DEFAULT_MAX_RAW_TX_FEE_RATE + : CFeeRate(request.params[1]); CTxMemPool &mempool = EnsureMemPool(request.context); int64_t virtual_size = GetVirtualTransactionSize(*tx); diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -141,7 +141,7 @@ } CScript scriptPubKey = GetScriptForDestination(destination); - Amount nAmount = AmountFromValue(outputs[name_]); + Amount nAmount = outputs[name_]; CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); @@ -218,8 +218,7 @@ txout.scriptPubKey = scriptPubKey; txout.nValue = MAX_MONEY; if (prevOut.exists("amount")) { - txout.nValue = - AmountFromValue(find_value(prevOut, "amount")); + txout.nValue = find_value(prevOut, "amount"); } else { // amount param is required in replay-protected txs. // Note that we must check for its presence here rather diff --git a/src/rpc/util.h b/src/rpc/util.h --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -79,7 +79,6 @@ extern std::vector ParseHexV(const UniValue &v, std::string strName); extern std::vector ParseHexO(const UniValue &o, std::string strKey); -extern Amount AmountFromValue(const UniValue &value); extern std::string HelpExampleCli(const std::string &methodname, const std::string &args); extern std::string HelpExampleRpc(const std::string &methodname, diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -78,24 +78,6 @@ } } -Amount AmountFromValue(const UniValue &value) { - if (!value.isNum() && !value.isStr()) { - throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string"); - } - - int64_t n; - if (!ParseFixedPoint(value.getValStr(), 8, &n)) { - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - } - - Amount amt = n * SATOSHI; - if (!MoneyRange(amt)) { - throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); - } - - return amt; -} - uint256 ParseHashV(const UniValue &v, std::string strName) { std::string strHex(v.get_str()); if (64 != strHex.length()) { diff --git a/src/test/fuzz/parse_univalue.cpp b/src/test/fuzz/parse_univalue.cpp --- a/src/test/fuzz/parse_univalue.cpp +++ b/src/test/fuzz/parse_univalue.cpp @@ -80,7 +80,7 @@ } catch (const std::runtime_error &) { } try { - (void)AmountFromValue(univalue); + [[maybe_unused]] Amount x = univalue; } catch (const UniValue &) { } catch (const std::runtime_error &) { } diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp --- a/src/test/rpc_tests.cpp +++ b/src/test/rpc_tests.cpp @@ -280,72 +280,59 @@ } BOOST_AUTO_TEST_CASE(rpc_parse_monetary_values) { - BOOST_CHECK_THROW(AmountFromValue(ValueFromString("-0.00000001")), - UniValue); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0")), Amount::zero()); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000000")), - Amount::zero()); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000001")), SATOSHI); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.17622195")), - 17622195 * SATOSHI); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.5")), - 50000000 * SATOSHI); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.50000000")), - 50000000 * SATOSHI); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.89898989")), - 89898989 * SATOSHI); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("1.00000000")), - 100000000 * SATOSHI); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("20999999.9999999")), + Amount x; + BOOST_CHECK_THROW(x = ValueFromString("-0.00000001"), UniValue); + BOOST_CHECK_EQUAL(x = ValueFromString("0"), Amount::zero()); + BOOST_CHECK_EQUAL(x = ValueFromString("0.00000000"), Amount::zero()); + BOOST_CHECK_EQUAL(x = ValueFromString("0.00000001"), SATOSHI); + BOOST_CHECK_EQUAL(x = ValueFromString("0.17622195"), 17622195 * SATOSHI); + BOOST_CHECK_EQUAL(x = ValueFromString("0.5"), 50000000 * SATOSHI); + BOOST_CHECK_EQUAL(x = ValueFromString("0.50000000"), 50000000 * SATOSHI); + BOOST_CHECK_EQUAL(x = ValueFromString("0.89898989"), 89898989 * SATOSHI); + BOOST_CHECK_EQUAL(x = ValueFromString("1.00000000"), 100000000 * SATOSHI); + BOOST_CHECK_EQUAL(x = ValueFromString("20999999.9999999"), int64_t(2099999999999990) * SATOSHI); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("20999999.99999999")), + BOOST_CHECK_EQUAL(x = ValueFromString("20999999.99999999"), int64_t(2099999999999999) * SATOSHI); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("1e-8")), - COIN / 100000000); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.1e-7")), - COIN / 100000000); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.01e-6")), - COIN / 100000000); - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString( + BOOST_CHECK_EQUAL(x = ValueFromString("1e-8"), COIN / 100000000); + BOOST_CHECK_EQUAL(x = ValueFromString("0.1e-7"), COIN / 100000000); + BOOST_CHECK_EQUAL(x = ValueFromString("0.01e-6"), COIN / 100000000); + BOOST_CHECK_EQUAL(x = ValueFromString( "0." "0000000000000000000000000000000000000000000000000000" - "000000000000000000000001e+68")), + "000000000000000000000001e+68"), COIN / 100000000); BOOST_CHECK_EQUAL( - AmountFromValue(ValueFromString("10000000000000000000000000000000000000" - "000000000000000000000000000e-64")), + x = ValueFromString("10000000000000000000000000000000000000" + "000000000000000000000000000e-64"), COIN); BOOST_CHECK_EQUAL( - AmountFromValue(ValueFromString( + x = ValueFromString( "0." "000000000000000000000000000000000000000000000000000000000000000100" - "000000000000000000000000000000000000000000000000000e64")), + "000000000000000000000000000000000000000000000000000e64"), COIN); // should fail - BOOST_CHECK_THROW(AmountFromValue(ValueFromString("1e-9")), UniValue); + BOOST_CHECK_THROW(x = ValueFromString("1e-9"), UniValue); // should fail - BOOST_CHECK_THROW(AmountFromValue(ValueFromString("0.000000019")), - UniValue); + BOOST_CHECK_THROW(x = ValueFromString("0.000000019"), UniValue); // should pass, cut trailing 0 - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.00000001000000")), - SATOSHI); + BOOST_CHECK_EQUAL(x = ValueFromString("0.00000001000000"), SATOSHI); // should fail - BOOST_CHECK_THROW(AmountFromValue(ValueFromString("19e-9")), UniValue); + BOOST_CHECK_THROW(x = ValueFromString("19e-9"), UniValue); // should pass, leading 0 is present - BOOST_CHECK_EQUAL(AmountFromValue(ValueFromString("0.19e-6")), - 19 * SATOSHI); + BOOST_CHECK_EQUAL(x = ValueFromString("0.19e-6"), 19 * SATOSHI); // overflow error - BOOST_CHECK_THROW(AmountFromValue(ValueFromString("92233720368.54775808")), - UniValue); + BOOST_CHECK_THROW(x = ValueFromString("92233720368.54775808"), UniValue); // overflow error - BOOST_CHECK_THROW(AmountFromValue(ValueFromString("1e+11")), UniValue); + BOOST_CHECK_THROW(x = ValueFromString("1e+11"), UniValue); // overflow error signless - BOOST_CHECK_THROW(AmountFromValue(ValueFromString("1e11")), UniValue); + BOOST_CHECK_THROW(x = ValueFromString("1e11"), UniValue); // overflow error - BOOST_CHECK_THROW(AmountFromValue(ValueFromString("93e+9")), UniValue); + BOOST_CHECK_THROW(x = ValueFromString("93e+9"), UniValue); } BOOST_AUTO_TEST_CASE(json_parse_errors) { @@ -356,10 +343,10 @@ BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0 ").get_real(), 1.0); // should fail, missing leading 0, therefore invalid JSON - BOOST_CHECK_THROW(AmountFromValue(ParseNonRFCJSONValue(".19e-6")), - std::runtime_error); - BOOST_CHECK_EQUAL(AmountFromValue(ParseNonRFCJSONValue( - "0.00000000000000000000000000000000000001e+30 ")), + Amount x; + BOOST_CHECK_THROW(x = ParseNonRFCJSONValue(".19e-6"), std::runtime_error); + BOOST_CHECK_EQUAL(x = ParseNonRFCJSONValue( + "0.00000000000000000000000000000000000001e+30 "), SATOSHI); // Invalid, initial garbage BOOST_CHECK_THROW(ParseNonRFCJSONValue("[1.0"), std::runtime_error); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -2412,7 +2412,7 @@ Amount nValue = Amount::zero(); unsigned int pos = 0; if (test.size() > 0 && test[pos].isArray()) { - nValue = AmountFromValue(test[pos][0]); + nValue = test[pos][0]; pos++; } diff --git a/src/util/moneystr.h b/src/util/moneystr.h --- a/src/util/moneystr.h +++ b/src/util/moneystr.h @@ -16,7 +16,8 @@ /** * Do not use these functions to represent or parse monetary amounts to or from - * JSON but use AmountFromValue and the Amount::operator UniValue() for that. + * JSON. Use Amount::operator UniValue() and Amount::Amount(UniValue T) for + * that. */ std::string FormatMoney(const Amount n); /** diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -466,7 +466,7 @@ } // Amount - Amount nAmount = AmountFromValue(request.params[1]); + Amount nAmount = request.params[1]; if (nAmount <= Amount::zero()) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); } @@ -1014,7 +1014,7 @@ destinations.insert(dest); CScript scriptPubKey = GetScriptForDestination(dest); - Amount nAmount = AmountFromValue(sendTo[name_]); + Amount nAmount = sendTo[name_]; if (nAmount <= Amount::zero()) { throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); } @@ -2809,7 +2809,7 @@ LOCK(pwallet->cs_wallet); - Amount nAmount = AmountFromValue(request.params[0]); + Amount nAmount = request.params[0]; CFeeRate tx_fee_rate(nAmount, 1000); CFeeRate max_tx_fee_rate(pwallet->m_default_max_tx_fee, 1000); if (tx_fee_rate == CFeeRate()) { @@ -3562,15 +3562,15 @@ const UniValue &options = request.params[4].get_obj(); if (options.exists("minimumAmount")) { - nMinimumAmount = AmountFromValue(options["minimumAmount"]); + nMinimumAmount = options["minimumAmount"]; } if (options.exists("maximumAmount")) { - nMaximumAmount = AmountFromValue(options["maximumAmount"]); + nMaximumAmount = options["maximumAmount"]; } if (options.exists("minimumSumAmount")) { - nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]); + nMinimumSumAmount = options["minimumSumAmount"]; } if (options.exists("maximumCount")) { @@ -3723,8 +3723,7 @@ } if (options.exists("feeRate")) { - coinControl.m_feerate = - CFeeRate(AmountFromValue(options["feeRate"])); + coinControl.m_feerate = CFeeRate(options["feeRate"]); coinControl.fOverrideFeeRate = true; }