aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Atack <jon@atack.com>2020-11-19 18:38:00 +0100
committerJon Atack <jon@atack.com>2020-11-26 19:31:26 +0100
commit54e1edcc2bca76f783170768e65bf0850b036b81 (patch)
treea2d17d01f56aeb73309c912c181db4f3359e3166
parent9facca3ce0ad3c325676f9c9f0e6ed4429f0c335 (diff)
downloadbitcoin-54e1edcc2bca76f783170768e65bf0850b036b81.tar.xz
Allow zero-fee fundrawtxn and walletcreatefundedpsbt calls
A check to raise an error on zero-fee txns was mistakenly extended in commit a0d4957 from the bumpfee and send{toaddress, many} RPCs to also include fundrawtransaction and walletcreatefundedpsbt. This commit overrides zero fee rate checking for these two RPCs, not only for the feeRate (BTC/kvB) arg to return to previous behavior, but also for the new fee_rate (sat/vB) arg. Github-Pull: #20426 Rebased-From: 1b3d7009280595108eb22ac1188bc43678
-rw-r--r--src/wallet/rpcwallet.cpp16
-rwxr-xr-xtest/functional/rpc_fundrawtransaction.py16
-rwxr-xr-xtest/functional/rpc_psbt.py15
-rwxr-xr-xtest/functional/wallet_send.py8
4 files changed, 27 insertions, 28 deletions
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 5cbdd1021a..428531c3da 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -217,14 +217,8 @@ static void SetFeeEstimateMode(const CWallet& wallet, CCoinControl& cc, const Un
if (!estimate_mode.isNull() && estimate_mode.get_str() != "unset") {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and fee_rate");
}
- CFeeRate fee_rate_in_sat_vb{CFeeRate(AmountFromValue(fee_rate), COIN)};
- if (override_min_fee) {
- if (fee_rate_in_sat_vb <= CFeeRate(0)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid fee_rate %s (must be greater than 0)", fee_rate_in_sat_vb.ToString(FeeEstimateMode::SAT_VB)));
- }
- cc.fOverrideFeeRate = true;
- }
- cc.m_feerate = fee_rate_in_sat_vb;
+ cc.m_feerate = CFeeRate(AmountFromValue(fee_rate), COIN);
+ if (override_min_fee) cc.fOverrideFeeRate = true;
// Default RBF to true for explicit fee_rate, if unset.
if (cc.m_signal_bip125_rbf == nullopt) cc.m_signal_bip125_rbf = true;
return;
@@ -3148,11 +3142,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("estimate_mode")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
}
- CFeeRate fee_rate(AmountFromValue(options["feeRate"]));
- if (fee_rate <= CFeeRate(0)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid feeRate %s (must be greater than 0)", fee_rate.ToString(FeeEstimateMode::BTC_KVB)));
- }
- coinControl.m_feerate = fee_rate;
+ coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
coinControl.fOverrideFeeRate = true;
}
diff --git a/test/functional/rpc_fundrawtransaction.py b/test/functional/rpc_fundrawtransaction.py
index f987867e61..8ee0ecab0a 100755
--- a/test/functional/rpc_fundrawtransaction.py
+++ b/test/functional/rpc_fundrawtransaction.py
@@ -721,11 +721,17 @@ class RawTransactionsTest(BitcoinTestFramework):
result2 = node.fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee})
result3 = node.fundrawtransaction(rawtx, {"fee_rate": 10 * btc_kvb_to_sat_vb * self.min_relay_tx_fee})
result4 = node.fundrawtransaction(rawtx, {"feeRate": 10 * self.min_relay_tx_fee})
+ # Test that funding non-standard "zero-fee" transactions is valid.
+ result5 = self.nodes[3].fundrawtransaction(rawtx, {"fee_rate": 0})
+ result6 = self.nodes[3].fundrawtransaction(rawtx, {"feeRate": 0})
+
result_fee_rate = result['fee'] * 1000 / count_bytes(result['hex'])
assert_fee_amount(result1['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
assert_fee_amount(result2['fee'], count_bytes(result2['hex']), 2 * result_fee_rate)
assert_fee_amount(result3['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
assert_fee_amount(result4['fee'], count_bytes(result3['hex']), 10 * result_fee_rate)
+ assert_fee_amount(result5['fee'], count_bytes(result5['hex']), 0)
+ assert_fee_amount(result6['fee'], count_bytes(result6['hex']), 0)
# With no arguments passed, expect fee of 141 satoshis.
assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00000141, vspan=0.00000001)
@@ -752,19 +758,15 @@ class RawTransactionsTest(BitcoinTestFramework):
node.fundrawtransaction, rawtx, {"estimate_mode": mode, "conf_target": n, "add_inputs": True})
self.log.info("Test invalid fee rate settings")
- assert_raises_rpc_error(-8, "Invalid fee_rate 0.000 sat/vB (must be greater than 0)",
- node.fundrawtransaction, rawtx, {"fee_rate": 0, "add_inputs": True})
- assert_raises_rpc_error(-8, "Invalid feeRate 0.00000000 BTC/kvB (must be greater than 0)",
- node.fundrawtransaction, rawtx, {"feeRate": 0, "add_inputs": True})
for param, value in {("fee_rate", 100000), ("feeRate", 1.000)}:
assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
node.fundrawtransaction, rawtx, {param: value, "add_inputs": True})
assert_raises_rpc_error(-3, "Amount out of range",
- node.fundrawtransaction, rawtx, {"fee_rate": -1, "add_inputs": True})
+ node.fundrawtransaction, rawtx, {param: -1, "add_inputs": True})
assert_raises_rpc_error(-3, "Amount is not a number or string",
- node.fundrawtransaction, rawtx, {"fee_rate": {"foo": "bar"}, "add_inputs": True})
+ node.fundrawtransaction, rawtx, {param: {"foo": "bar"}, "add_inputs": True})
assert_raises_rpc_error(-3, "Invalid amount",
- node.fundrawtransaction, rawtx, {"fee_rate": "", "add_inputs": True})
+ node.fundrawtransaction, rawtx, {param: "", "add_inputs": True})
self.log.info("Test min fee rate checks are bypassed with fundrawtxn, e.g. a fee_rate under 1 sat/vB is allowed")
node.fundrawtransaction(rawtx, {"fee_rate": 0.99999999, "add_inputs": True})
diff --git a/test/functional/rpc_psbt.py b/test/functional/rpc_psbt.py
index 498197b5e5..5840801b00 100755
--- a/test/functional/rpc_psbt.py
+++ b/test/functional/rpc_psbt.py
@@ -192,26 +192,27 @@ class PSBTTest(BitcoinTestFramework):
assert_approx(res1["fee"], 0.055, 0.005)
res2 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": 0.1, "add_inputs": True})
assert_approx(res2["fee"], 0.055, 0.005)
+
self.log.info("Test min fee rate checks with walletcreatefundedpsbt are bypassed, e.g. a fee_rate under 1 sat/vB is allowed")
res3 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": 0.99999999, "add_inputs": True})
assert_approx(res3["fee"], 0.00000381, 0.0000001)
res4 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": 0.00000999, "add_inputs": True})
assert_approx(res4["fee"], 0.00000381, 0.0000001)
+ self.log.info("Test min fee rate checks with walletcreatefundedpsbt are bypassed and that funding non-standard 'zero-fee' transactions is valid")
+ for param in ["fee_rate", "feeRate"]:
+ assert_equal(self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {param: 0, "add_inputs": True})["fee"], 0)
+
self.log.info("Test invalid fee rate settings")
- assert_raises_rpc_error(-8, "Invalid fee_rate 0.000 sat/vB (must be greater than 0)",
- self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": 0, "add_inputs": True})
- assert_raises_rpc_error(-8, "Invalid feeRate 0.00000000 BTC/kvB (must be greater than 0)",
- self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"feeRate": 0, "add_inputs": True})
for param, value in {("fee_rate", 100000), ("feeRate", 1)}:
assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: value, "add_inputs": True})
assert_raises_rpc_error(-3, "Amount out of range",
- self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": -1, "add_inputs": True})
+ self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: -1, "add_inputs": True})
assert_raises_rpc_error(-3, "Amount is not a number or string",
- self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": {"foo": "bar"}, "add_inputs": True})
+ self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: {"foo": "bar"}, "add_inputs": True})
assert_raises_rpc_error(-3, "Invalid amount",
- self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": "", "add_inputs": True})
+ self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: "", "add_inputs": True})
self.log.info("- raises RPC error if both feeRate and fee_rate are passed")
assert_raises_rpc_error(-8, "Cannot specify both fee_rate (sat/vB) and feeRate (BTC/kvB)",
diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py
index 376cfdec6c..192e9065e6 100755
--- a/test/functional/wallet_send.py
+++ b/test/functional/wallet_send.py
@@ -302,10 +302,16 @@ class WalletSendTest(BitcoinTestFramework):
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=v, estimate_mode=mode,
expect_error=(-3, "Expected type number for conf_target, got {}".format(k)))
- # Test setting explicit fee rate just below the minimum.
+ # Test setting explicit fee rate just below the minimum and at zero.
self.log.info("Explicit fee rate raises RPC error 'fee rate too low' if fee_rate of 0.99999999 is passed")
self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=0.99999999,
expect_error=(-4, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=0.99999999,
+ expect_error=(-4, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=0,
+ expect_error=(-4, "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
+ self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=0,
+ expect_error=(-4, "Fee rate (0.000 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)"))
# TODO: Return hex if fee rate is below -maxmempool
# res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0.1, estimate_mode="sat/b", add_to_wallet=False)