mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-11 14:38:29 +01:00
Merge #13541: wallet/rpc: sendrawtransaction maxfeerate
7abd2e697cwallet/rpc: add maxfeerate parameter to testmempoolaccept (Karl-Johan Alm)6c0a6f73e3wallet/rpc: add maxfeerate parameter to sendrawtransaction (Karl-Johan Alm)e5efacb941test: Refactor vout fetches in rpc_rawtransaction (Karl-Johan Alm) Pull request description: This adds a new `maxfeerate` parameter to `sendrawtransaction` which forces the node to reject a transaction whose feerate is above the given fee rate. This is a safety harness from shooting yourself in the foot and accidentally overpaying fees. See also #12911. Tree-SHA512: efa50134a7c17c9330cfdfd48ba400e095c0a419cc45e630618d8b44929c25d780d1bb2710c1fbbb6e687eca373505b0338cdaa7f2ff4ca22636d84c31557a2e
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
#include <script/standard.h>
|
||||
#include <uint256.h>
|
||||
#include <util/bip32.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
@@ -1039,7 +1040,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
|
||||
"\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
|
||||
{
|
||||
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
|
||||
{"allowhighfees", RPCArg::Type::BOOL, /* default */ "false", "Allow high fees"},
|
||||
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(maxTxFee), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
|
||||
},
|
||||
RPCResult{
|
||||
"\"hex\" (string) The transaction hash in hex\n"
|
||||
@@ -1056,7 +1057,10 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
|
||||
},
|
||||
}.ToString());
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VSTR,
|
||||
UniValueType(), // NUM or BOOL, checked later
|
||||
});
|
||||
|
||||
// parse hex string from parameter
|
||||
CMutableTransaction mtx;
|
||||
@@ -1064,12 +1068,24 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
|
||||
|
||||
bool allowhighfees = false;
|
||||
if (!request.params[1].isNull()) allowhighfees = request.params[1].get_bool();
|
||||
const CAmount highfee{allowhighfees ? 0 : ::maxTxFee};
|
||||
CAmount max_raw_tx_fee = maxTxFee;
|
||||
// TODO: temporary migration code for old clients. Remove in v0.20
|
||||
if (request.params[1].isBool()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0.");
|
||||
} else if (request.params[1].isNum()) {
|
||||
size_t weight = GetTransactionWeight(*tx);
|
||||
CFeeRate fr(AmountFromValue(request.params[1]));
|
||||
// the +3/4 part rounds the value up, and is the same formula used when
|
||||
// calculating the fee for a transaction
|
||||
// (see GetVirtualTransactionSize)
|
||||
max_raw_tx_fee = fr.GetFee((weight+3)/4);
|
||||
} else if (!request.params[1].isNull()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric");
|
||||
}
|
||||
|
||||
uint256 txid;
|
||||
std::string err_string;
|
||||
const TransactionError err = BroadcastTransaction(tx, txid, err_string, highfee);
|
||||
const TransactionError err = BroadcastTransaction(tx, txid, err_string, max_raw_tx_fee);
|
||||
if (TransactionError::OK != err) {
|
||||
throw JSONRPCTransactionError(err, err_string);
|
||||
}
|
||||
@@ -1092,7 +1108,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
|
||||
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
|
||||
},
|
||||
},
|
||||
{"allowhighfees", RPCArg::Type::BOOL, /* default */ "false", "Allow high fees"},
|
||||
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(maxTxFee), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
|
||||
},
|
||||
RPCResult{
|
||||
"[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n"
|
||||
@@ -1117,7 +1133,11 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
|
||||
}.ToString());
|
||||
}
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VBOOL});
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VARR,
|
||||
UniValueType(), // NUM or BOOL, checked later
|
||||
});
|
||||
|
||||
if (request.params[0].get_array().size() != 1) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Array must contain exactly one raw transaction for now");
|
||||
}
|
||||
@@ -1129,9 +1149,19 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
|
||||
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
|
||||
const uint256& tx_hash = tx->GetHash();
|
||||
|
||||
CAmount max_raw_tx_fee = ::maxTxFee;
|
||||
if (!request.params[1].isNull() && request.params[1].get_bool()) {
|
||||
max_raw_tx_fee = 0;
|
||||
CAmount max_raw_tx_fee = maxTxFee;
|
||||
// TODO: temporary migration code for old clients. Remove in v0.20
|
||||
if (request.params[1].isBool()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0.");
|
||||
} else if (request.params[1].isNum()) {
|
||||
size_t weight = GetTransactionWeight(*tx);
|
||||
CFeeRate fr(AmountFromValue(request.params[1]));
|
||||
// the +3/4 part rounds the value up, and is the same formula used when
|
||||
// calculating the fee for a transaction
|
||||
// (see GetVirtualTransactionSize)
|
||||
max_raw_tx_fee = fr.GetFee((weight+3)/4);
|
||||
} else if (!request.params[1].isNull()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric");
|
||||
}
|
||||
|
||||
UniValue result(UniValue::VARR);
|
||||
@@ -2048,11 +2078,11 @@ static const CRPCCommand commands[] =
|
||||
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
|
||||
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
|
||||
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
|
||||
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
|
||||
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees|maxfeerate"} },
|
||||
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
|
||||
{ "hidden", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} },
|
||||
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
|
||||
{ "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees"} },
|
||||
{ "rawtransactions", "testmempoolaccept", &testmempoolaccept, {"rawtxs","allowhighfees|maxfeerate"} },
|
||||
{ "rawtransactions", "decodepsbt", &decodepsbt, {"psbt"} },
|
||||
{ "rawtransactions", "combinepsbt", &combinepsbt, {"txs"} },
|
||||
{ "rawtransactions", "finalizepsbt", &finalizepsbt, {"psbt", "extract"} },
|
||||
|
||||
Reference in New Issue
Block a user