Skip to content

Commit 0ee0474

Browse files
committed
Merge bitcoin#16521: rpc: Use the default maxfeerate value as BTC/kB
2dfd683 test: Add test for default maxfeerate in sendrawtransaction (Joonmo Yang) 261843e wallet/rpc: Use the default maxfeerate value as BTC/kB (Joonmo Yang) Pull request description: Fixes bitcoin#16382 This patch tries to treat `maxfeerate` in sendrawtransaction/testmempoolaccept RPC as a rate(BTC/kB) instead of an absolute value(BTC). The included test case checks if the new behavior works correctly, by using the transaction with an absolute fee of ~0.02BTC, where the fee rate is ~0.2BTC/kB. This test should be failing if the default `maxfeerate` is 0.1BTC, but pass if the default value is 0.1BTC/kB ACKs for top commit: laanwj: ACK 2dfd683 (ACKs by Sjors and MarcoFalke above for trivially different code) Tree-SHA512: a1795bffe8a182acef8844797955db1f60bb0c0ded97148f3572dc265234d5219271a3a7aa0b6418a43f73b2b2720ef7412ba169c99bb1cdcac52051f537d6af
2 parents dd8cf82 + 2dfd683 commit 0ee0474

File tree

5 files changed

+48
-26
lines changed

5 files changed

+48
-26
lines changed

src/rpc/rawtransaction.cpp

+17-20
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <node/coin.h>
1515
#include <node/psbt.h>
1616
#include <node/transaction.h>
17+
#include <policy/policy.h>
1718
#include <policy/rbf.h>
1819
#include <primitives/transaction.h>
1920
#include <psbt.h>
@@ -38,11 +39,11 @@
3839

3940
#include <univalue.h>
4041

41-
/** High fee for sendrawtransaction and testmempoolaccept.
42-
* By default, transaction with a fee higher than this will be rejected by the
43-
* RPCs. This can be overridden with the maxfeerate argument.
42+
/** High fee rate for sendrawtransaction and testmempoolaccept.
43+
* By default, transaction with a fee rate higher than this will be rejected by
44+
* the RPCs. This can be overridden with the maxfeerate argument.
4445
*/
45-
constexpr static CAmount DEFAULT_MAX_RAW_TX_FEE{COIN / 10};
46+
static const CFeeRate DEFAULT_MAX_RAW_TX_FEE_RATE{COIN / 10};
4647

4748
static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
4849
{
@@ -775,7 +776,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
775776
"\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
776777
{
777778
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
778-
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE),
779+
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()),
779780
"Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT +
780781
"/kB.\nSet to 0 to accept any fee rate.\n"},
781782
},
@@ -805,19 +806,17 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
805806
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
806807
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
807808

808-
CAmount max_raw_tx_fee = DEFAULT_MAX_RAW_TX_FEE;
809+
CFeeRate max_raw_tx_fee_rate = DEFAULT_MAX_RAW_TX_FEE_RATE;
809810
// TODO: temporary migration code for old clients. Remove in v0.20
810811
if (request.params[1].isBool()) {
811812
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.");
812813
} else if (!request.params[1].isNull()) {
813-
size_t weight = GetTransactionWeight(*tx);
814-
CFeeRate fr(AmountFromValue(request.params[1]));
815-
// the +3/4 part rounds the value up, and is the same formula used when
816-
// calculating the fee for a transaction
817-
// (see GetVirtualTransactionSize)
818-
max_raw_tx_fee = fr.GetFee((weight+3)/4);
814+
max_raw_tx_fee_rate = CFeeRate(AmountFromValue(request.params[1]));
819815
}
820816

817+
int64_t virtual_size = GetVirtualTransactionSize(*tx);
818+
CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
819+
821820
std::string err_string;
822821
AssertLockNotHeld(cs_main);
823822
const TransactionError err = BroadcastTransaction(tx, err_string, max_raw_tx_fee, /*relay*/ true, /*wait_callback*/ true);
@@ -841,7 +840,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
841840
{"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""},
842841
},
843842
},
844-
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
843+
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK()), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
845844
},
846845
RPCResult{
847846
"[ (array) The result of the mempool acceptance test for each raw transaction in the input array.\n"
@@ -881,19 +880,17 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request)
881880
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
882881
const uint256& tx_hash = tx->GetHash();
883882

884-
CAmount max_raw_tx_fee = DEFAULT_MAX_RAW_TX_FEE;
883+
CFeeRate max_raw_tx_fee_rate = DEFAULT_MAX_RAW_TX_FEE_RATE;
885884
// TODO: temporary migration code for old clients. Remove in v0.20
886885
if (request.params[1].isBool()) {
887886
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.");
888887
} else if (!request.params[1].isNull()) {
889-
size_t weight = GetTransactionWeight(*tx);
890-
CFeeRate fr(AmountFromValue(request.params[1]));
891-
// the +3/4 part rounds the value up, and is the same formula used when
892-
// calculating the fee for a transaction
893-
// (see GetVirtualTransactionSize)
894-
max_raw_tx_fee = fr.GetFee((weight+3)/4);
888+
max_raw_tx_fee_rate = CFeeRate(AmountFromValue(request.params[1]));
895889
}
896890

