aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMarcoFalke <falke.marco@gmail.com>2019-03-18 13:22:06 -0400
committerMarcoFalke <falke.marco@gmail.com>2019-03-18 13:22:15 -0400
commitc033c4b5cef89a654e4d9d5c5f9bd823871b068b (patch)
tree6010a8bf7db2b77c656dcc5e12a9b390cbebf9a0 /src
parent2c336a9ccdf4434a0c80b83ea348bdfff9cea62b (diff)
parent7abd2e697c0f8e93245e09ac853bae05d0b48bee (diff)
downloadbitcoin-c033c4b5cef89a654e4d9d5c5f9bd823871b068b.tar.xz
Merge #13541: wallet/rpc: sendrawtransaction maxfeerate
7abd2e697c wallet/rpc: add maxfeerate parameter to testmempoolaccept (Karl-Johan Alm) 6c0a6f73e3 wallet/rpc: add maxfeerate parameter to sendrawtransaction (Karl-Johan Alm) e5efacb941 test: 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
Diffstat (limited to 'src')
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/rawtransaction.cpp56
-rw-r--r--src/validation.cpp20
3 files changed, 65 insertions, 13 deletions
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index a266580b3d..4144a17bc3 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -92,8 +92,10 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "signrawtransactionwithkey", 2, "prevtxs" },
{ "signrawtransactionwithwallet", 1, "prevtxs" },
{ "sendrawtransaction", 1, "allowhighfees" },
+ { "sendrawtransaction", 1, "maxfeerate" },
{ "testmempoolaccept", 0, "rawtxs" },
{ "testmempoolaccept", 1, "allowhighfees" },
+ { "testmempoolaccept", 1, "maxfeerate" },
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" },
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 71acdbfb99..1d58725a58 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -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"} },
diff --git a/src/validation.cpp b/src/validation.cpp
index a9b5bcf4a4..97f51d2968 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -3159,6 +3159,26 @@ static int GetWitnessCommitmentIndex(const CBlock& block)
return commitpos;
}
+// Compute at which vout of the block's coinbase transaction the signet
+// signature occurs, or -1 if not found.
+static int GetSignetSignatureIndex(const CBlock& block)
+{
+ if (!block.vtx.empty()) {
+ for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
+ if (block.vtx[0]->vout[o].scriptPubKey.size() >= 68 // at minimum 64 byte signature plus script/header data
+ && block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN // unspendable
+ && block.vtx[0]->vout[o].scriptPubKey[1] == block.vtx[0]->vout[o].scriptPubKey.size() - 1 // push the rest
+ && block.vtx[0]->vout[o].scriptPubKey[2] == 0xec // 's' ^ 0x9f
+ && block.vtx[0]->vout[o].scriptPubKey[3] == 0xc7 // 'i' ^ 0xae
+ && block.vtx[0]->vout[o].scriptPubKey[4] == 0xda // 'g' ^ 0xbd
+ && block.vtx[0]->vout[o].scriptPubKey[5] == 0xa2) { // 'n' ^ 0xcc
+ return (int)o;
+ }
+ }
+ }
+ return -1;
+}
+
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams)
{
int commitpos = GetWitnessCommitmentIndex(block);