Skip to content

Commit 3ac7b0c

Browse files
committed
wallet: fundrawtx fee rate coverage, fixup ParseConfirmTarget()
1 parent 2d8eba8 commit 3ac7b0c

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

src/rpc/util.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,11 +272,12 @@ UniValue DescribeAddress(const CTxDestination& dest)
272272

273273
unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
274274
{
275-
int target = value.get_int();
276-
if (target < 1 || (unsigned int)target > max_target) {
277-
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u - %u", 1, max_target));
275+
const int target{value.get_int()};
276+
const unsigned int unsigned_target{static_cast<unsigned int>(target)};
277+
if (target < 1 || unsigned_target > max_target) {
278+
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u and %u", 1, max_target));
278279
}
279-
return (unsigned int)target;
280+
return unsigned_target;
280281
}
281282

282283
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr)

test/functional/rpc_fundrawtransaction.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from decimal import Decimal
88
from test_framework.test_framework import BitcoinTestFramework
99
from test_framework.util import (
10+
assert_approx,
1011
assert_equal,
1112
assert_fee_amount,
1213
assert_greater_than,
@@ -89,6 +90,7 @@ def run_test(self):
8990
self.test_op_return()
9091
self.test_watchonly()
9192
self.test_all_watched_funds()
93+
self.test_feerate_with_conf_target_and_estimate_mode()
9294
self.test_option_feerate()
9395
self.test_address_reuse()
9496
self.test_option_subtract_fee_from_outputs()
@@ -672,6 +674,59 @@ def test_option_feerate(self):
672674
assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
673675
assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
674676

677+
def test_feerate_with_conf_target_and_estimate_mode(self):
678+
self.log.info("Test fundrawtxn passing an explicit fee rate using conf_target and estimate_mode")
679+
node = self.nodes[3]
680+
# Make sure there is exactly one input so coin selection can't skew the result.
681+
assert_equal(len(node.listunspent(1)), 1)
682+
inputs = []
683+
outputs = {node.getnewaddress() : 1}
684+
rawtx = node.createrawtransaction(inputs, outputs)
685+
686+
for unit, fee_rate in {"btc/kb": 0.1, "sat/b": 10000}.items():
687+
self.log.info("Test fundrawtxn with conf_target {} estimate_mode {} produces expected fee".format(fee_rate, unit))
688+
# With no arguments passed, expect fee of 141 sats/b.
689+
assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00000141, vspan=0.00000001)
690+
# Expect fee to be 10,000x higher when explicit fee 10,000x greater is specified.
691+
result = node.fundrawtransaction(rawtx, {"conf_target": fee_rate, "estimate_mode": unit})
692+
assert_approx(result["fee"], vexp=0.0141, vspan=0.0001)
693+
694+
for field, fee_rate in {"conf_target": 0.1, "estimate_mode": "sat/b"}.items():
695+
self.log.info("Test fundrawtxn raises RPC error if both feeRate and {} are passed".format(field))
696+
assert_raises_rpc_error(
697+
-8, "Cannot specify both {} and feeRate".format(field),
698+
lambda: node.fundrawtransaction(rawtx, {"feeRate": 0.1, field: fee_rate}))
699+
700+
self.log.info("Test fundrawtxn with invalid estimate_mode settings")
701+
for k, v in {"number": 42, "object": {"foo": "bar"}}.items():
702+
assert_raises_rpc_error(-3, "Expected type string for estimate_mode, got {}".format(k),
703+
lambda: self.nodes[1].fundrawtransaction(rawtx, {"estimate_mode": v, "conf_target": 0.1}))
704+
for mode in ["foo", Decimal("3.141592")]:
705+
assert_raises_rpc_error(-8, "Invalid estimate_mode parameter",
706+
lambda: self.nodes[1].fundrawtransaction(rawtx, {"estimate_mode": mode, "conf_target": 0.1}))
707+
708+
self.log.info("Test fundrawtxn with invalid conf_target settings")
709+
for mode in ["unset", "economical", "conservative", "btc/kb", "sat/b"]:
710+
self.log.debug("{}".format(mode))
711+
for k, v in {"string": "", "object": {"foo": "bar"}}.items():
712+
assert_raises_rpc_error(-3, "Expected type number for conf_target, got {}".format(k),
713+
lambda: self.nodes[1].fundrawtransaction(rawtx, {"estimate_mode": mode, "conf_target": v}))
714+
if mode in ["btc/kb", "sat/b"]:
715+
assert_raises_rpc_error(-3, "Amount out of range",
716+
lambda: self.nodes[1].fundrawtransaction(rawtx, {"estimate_mode": mode, "conf_target": -1}))
717+
assert_raises_rpc_error(-4, "Fee rate (0.00000000 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)",
718+
lambda: self.nodes[1].fundrawtransaction(rawtx, {"estimate_mode": mode, "conf_target": 0}))
719+
else:
720+
for n in [-1, 0, 1009]:
721+
assert_raises_rpc_error(-8, "Invalid conf_target, must be between 1 and 1008",
722+
lambda: self.nodes[1].fundrawtransaction(rawtx, {"estimate_mode": mode, "conf_target": n}))
723+
724+
for unit, fee_rate in {"sat/B": 0.99999999, "BTC/kB": 0.00000999}.items():
725+
self.log.info("- raises RPC error 'fee rate too low' if conf_target {} and estimate_mode {} are passed".format(fee_rate, unit))
726+
assert_raises_rpc_error(-4, "Fee rate (0.00000999 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)",
727+
lambda: self.nodes[1].fundrawtransaction(rawtx, {"estimate_mode": unit, "conf_target": fee_rate, "add_inputs": True}))
728+
729+
675730
def test_address_reuse(self):
676731
"""Test no address reuse occurs."""
677732
self.log.info("Test fundrawtxn does not reuse addresses")

0 commit comments

Comments
 (0)