891+
int64_t virtual_size = GetVirtualTransactionSize(*tx);
892+
CAmount max_raw_tx_fee = max_raw_tx_fee_rate.GetFee(virtual_size);
893+
897894
UniValue result(UniValue::VARR);
898895
UniValue result_0(UniValue::VOBJ);
899896
result_0.pushKV("txid", tx_hash.GetHex());

test/functional/feature_segwit.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ def run_test(self):
257257
tx.vin.append(CTxIn(COutPoint(int(txid2, 16), 0), b""))
258258
tx.vout.append(CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee
259259
tx.calc_sha256()
260-
txid3 = self.nodes[0].sendrawtransaction(ToHex(tx))
260+
txid3 = self.nodes[0].sendrawtransaction(ToHex(tx), 0)
261261
assert tx.wit.is_null()
262262
assert txid3 in self.nodes[0].getrawmempool()
263263

test/functional/mempool_accept.py

+1
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ def run_test(self):
183183
self.check_mempool_result(
184184
result_expected=[{'txid': tx.rehash(), 'allowed': True}],
185185
rawtxs=[tx.serialize().hex()],
186+
maxfeerate=0,
186187
)
187188

188189
self.log.info('A transaction with no outputs')

test/functional/rpc_rawtransaction.py

+28-4
Original file line numberDiff line numberDiff line change
@@ -432,27 +432,51 @@ def run_test(self):
432432

433433
self.log.info('sendrawtransaction/testmempoolaccept with maxfeerate')
434434

435+
# Test a transaction with small fee
435436
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0)
436437
rawTx = self.nodes[0].getrawtransaction(txId, True)
437438
vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('1.00000000'))
438439

439440
self.sync_all()
440441
inputs = [{ "txid" : txId, "vout" : vout['n'] }]
441-
outputs = { self.nodes[0].getnewaddress() : Decimal("0.99999000") } # 1000 sat fee
442+
outputs = { self.nodes[0].getnewaddress() : Decimal("0.999990000") } # 10000 sat fee
442443
rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
443444
rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx)
444445
assert_equal(rawTxSigned['complete'], True)
445-
# 1000 sat fee, ~100 b transaction, fee rate should land around 10 sat/b = 0.00010000 BTC/kB
446+
# 10000 sat fee, ~100 b transaction, fee rate should land around 100 sat/b = 0.00100000 BTC/kB
446447
# Thus, testmempoolaccept should reject
447448
testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']], 0.00001000)[0]
448449
assert_equal(testres['allowed'], False)
449450
assert_equal(testres['reject-reason'], '256: absurdly-high-fee')
450451
# and sendrawtransaction should throw
451452
assert_raises_rpc_error(-26, "absurdly-high-fee", self.nodes[2].sendrawtransaction, rawTxSigned['hex'], 0.00001000)
452453
# And below calls should both succeed
453-
testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']], maxfeerate='0.00070000')[0]
454+
testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']])[0]
454455
assert_equal(testres['allowed'], True)
455-
self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate='0.00070000')
456+
self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'])
457+
458+
# Test a transaction with large fee
459+
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0)
460+
rawTx = self.nodes[0].getrawtransaction(txId, True)
461+
vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('1.00000000'))
462+
463+
self.sync_all()
464+
inputs = [{ "txid" : txId, "vout" : vout['n'] }]
465+
outputs = { self.nodes[0].getnewaddress() : Decimal("0.98000000") } # 2000000 sat fee
466+
rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
467+
rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx)
468+
assert_equal(rawTxSigned['complete'], True)
469+
# 2000000 sat fee, ~100 b transaction, fee rate should land around 20000 sat/b = 0.20000000 BTC/kB
470+
# Thus, testmempoolaccept should reject
471+
testres = self.nodes[2].testmempoolaccept([rawTxSigned['hex']])[0]
472+
assert_equal(testres['allowed'], False)
473+
assert_equal(testres['reject-reason'], '256: absurdly-high-fee')
474+
# and sendrawtransaction should throw
475+
assert_raises_rpc_error(-26, "absurdly-high-fee", self.nodes[2].sendrawtransaction, rawTxSigned['hex'])
476+
# And below calls should both succeed
477+
testres = self.nodes[2].testmempoolaccept(rawtxs=[rawTxSigned['hex']], maxfeerate='0.20000000')[0]
478+
assert_equal(testres['allowed'], True)
479+
self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate='0.20000000')
456480

457481

458482
if __name__ == '__main__':

test/functional/wallet_basic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ def run_test(self):
433433
# Split into two chains
434434
rawtx = self.nodes[0].createrawtransaction([{"txid": singletxid, "vout": 0}], {chain_addrs[0]: node0_balance / 2 - Decimal('0.01'), chain_addrs[1]: node0_balance / 2 - Decimal('0.01')})
435435
signedtx = self.nodes[0].signrawtransactionwithwallet(rawtx)
436-
singletxid = self.nodes[0].sendrawtransaction(signedtx["hex"])
436+
singletxid = self.nodes[0].sendrawtransaction(signedtx["hex"], 0)
437437
self.nodes[0].generate(1)
438438

439439
# Make a long chain of unconfirmed payments without hitting mempool limit

0 commit comments

Comments
 (0)