Skip to content

Commit 6c0a6f7

Browse files
committed
wallet/rpc: add maxfeerate parameter to sendrawtransaction
1 parent e5efacb commit 6c0a6f7

13 files changed

+126
-70
lines changed

src/rpc/client.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
9292
{ "signrawtransactionwithkey", 2, "prevtxs" },
9393
{ "signrawtransactionwithwallet", 1, "prevtxs" },
9494
{ "sendrawtransaction", 1, "allowhighfees" },
95+
{ "sendrawtransaction", 1, "maxfeerate" },
9596
{ "testmempoolaccept", 0, "rawtxs" },
9697
{ "testmempoolaccept", 1, "allowhighfees" },
9798
{ "combinerawtransaction", 0, "txs" },

src/rpc/rawtransaction.cpp

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <script/standard.h>
2929
#include <uint256.h>
3030
#include <util/bip32.h>
31+
#include <util/moneystr.h>
3132
#include <util/strencodings.h>
3233
#include <validation.h>
3334
#include <validationinterface.h>
@@ -1039,7 +1040,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
10391040
"\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
10401041
{
10411042
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
1042-
{"allowhighfees", RPCArg::Type::BOOL, /* default */ "false", "Allow high fees"},
1043+
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(maxTxFee), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
10431044
},
10441045
RPCResult{
10451046
"\"hex\" (string) The transaction hash in hex\n"
@@ -1056,20 +1057,35 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
10561057
},
10571058
}.ToString());
10581059

1059-
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
1060+
RPCTypeCheck(request.params, {
1061+
UniValue::VSTR,
1062+
UniValueType(), // NUM or BOOL, checked later
1063+
});
10601064

10611065
// parse hex string from parameter
10621066
CMutableTransaction mtx;
10631067
if (!DecodeHexTx(mtx, request.params[0].get_str()))
10641068
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
10651069
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
10661070

1067-
bool allowhighfees = false;
1068-
if (!request.params[1].isNull()) allowhighfees = request.params[1].get_bool();
1069-
const CAmount highfee{allowhighfees ? 0 : ::maxTxFee};
1071+
CAmount max_raw_tx_fee = maxTxFee;
1072+
// TODO: temporary migration code for old clients. Remove in v0.20
1073+
if (request.params[1].isBool()) {
1074+
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.");
1075+
} else if (request.params[1].isNum()) {
1076+
size_t weight = GetTransactionWeight(*tx);
1077+
CFeeRate fr(AmountFromValue(request.params[1]));
1078+
// the +3/4 part rounds the value up, and is the same formula used when
1079+
// calculating the fee for a transaction
1080+
// (see GetVirtualTransactionSize)
1081+
max_raw_tx_fee = fr.GetFee((weight+3)/4);
1082+
} else if (!request.params[1].isNull()) {
1083+
throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric");
1084+
}
1085+
10701086
uint256 txid;
10711087
std::string err_string;
1072-
const TransactionError err = BroadcastTransaction(tx, txid, err_string, highfee);
1088+
const TransactionError err = BroadcastTransaction(tx, txid, err_string, max_raw_tx_fee);
10731089
if (TransactionError::OK != err) {
10741090
throw JSONRPCTransactionError(err, err_string);
10751091
}
@@ -2048,7 +2064,7 @@ static const CRPCCommand commands[] =
20482064
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
20492065
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
20502066
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
2051-
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
2067+
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees|maxfeerate"} },
20522068
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
20532069
{ "hidden", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} },
20542070
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },

src/validation.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3130,6 +3130,26 @@ static int GetWitnessCommitmentIndex(const CBlock& block)
31303130
return commitpos;
31313131
}
31323132

