Skip to content

Commit

Permalink
qa: Ensure consistent use of decimals
Browse files Browse the repository at this point in the history
This change fixes tests for Python 3.12.8 and 3.13.1 on NetBSD.
  • Loading branch information
hebasto committed Dec 31, 2024
1 parent 228aba2 commit 8f667da
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 113 deletions.
3 changes: 2 additions & 1 deletion test/functional/feature_assumeutxo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
The assumeutxo value generated and used here is committed to in
`CRegTestParams::m_assumeutxo_data` in `src/kernel/chainparams.cpp`.
"""
from decimal import Decimal
from shutil import rmtree

from dataclasses import dataclass
Expand Down Expand Up @@ -560,7 +561,7 @@ def check_tx_counts(final: bool) -> None:
prev_tx = n0.getblock(spend_coin_blockhash, 3)['tx'][0]
prevout = {"txid": prev_tx['txid'], "vout": 0, "scriptPubKey": prev_tx['vout'][0]['scriptPubKey']['hex']}
privkey = n0.get_deterministic_priv_key().key
raw_tx = n1.createrawtransaction([prevout], {getnewdestination()[2]: 24.99})
raw_tx = n1.createrawtransaction([prevout], {getnewdestination()[2]: Decimal("24.99")})
signed_tx = n1.signrawtransactionwithkey(raw_tx, [privkey], [prevout])['hex']
signed_txid = tx_from_hex(signed_tx).rehash()

Expand Down
42 changes: 21 additions & 21 deletions test/functional/rpc_psbt.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ def run_test(self):
p2pkh_pos = out['n']

inputs = [{"txid": txid, "vout": p2wpkh_pos}, {"txid": txid, "vout": p2sh_p2wpkh_pos}, {"txid": txid, "vout": p2pkh_pos}]
outputs = [{self.nodes[1].getnewaddress(): 29.99}]
outputs = [{self.nodes[1].getnewaddress(): Decimal("29.99")}]

# spend single key from node 1
created_psbt = self.nodes[1].walletcreatefundedpsbt(inputs, outputs)
Expand All @@ -370,15 +370,15 @@ def run_test(self):
self.nodes[1].sendrawtransaction(walletprocesspsbt_out['hex'])

self.log.info("Test walletcreatefundedpsbt fee rate of 10000 sat/vB and 0.1 BTC/kvB produces a total fee at or slightly below -maxtxfee (~0.05290000)")
res1 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": 10000, "add_inputs": True})
res1 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": Decimal("10000"), "add_inputs": True})
assert_approx(res1["fee"], 0.055, 0.005)
res2 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": "0.1", "add_inputs": True})
res2 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": Decimal("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.999", "add_inputs": True})
res3 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"fee_rate": Decimal("0.999"), "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})
res4 = self.nodes[1].walletcreatefundedpsbt(inputs, outputs, 0, {"feeRate": Decimal("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")
Expand All @@ -394,21 +394,21 @@ def run_test(self):
assert_raises_rpc_error(-3, "Amount is not a number or string",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: {"foo": "bar"}, "add_inputs": True})
# Test fee rate values that don't pass fixed-point parsing checks.
for invalid_value in ["", 0.000000001, 1e-09, 1.111111111, 1111111111111111, "31.999999999999999999999"]:
for invalid_value in [""] + list(map(Decimal, "0.000000001 1e-09 1.111111111 1111111111111111 31.999999999999999999999".split())):
assert_raises_rpc_error(-3, "Invalid amount",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: invalid_value, "add_inputs": True})
# Test fee_rate values that cannot be represented in sat/vB.
for invalid_value in [0.0001, 0.00000001, 0.00099999, 31.99999999]:
for invalid_value in list(map(Decimal, "0.0001 0.00000001 0.00099999 31.99999999".split())):
assert_raises_rpc_error(-3, "Invalid amount",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": invalid_value, "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)",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": 0.1, "feeRate": 0.1, "add_inputs": True})
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": Decimal("0.1"), "feeRate": Decimal("0.1"), "add_inputs": True})

self.log.info("- raises RPC error if both feeRate and estimate_mode passed")
assert_raises_rpc_error(-8, "Cannot specify both estimate_mode and feeRate",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"estimate_mode": "economical", "feeRate": 0.1, "add_inputs": True})
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"estimate_mode": "economical", "feeRate": Decimal("0.1"), "add_inputs": True})

for param in ["feeRate", "fee_rate"]:
self.log.info("- raises RPC error if both {} and conf_target are passed".format(param))
Expand All @@ -418,7 +418,7 @@ def run_test(self):

self.log.info("- raises RPC error if both fee_rate and estimate_mode are passed")
assert_raises_rpc_error(-8, "Cannot specify both estimate_mode and fee_rate",
self.nodes[1].walletcreatefundedpsbt ,inputs, outputs, 0, {"fee_rate": 1, "estimate_mode": "economical", "add_inputs": True})
self.nodes[1].walletcreatefundedpsbt ,inputs, outputs, 0, {"fee_rate": Decimal("1"), "estimate_mode": "economical", "add_inputs": True})

self.log.info("- raises RPC error with invalid estimate_mode settings")
for k, v in {"number": 42, "object": {"foo": "bar"}}.items():
Expand All @@ -440,14 +440,14 @@ def run_test(self):

self.log.info("Test walletcreatefundedpsbt with too-high fee rate produces total fee well above -maxtxfee and raises RPC error")
# previously this was silently capped at -maxtxfee
for bool_add, outputs_array in {True: outputs, False: [{self.nodes[1].getnewaddress(): 1}]}.items():
for bool_add, outputs_array in {True: outputs, False: [{self.nodes[1].getnewaddress(): Decimal("1")}]}.items():
msg = "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)"
assert_raises_rpc_error(-4, msg, self.nodes[1].walletcreatefundedpsbt, inputs, outputs_array, 0, {"fee_rate": 1000000, "add_inputs": bool_add})
assert_raises_rpc_error(-4, msg, self.nodes[1].walletcreatefundedpsbt, inputs, outputs_array, 0, {"feeRate": 1, "add_inputs": bool_add})
assert_raises_rpc_error(-4, msg, self.nodes[1].walletcreatefundedpsbt, inputs, outputs_array, 0, {"fee_rate": Decimal("1000000"), "add_inputs": bool_add})
assert_raises_rpc_error(-4, msg, self.nodes[1].walletcreatefundedpsbt, inputs, outputs_array, 0, {"feeRate": Decimal("1"), "add_inputs": bool_add})

self.log.info("Test various PSBT operations")
# partially sign multisig things with node 1
psbtx = wmulti.walletcreatefundedpsbt(inputs=[{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], outputs={self.nodes[1].getnewaddress():29.99}, changeAddress=self.nodes[1].getrawchangeaddress())['psbt']
psbtx = wmulti.walletcreatefundedpsbt(inputs=[{"txid":txid,"vout":p2wsh_pos},{"txid":txid,"vout":p2sh_pos},{"txid":txid,"vout":p2sh_p2wsh_pos}], outputs={self.nodes[1].getnewaddress():Decimal("29.99")}, changeAddress=self.nodes[1].getrawchangeaddress())['psbt']
walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(psbtx)
psbtx = walletprocesspsbt_out['psbt']
assert_equal(walletprocesspsbt_out['complete'], False)
Expand All @@ -461,11 +461,11 @@ def run_test(self):
self.nodes[2].sendrawtransaction(walletprocesspsbt_out['hex'])

# check that walletprocesspsbt fails to decode a non-psbt
rawtx = self.nodes[1].createrawtransaction([{"txid":txid,"vout":p2wpkh_pos}], {self.nodes[1].getnewaddress():9.99})
rawtx = self.nodes[1].createrawtransaction([{"txid":txid,"vout":p2wpkh_pos}], {self.nodes[1].getnewaddress():Decimal("9.99")})
assert_raises_rpc_error(-22, "TX decode failed", self.nodes[1].walletprocesspsbt, rawtx)

# Convert a non-psbt to psbt and make sure we can decode it
rawtx = self.nodes[0].createrawtransaction([], {self.nodes[1].getnewaddress():10})
rawtx = self.nodes[0].createrawtransaction([], {self.nodes[1].getnewaddress():Decimal("10")})
rawtx = self.nodes[0].fundrawtransaction(rawtx)
new_psbt = self.nodes[0].converttopsbt(rawtx['hex'])
self.nodes[0].decodepsbt(new_psbt)
Expand All @@ -492,7 +492,7 @@ def run_test(self):
self.generate(self.nodes[0], 6)[0]

# Create a psbt spending outputs from nodes 1 and 2
psbt_orig = self.nodes[0].createpsbt([utxo1, utxo2], {self.nodes[0].getnewaddress():25.999})
psbt_orig = self.nodes[0].createpsbt([utxo1, utxo2], {self.nodes[0].getnewaddress():Decimal("25.999")})

# Update psbts, should only have data for one input and not the other
psbt1 = self.nodes[1].walletprocesspsbt(psbt_orig, False, "ALL")['psbt']
Expand Down Expand Up @@ -558,7 +558,7 @@ def run_test(self):
self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)

# Make sure the wallet's change type is respected by default
small_output = {self.nodes[0].getnewaddress():0.1}
small_output = {self.nodes[0].getnewaddress():Decimal("0.1")}
psbtx_native = self.nodes[0].walletcreatefundedpsbt([], [small_output])
self.assert_change_type(psbtx_native, "witness_v0_keyhash")
psbtx_legacy = self.nodes[1].walletcreatefundedpsbt([], [small_output])
Expand All @@ -585,8 +585,8 @@ def run_test(self):
wunsafe = self.nodes[2].get_wallet_rpc("unsafe")
self.nodes[0].sendtoaddress(wunsafe.getnewaddress(), 2)
self.sync_mempools()
assert_raises_rpc_error(-4, "Insufficient funds", wunsafe.walletcreatefundedpsbt, [], [{self.nodes[0].getnewaddress(): 1}])
wunsafe.walletcreatefundedpsbt([], [{self.nodes[0].getnewaddress(): 1}], 0, {"include_unsafe": True})
assert_raises_rpc_error(-4, "Insufficient funds", wunsafe.walletcreatefundedpsbt, [], [{self.nodes[0].getnewaddress(): Decimal("1")}])
wunsafe.walletcreatefundedpsbt([], [{self.nodes[0].getnewaddress(): Decimal("1")}], 0, {"include_unsafe": True})

# BIP 174 Test Vectors

Expand Down Expand Up @@ -680,7 +680,7 @@ def test_psbt_input_keys(psbt_input, keys):
assert_equal(set(keys), set(psbt_input.keys()))

# Create a PSBT. None of the inputs are filled initially
psbt = self.nodes[1].createpsbt([utxo1, utxo2, utxo3], {self.nodes[0].getnewaddress():32.999})
psbt = self.nodes[1].createpsbt([utxo1, utxo2, utxo3], {self.nodes[0].getnewaddress():Decimal("32.999")})
decoded = self.nodes[1].decodepsbt(psbt)
test_psbt_input_keys(decoded['inputs'][0], [])
test_psbt_input_keys(decoded['inputs'][1], [])
Expand Down
26 changes: 13 additions & 13 deletions test/functional/wallet_bumpfee.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def run_test(self):
self.log.info("Mining blocks...")
self.generate(peer_node, 110)
for _ in range(25):
peer_node.sendtoaddress(rbf_node_address, 0.001)
peer_node.sendtoaddress(rbf_node_address, Decimal("0.001"))
self.sync_all()
self.generate(peer_node, 1)
assert_equal(rbf_node.getbalance(), Decimal("0.025"))
Expand Down Expand Up @@ -176,7 +176,7 @@ def test_invalid_parameters(self, rbf_node, peer_node, dest_address):
assert_raises_rpc_error(-8, "Invalid parameter, output argument cannot be an empty array",
rbf_node.bumpfee, rbfid, {"outputs": []})
assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: " + dest_address,
rbf_node.bumpfee, rbfid, {"outputs": [{dest_address: 0.1}, {dest_address: 0.2}]})
rbf_node.bumpfee, rbfid, {"outputs": [{dest_address: Decimal("0.1")}, {dest_address: Decimal("0.2")}]})
assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data",
rbf_node.bumpfee, rbfid, {"outputs": [{"data": "deadbeef"}, {"data": "deadbeef"}]})

Expand All @@ -185,7 +185,7 @@ def test_invalid_parameters(self, rbf_node, peer_node, dest_address):
assert_raises_rpc_error(-8, "Change position is out of range", rbf_node.bumpfee, rbfid, {"original_change_index": 2})

self.log.info("Test outputs and original_change_index cannot both be provided")
assert_raises_rpc_error(-8, "The options 'outputs' and 'original_change_index' are incompatible. You can only either specify a new set of outputs, or designate a change output to be recycled.", rbf_node.bumpfee, rbfid, {"original_change_index": 2, "outputs": [{dest_address: 0.1}]})
assert_raises_rpc_error(-8, "The options 'outputs' and 'original_change_index' are incompatible. You can only either specify a new set of outputs, or designate a change output to be recycled.", rbf_node.bumpfee, rbfid, {"original_change_index": 2, "outputs": [{dest_address: Decimal("0.1")}]})

self.clear_mempool()

Expand Down Expand Up @@ -315,8 +315,8 @@ def test_simple_bumpfee_succeeds(self, mode, rbf_node, peer_node, dest_address):
bumped_tx = rbf_node.bumpfee(rbfid, fee_rate=NORMAL)
elif mode == "new_outputs":
new_address = peer_node.getnewaddress()
bumped_psbt = rbf_node.psbtbumpfee(rbfid, outputs={new_address: 0.0003})
bumped_tx = rbf_node.bumpfee(rbfid, outputs={new_address: 0.0003})
bumped_psbt = rbf_node.psbtbumpfee(rbfid, outputs={new_address: Decimal("0.0003")})
bumped_tx = rbf_node.bumpfee(rbfid, outputs={new_address: Decimal("0.0003")})
else:
bumped_psbt = rbf_node.psbtbumpfee(rbfid)
bumped_tx = rbf_node.bumpfee(rbfid)
Expand Down Expand Up @@ -353,7 +353,7 @@ def test_segwit_bumpfee_succeeds(self, rbf_node, dest_address):
# which spends it, and make sure bumpfee can be called on it.

segwit_out = rbf_node.getnewaddress(address_type='bech32')
segwitid = rbf_node.send({segwit_out: "0.0009"}, options={"change_position": 1})["txid"]
segwitid = rbf_node.send({segwit_out: Decimal("0.0009")}, options={"change_position": 1})["txid"]

rbfraw = rbf_node.createrawtransaction([{
'txid': segwitid,
Expand Down Expand Up @@ -423,7 +423,7 @@ def test_bumpfee_with_descendant_fails(self, rbf_node, rbf_node_address, dest_ad
self.log.info('Test that fee cannot be bumped when it has descendant')
# parent is send-to-self, so we don't have to check which output is change when creating the child tx
parent_id = spend_one_input(rbf_node, rbf_node_address)
tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: 0.00020000})
tx = rbf_node.createrawtransaction([{"txid": parent_id, "vout": 0}], {dest_address: Decimal("0.00020000")})
tx = rbf_node.signrawtransactionwithwallet(tx)
rbf_node.sendrawtransaction(tx["hex"])
assert_raises_rpc_error(-8, "Transaction has descendants in the wallet", rbf_node.bumpfee, parent_id)
Expand All @@ -443,7 +443,7 @@ def test_bumpfee_with_abandoned_descendant_succeeds(self, rbf_node, rbf_node_add
# parent is send-to-self, so we don't have to check which output is change when creating the child tx
parent_id = spend_one_input(rbf_node, rbf_node_address)
# Submit child transaction with low fee
child_id = rbf_node.send(outputs={dest_address: 0.00020000},
child_id = rbf_node.send(outputs={dest_address: Decimal("0.00020000")},
options={"inputs": [{"txid": parent_id, "vout": 0}], "fee_rate": 2})["txid"]
assert child_id in rbf_node.getrawmempool()

Expand Down Expand Up @@ -631,12 +631,12 @@ def test_watchonly_psbt(self, peer_node, rbf_node, dest_address):

funding_address1 = watcher.getnewaddress(address_type='bech32')
funding_address2 = watcher.getnewaddress(address_type='bech32')
peer_node.sendmany("", {funding_address1: 0.001, funding_address2: 0.001})
peer_node.sendmany("", {funding_address1: Decimal("0.001"), funding_address2: Decimal("0.001")})
self.generate(peer_node, 1)

# Create single-input PSBT for transaction to be bumped
# Ensure the payment amount + change can be fully funded using one of the 0.001BTC inputs.
psbt = watcher.walletcreatefundedpsbt([watcher.listunspent()[0]], {dest_address: 0.0005}, 0,
psbt = watcher.walletcreatefundedpsbt([watcher.listunspent()[0]], {dest_address: Decimal("0.0005")}, 0,
{"fee_rate": 1, "add_inputs": False}, True)['psbt']
psbt_signed = signer.walletprocesspsbt(psbt=psbt, sign=True, sighashtype="ALL", bip32derivs=True)
original_txid = watcher.sendrawtransaction(psbt_signed["hex"])
Expand Down Expand Up @@ -841,12 +841,12 @@ def test_bumpfee_with_feerate_ignores_walletincrementalrelayfee(self, rbf_node,
tx = rbf_node.send(outputs=[{dest_address: 1}], fee_rate=2)

# Ensure you can not fee bump with a fee_rate below or equal to the original fee_rate
assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 1})
assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 2})
assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": Decimal("1")})
assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": Decimal("2")})

# Ensure you can not fee bump if the fee_rate is more than original fee_rate but the total fee from new fee_rate is
# less than (original fee + incrementalrelayfee)
assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": 2.8})
assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, tx["txid"], {"fee_rate": Decimal("2.8")})

# You can fee bump as long as the new fee set from fee_rate is at least (original fee + incrementalrelayfee)
rbf_node.bumpfee(tx["txid"], {"fee_rate": 3})
Expand Down
Loading

0 comments on commit 8f667da

Please sign in to comment.