aboutsummaryrefslogtreecommitdiff
path: root/test/functional/wallet_basic.py
diff options
context:
space:
mode:
authorJon Atack <jon@atack.com>2020-11-04 13:13:17 +0100
committerJon Atack <jon@atack.com>2020-11-11 15:55:59 +0100
commita0d495747320c79b27a83c216dcc526ac8df8f24 (patch)
tree1d662469632dc37850ff4b5bd9a9e04566f8e78a /test/functional/wallet_basic.py
parente21212f01b7c41eba13b0479b252053cf482bc1f (diff)
wallet: introduce fee_rate (sat/vB) param/option
Create a fee_rate (sat/vB) RPC param and replace overloading the conf_target and estimate_mode params in the following 6 RPCs with it: - sendtoaddress - sendmany - send - fundrawtransaction - walletcreatefundedpsbt - bumpfee In RPC bumpfee, the previously existing fee_rate remains but the unit is changed from BTC/kvB to sat/vB. This is a breaking change, but it should not be an overly risky one, as the units change by a factor of 1e5 and any fees specified in BTC/kvB after this commit will either be too low and raise an error or be 1 sat/vB and can be RBFed. Update the test coverage for each RPC. Co-authored-by: Murch <murch@murch.one>
Diffstat (limited to 'test/functional/wallet_basic.py')
-rwxr-xr-xtest/functional/wallet_basic.py178
1 files changed, 61 insertions, 117 deletions
diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
index 40daffd2c2..7b4d459503 100755
--- a/test/functional/wallet_basic.py
+++ b/test/functional/wallet_basic.py
@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test the wallet."""
from decimal import Decimal
+from itertools import product
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
@@ -14,6 +15,8 @@ from test_framework.util import (
)
from test_framework.wallet_util import test_address
+OUT_OF_RANGE = "Amount out of range"
+
class WalletTest(BitcoinTestFramework):
def set_test_params(self):
@@ -74,7 +77,7 @@ class WalletTest(BitcoinTestFramework):
assert_equal(len(self.nodes[1].listunspent()), 1)
assert_equal(len(self.nodes[2].listunspent()), 0)
- self.log.info("test gettxout")
+ self.log.info("Test gettxout")
confirmed_txid, confirmed_index = utxos[0]["txid"], utxos[0]["vout"]
# First, outputs that are unspent both in the chain and in the
# mempool should appear with or without include_mempool
@@ -87,7 +90,7 @@ class WalletTest(BitcoinTestFramework):
self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 11)
mempool_txid = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 10)
- self.log.info("test gettxout (second part)")
+ self.log.info("Test gettxout (second part)")
# utxo spent in mempool should be visible if you exclude mempool
# but invisible if you include mempool
txout = self.nodes[0].gettxout(confirmed_txid, confirmed_index, False)
@@ -227,65 +230,41 @@ class WalletTest(BitcoinTestFramework):
assert_equal(self.nodes[2].getbalance(), node_2_bal)
node_0_bal = self.check_fee_amount(self.nodes[0].getbalance(), node_0_bal + Decimal('10'), fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
- self.log.info("Test case-insensitive explicit fee rate (sendmany as BTC/kB)")
- # Throw if no conf_target provided
- assert_raises_rpc_error(-8, "Selected estimate_mode bTc/kB requires a fee rate to be specified in conf_target",
- self.nodes[2].sendmany,
- amounts={ address: 10 },
- estimate_mode='bTc/kB')
- # Throw if negative feerate
- assert_raises_rpc_error(-3, "Amount out of range",
- self.nodes[2].sendmany,
- amounts={ address: 10 },
- conf_target=-1,
- estimate_mode='bTc/kB')
- fee_per_kb = 0.0002500
- explicit_fee_per_byte = Decimal(fee_per_kb) / 1000
- txid = self.nodes[2].sendmany(
- amounts={ address: 10 },
- conf_target=fee_per_kb,
- estimate_mode='bTc/kB',
- )
- self.nodes[2].generate(1)
- self.sync_all(self.nodes[0:3])
- node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), explicit_fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
- assert_equal(self.nodes[2].getbalance(), node_2_bal)
- node_0_bal += Decimal('10')
- assert_equal(self.nodes[0].getbalance(), node_0_bal)
+ self.log.info("Test sendmany with fee_rate param (explicit fee rate in sat/vB)")
+ fee_rate_sat_vb = 2
+ fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
+ explicit_fee_rate_btc_kvb = Decimal(fee_rate_btc_kvb) / 1000
- self.log.info("Test case-insensitive explicit fee rate (sendmany as sat/B)")
- # Throw if no conf_target provided
- assert_raises_rpc_error(-8, "Selected estimate_mode sat/b requires a fee rate to be specified in conf_target",
- self.nodes[2].sendmany,
- amounts={ address: 10 },
- estimate_mode='sat/b')
- # Throw if negative feerate
- assert_raises_rpc_error(-3, "Amount out of range",
- self.nodes[2].sendmany,
- amounts={ address: 10 },
- conf_target=-1,
- estimate_mode='sat/b')
- fee_sat_per_b = 2
- fee_per_kb = fee_sat_per_b / 100000.0
- explicit_fee_per_byte = Decimal(fee_per_kb) / 1000
- txid = self.nodes[2].sendmany(
- amounts={ address: 10 },
- conf_target=fee_sat_per_b,
- estimate_mode='sAT/b',
- )
+ # Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
+ txid = self.nodes[2].sendmany(amounts={address: 10}, conf_target=0, estimate_mode="", fee_rate=fee_rate_sat_vb)
self.nodes[2].generate(1)
self.sync_all(self.nodes[0:3])
balance = self.nodes[2].getbalance()
- node_2_bal = self.check_fee_amount(balance, node_2_bal - Decimal('10'), explicit_fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
+ node_2_bal = self.check_fee_amount(balance, node_2_bal - Decimal('10'), explicit_fee_rate_btc_kvb, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
assert_equal(balance, node_2_bal)
node_0_bal += Decimal('10')
assert_equal(self.nodes[0].getbalance(), node_0_bal)
+ for key in ["totalFee", "feeRate"]:
+ assert_raises_rpc_error(-8, "Unknown named parameter key", self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=1, key=1)
+
# Test setting explicit fee rate just below the minimum.
- for unit, fee_rate in {"BTC/kB": 0.00000999, "sat/B": 0.99999999}.items():
- self.log.info("Test sendmany raises 'fee rate too low' if conf_target {} and estimate_mode {} are passed".format(fee_rate, unit))
- assert_raises_rpc_error(-6, "Fee rate (0.00000999 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)",
- self.nodes[2].sendmany, amounts={address: 10}, estimate_mode=unit, conf_target=fee_rate)
+ self.log.info("Test sendmany raises 'fee rate too low' if fee_rate of 0.99999999 is passed")
+ assert_raises_rpc_error(-6, "Fee rate (0.00000999 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)",
+ self.nodes[2].sendmany, amounts={address: 10}, fee_rate=0.99999999)
+
+ self.log.info("Test sendmany raises if fee_rate of 0 or -1 is passed")
+ assert_raises_rpc_error(-6, "Fee rate (0.00000000 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)",
+ self.nodes[2].sendmany, amounts={address: 10}, fee_rate=0)
+ assert_raises_rpc_error(-3, OUT_OF_RANGE, self.nodes[2].sendmany, amounts={address: 10}, fee_rate=-1)
+
+ self.log.info("Test sendmany raises if an invalid conf_target or estimate_mode is passed")
+ for target, mode in product([-1, 0, 1009], ["economical", "conservative"]):
+ assert_raises_rpc_error(-8, "Invalid conf_target, must be between 1 and 1008", # max value of 1008 per src/policy/fees.h
+ self.nodes[2].sendmany, amounts={address: 1}, conf_target=target, estimate_mode=mode)
+ for target, mode in product([-1, 0], ["btc/kb", "sat/b"]):
+ assert_raises_rpc_error(-8, "Invalid estimate_mode parameter",
+ self.nodes[2].sendmany, amounts={address: 1}, conf_target=target, estimate_mode=mode)
self.start_node(3, self.nodes[3].extra_args)
self.connect_nodes(0, 3)
@@ -318,7 +297,7 @@ class WalletTest(BitcoinTestFramework):
assert_equal(uTx['amount'], Decimal('0'))
assert found
- # do some -walletbroadcast tests
+ self.log.info("Test -walletbroadcast")
self.stop_nodes()
self.start_node(0, ["-walletbroadcast=0"])
self.start_node(1, ["-walletbroadcast=0"])
@@ -378,7 +357,7 @@ class WalletTest(BitcoinTestFramework):
# General checks for errors from incorrect inputs
# This will raise an exception because the amount is negative
- assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "-1")
+ assert_raises_rpc_error(-3, OUT_OF_RANGE, self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "-1")
# This will raise an exception because the amount type is wrong
assert_raises_rpc_error(-3, "Invalid amount", self.nodes[0].sendtoaddress, self.nodes[2].getnewaddress(), "1f-4")
@@ -420,78 +399,43 @@ class WalletTest(BitcoinTestFramework):
self.nodes[0].generate(1)
self.sync_all(self.nodes[0:3])
- self.log.info("Test case-insensitive explicit fee rate (sendtoaddress as BTC/kB)")
- self.nodes[0].generate(1)
- self.sync_all(self.nodes[0:3])
+ self.log.info("Test sendtoaddress with fee_rate param (explicit fee rate in sat/vB)")
prebalance = self.nodes[2].getbalance()
assert prebalance > 2
address = self.nodes[1].getnewaddress()
- # Throw if no conf_target provided
- assert_raises_rpc_error(-8, "Selected estimate_mode BTc/Kb requires a fee rate to be specified in conf_target",
- self.nodes[2].sendtoaddress,
- address=address,
- amount=1.0,
- estimate_mode='BTc/Kb')
- # Throw if negative feerate
- assert_raises_rpc_error(-3, "Amount out of range",
- self.nodes[2].sendtoaddress,
- address=address,
- amount=1.0,
- conf_target=-1,
- estimate_mode='btc/kb')
- txid = self.nodes[2].sendtoaddress(
- address=address,
- amount=1.0,
- conf_target=0.00002500,
- estimate_mode='btc/kb',
- )
- tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])
- self.sync_all(self.nodes[0:3])
- self.nodes[0].generate(1)
- self.sync_all(self.nodes[0:3])
- postbalance = self.nodes[2].getbalance()
- fee = prebalance - postbalance - Decimal('1')
- assert_fee_amount(fee, tx_size, Decimal('0.00002500'))
-
- self.sync_all(self.nodes[0:3])
+ amount = 3
+ fee_rate_sat_vb = 2
+ fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
- self.log.info("Test case-insensitive explicit fee rate (sendtoaddress as sat/B)")
- self.nodes[0].generate(1)
- prebalance = self.nodes[2].getbalance()
- assert prebalance > 2
- address = self.nodes[1].getnewaddress()
- # Throw if no conf_target provided
- assert_raises_rpc_error(-8, "Selected estimate_mode SAT/b requires a fee rate to be specified in conf_target",
- self.nodes[2].sendtoaddress,
- address=address,
- amount=1.0,
- estimate_mode='SAT/b')
- # Throw if negative feerate
- assert_raises_rpc_error(-3, "Amount out of range",
- self.nodes[2].sendtoaddress,
- address=address,
- amount=1.0,
- conf_target=-1,
- estimate_mode='SAT/b')
- txid = self.nodes[2].sendtoaddress(
- address=address,
- amount=1.0,
- conf_target=2,
- estimate_mode='SAT/B',
- )
+ # Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
+ txid = self.nodes[2].sendtoaddress(address=address, amount=amount, conf_target=0, estimate_mode="", fee_rate=fee_rate_sat_vb)
tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])
- self.sync_all(self.nodes[0:3])
self.nodes[0].generate(1)
self.sync_all(self.nodes[0:3])
postbalance = self.nodes[2].getbalance()
- fee = prebalance - postbalance - Decimal('1')
- assert_fee_amount(fee, tx_size, Decimal('0.00002000'))
+ fee = prebalance - postbalance - Decimal(amount)
+ assert_fee_amount(fee, tx_size, Decimal(fee_rate_btc_kvb))
+
+ for key in ["totalFee", "feeRate"]:
+ assert_raises_rpc_error(-8, "Unknown named parameter key", self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=1, key=1)
# Test setting explicit fee rate just below the minimum.
- for unit, fee_rate in {"BTC/kB": 0.00000999, "sat/B": 0.99999999}.items():
- self.log.info("Test sendtoaddress raises 'fee rate too low' if conf_target {} and estimate_mode {} are passed".format(fee_rate, unit))
- assert_raises_rpc_error(-6, "Fee rate (0.00000999 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)",
- self.nodes[2].sendtoaddress, address=address, amount=1, estimate_mode=unit, conf_target=fee_rate)
+ self.log.info("Test sendtoaddress raises 'fee rate too low' if fee_rate of 0.99999999 is passed")
+ assert_raises_rpc_error(-6, "Fee rate (0.00000999 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)",
+ self.nodes[2].sendtoaddress, address=address, amount=1, fee_rate=0.99999999)
+
+ self.log.info("Test sendtoaddress raises if fee_rate of 0 or -1 is passed")
+ assert_raises_rpc_error(-6, "Fee rate (0.00000000 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)",
+ self.nodes[2].sendtoaddress, address=address, amount=10, fee_rate=0)
+ assert_raises_rpc_error(-3, OUT_OF_RANGE, self.nodes[2].sendtoaddress, address=address, amount=1.0, fee_rate=-1)
+
+ self.log.info("Test sendtoaddress raises if an invalid conf_target or estimate_mode is passed")
+ for target, mode in product([-1, 0, 1009], ["economical", "conservative"]):
+ assert_raises_rpc_error(-8, "Invalid conf_target, must be between 1 and 1008", # max value of 1008 per src/policy/fees.h
+ self.nodes[2].sendtoaddress, address=address, amount=1, conf_target=target, estimate_mode=mode)
+ for target, mode in product([-1, 0], ["btc/kb", "sat/b"]):
+ assert_raises_rpc_error(-8, "Invalid estimate_mode parameter",
+ self.nodes[2].sendtoaddress, address=address, amount=1, conf_target=target, estimate_mode=mode)
# 2. Import address from node2 to node1
self.nodes[1].importaddress(address_to_import)
@@ -549,7 +493,7 @@ class WalletTest(BitcoinTestFramework):
]
chainlimit = 6
for m in maintenance:
- self.log.info("check " + m)
+ self.log.info("Test " + m)
self.stop_nodes()
# set lower ancestor limit for later
self.start_node(0, [m, "-limitancestorcount=" + str(chainlimit)])