3133+
// Compute at which vout of the block's coinbase transaction the signet
3134+
// signature occurs, or -1 if not found.
3135+
static int GetSignetSignatureIndex(const CBlock& block)
3136+
{
3137+
if (!block.vtx.empty()) {
3138+
for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
3139+
if (block.vtx[0]->vout[o].scriptPubKey.size() >= 68 // at minimum 64 byte signature plus script/header data
3140+
&& block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN // unspendable
3141+
&& block.vtx[0]->vout[o].scriptPubKey[1] == block.vtx[0]->vout[o].scriptPubKey.size() - 1 // push the rest
3142+
&& block.vtx[0]->vout[o].scriptPubKey[2] == 0xec // 's' ^ 0x9f
3143+
&& block.vtx[0]->vout[o].scriptPubKey[3] == 0xc7 // 'i' ^ 0xae
3144+
&& block.vtx[0]->vout[o].scriptPubKey[4] == 0xda // 'g' ^ 0xbd
3145+
&& block.vtx[0]->vout[o].scriptPubKey[5] == 0xa2) { // 'n' ^ 0xcc
3146+
return (int)o;
3147+
}
3148+
}
3149+
}
3150+
return -1;
3151+
}
3152+
31333153
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams)
31343154
{
31353155
int commitpos = GetWitnessCommitmentIndex(block);

test/functional/feature_fee_estimation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee
6565
# the ScriptSig that will satisfy the ScriptPubKey.
6666
for inp in tx.vin:
6767
inp.scriptSig = SCRIPT_SIG[inp.prevout.n]
68-
txid = from_node.sendrawtransaction(ToHex(tx), True)
68+
txid = from_node.sendrawtransaction(hexstring=ToHex(tx), maxfeerate=0)
6969
unconflist.append({"txid": txid, "vout": 0, "amount": total_in - amount - fee})
7070
unconflist.append({"txid": txid, "vout": 1, "amount": amount})
7171

@@ -95,7 +95,7 @@ def split_inputs(from_node, txins, txouts, initial_split=False):
9595
else:
9696
tx.vin[0].scriptSig = SCRIPT_SIG[prevtxout["vout"]]
9797
completetx = ToHex(tx)
98-
txid = from_node.sendrawtransaction(completetx, True)
98+
txid = from_node.sendrawtransaction(hexstring=completetx, maxfeerate=0)
9999
txouts.append({"txid": txid, "vout": 0, "amount": half_change})
100100
txouts.append({"txid": txid, "vout": 1, "amount": rem_change})
101101

test/functional/feature_nulldummy.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,17 +64,17 @@ def run_test(self):
6464

6565
self.log.info("Test 1: NULLDUMMY compliant base transactions should be accepted to mempool and mined before activation [430]")
6666
test1txs = [create_transaction(self.nodes[0], coinbase_txid[0], self.ms_address, amount=49)]
67-
txid1 = self.nodes[0].sendrawtransaction(test1txs[0].serialize_with_witness().hex(), True)
67+
txid1 = self.nodes[0].sendrawtransaction(test1txs[0].serialize_with_witness().hex(), 0)
6868
test1txs.append(create_transaction(self.nodes[0], txid1, self.ms_address, amount=48))
69-
txid2 = self.nodes[0].sendrawtransaction(test1txs[1].serialize_with_witness().hex(), True)
69+
txid2 = self.nodes[0].sendrawtransaction(test1txs[1].serialize_with_witness().hex(), 0)
7070
test1txs.append(create_transaction(self.nodes[0], coinbase_txid[1], self.wit_ms_address, amount=49))
71-
txid3 = self.nodes[0].sendrawtransaction(test1txs[2].serialize_with_witness().hex(), True)
71+
txid3 = self.nodes[0].sendrawtransaction(test1txs[2].serialize_with_witness().hex(), 0)
7272
self.block_submit(self.nodes[0], test1txs, False, True)
7373

7474
self.log.info("Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation")
7575
test2tx = create_transaction(self.nodes[0], txid2, self.ms_address, amount=47)
7676
trueDummy(test2tx)
77-
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test2tx.serialize_with_witness().hex(), True)
77+
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test2tx.serialize_with_witness().hex(), 0)
7878

7979
self.log.info("Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]")
8080
self.block_submit(self.nodes[0], [test2tx], False, True)
@@ -83,19 +83,19 @@ def run_test(self):
8383
test4tx = create_transaction(self.nodes[0], test2tx.hash, self.address, amount=46)
8484
test6txs = [CTransaction(test4tx)]
8585
trueDummy(test4tx)
86-
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test4tx.serialize_with_witness().hex(), True)
86+
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test4tx.serialize_with_witness().hex(), 0)
8787
self.block_submit(self.nodes[0], [test4tx])
8888

8989
self.log.info("Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation")
9090
test5tx = create_transaction(self.nodes[0], txid3, self.wit_address, amount=48)
9191
test6txs.append(CTransaction(test5tx))
9292
test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01'
93-
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test5tx.serialize_with_witness().hex(), True)
93+
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test5tx.serialize_with_witness().hex(), 0)
9494
self.block_submit(self.nodes[0], [test5tx], True)
9595

9696
self.log.info("Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [432]")
9797
for i in test6txs:
98-
self.nodes[0].sendrawtransaction(i.serialize_with_witness().hex(), True)
98+
self.nodes[0].sendrawtransaction(i.serialize_with_witness().hex(), 0)
9999
self.block_submit(self.nodes[0], test6txs, True, True)
100100

101101
def block_submit(self, node, txs, witness=False, accept=False):

0 commit comments

Comments
 (0)