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 311ef06
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 87 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 @@ -396,19 +396,19 @@ def run_test(self):
# 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"]:
assert_raises_rpc_error(-3, "Invalid amount",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: invalid_value, "add_inputs": True})
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {param: Decimal(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]:
assert_raises_rpc_error(-3, "Invalid amount",
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": invalid_value, "add_inputs": True})
self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": Decimal(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
32 changes: 16 additions & 16 deletions test/functional/wallet_conflicts.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ def test_block_conflicts(self):
self.disconnect_nodes(2, 1)

self.log.info("Create transactions that conflict with each other")
output_A = self.get_utxo_of_value(from_tx_id=txid_conflict_from_1, search_value=10)
output_B = self.get_utxo_of_value(from_tx_id=txid_conflict_from_2, search_value=10)
output_A = self.get_utxo_of_value(from_tx_id=txid_conflict_from_1, search_value=Decimal("10"))
output_B = self.get_utxo_of_value(from_tx_id=txid_conflict_from_2, search_value=Decimal("10"))

# First create a transaction that consumes both A and B outputs.
#
Expand All @@ -80,7 +80,7 @@ def test_block_conflicts(self):
self.generate(self.nodes[0], 1, sync_fun=self.no_op)

# Now that 'AB_parent_tx' was broadcast, build 'Child_Tx'
output_c = self.get_utxo_of_value(from_tx_id=txid_AB_parent, search_value=19.99998)
output_c = self.get_utxo_of_value(from_tx_id=txid_AB_parent, search_value=Decimal("19.99998"))
inputs_tx_C_child = [({"txid": txid_AB_parent, "vout": output_c})]

tx_C_child = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs_tx_C_child, {self.nodes[0].getnewaddress() : Decimal("19.99996")}))
Expand Down Expand Up @@ -152,15 +152,15 @@ def test_mempool_conflict(self):
assert all([tx["amount"] == 25 for tx in unspents])

# tx1 spends unspent[0] and unspent[1]
raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[1]], outputs=[{bob.getnewaddress() : 49.9999}])
raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[1]], outputs=[{bob.getnewaddress() : Decimal("49.9999")}])
tx1 = alice.signrawtransactionwithwallet(raw_tx)['hex']

# tx2 spends unspent[1] and unspent[2], conflicts with tx1
raw_tx = alice.createrawtransaction(inputs=[unspents[1], unspents[2]], outputs=[{bob.getnewaddress() : 49.99}])
raw_tx = alice.createrawtransaction(inputs=[unspents[1], unspents[2]], outputs=[{bob.getnewaddress() : Decimal("49.99")}])
tx2 = alice.signrawtransactionwithwallet(raw_tx)['hex']

# tx3 spends unspent[2], conflicts with tx2
raw_tx = alice.createrawtransaction(inputs=[unspents[2]], outputs=[{bob.getnewaddress() : 24.9899}])
raw_tx = alice.createrawtransaction(inputs=[unspents[2]], outputs=[{bob.getnewaddress() : Decimal("24.9899")}])
tx3 = alice.signrawtransactionwithwallet(raw_tx)['hex']

# broadcast tx1
Expand Down Expand Up @@ -222,21 +222,21 @@ def test_mempool_and_block_conflicts(self):
self.disconnect_nodes(0, 1)

# Sends funds to bob
raw_tx = alice.createrawtransaction(inputs=[unspents[0]], outputs=[{bob.getnewaddress() : 24.99999}])
raw_tx = alice.createrawtransaction(inputs=[unspents[0]], outputs=[{bob.getnewaddress() : Decimal("24.99999")}])
raw_tx1 = alice.signrawtransactionwithwallet(raw_tx)['hex']
tx1_txid = bob.sendrawtransaction(raw_tx1) # broadcast original tx spending unspents[0] only to bob

# create a conflict to previous tx (also spends unspents[0]), but don't broadcast, sends funds back to alice
raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[2]], outputs=[{alice.getnewaddress() : 49.999}])
raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[2]], outputs=[{alice.getnewaddress() : Decimal("49.999")}])
tx1_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']

# Sends funds to bob
raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{bob.getnewaddress() : 24.9999}])
raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{bob.getnewaddress() : Decimal("24.9999")}])
raw_tx2 = alice.signrawtransactionwithwallet(raw_tx)['hex']
tx2_txid = bob.sendrawtransaction(raw_tx2) # broadcast another original tx spending unspents[1] only to bob

# create a conflict to previous tx (also spends unspents[1]), but don't broadcast, sends funds to alice
raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{alice.getnewaddress() : 24.9999}])
raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{alice.getnewaddress() : Decimal("24.9999")}])
tx2_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']

bob_unspents = [{"txid" : element, "vout" : 0} for element in [tx1_txid, tx2_txid]]
Expand All @@ -245,7 +245,7 @@ def test_mempool_and_block_conflicts(self):
assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("49.99989000"))

# spend both of bob's unspents, child tx of tx1 and tx2
raw_tx = bob.createrawtransaction(inputs=[bob_unspents[0], bob_unspents[1]], outputs=[{bob.getnewaddress() : 49.999}])
raw_tx = bob.createrawtransaction(inputs=[bob_unspents[0], bob_unspents[1]], outputs=[{bob.getnewaddress() : Decimal("49.999")}])
raw_tx3 = bob.signrawtransactionwithwallet(raw_tx)['hex']
tx3_txid = bob.sendrawtransaction(raw_tx3) # broadcast tx only to bob

Expand Down Expand Up @@ -301,7 +301,7 @@ def test_mempool_and_block_conflicts(self):
assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("24.99990000"))

# create a conflict to previous tx (also spends unspents[2]), but don't broadcast, sends funds back to alice
raw_tx = alice.createrawtransaction(inputs=[unspents[2]], outputs=[{alice.getnewaddress() : 24.99}])
raw_tx = alice.createrawtransaction(inputs=[unspents[2]], outputs=[{alice.getnewaddress() : Decimal("24.99")}])
tx1_conflict_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']

bob.sendrawtransaction(tx1_conflict_conflict) # kick tx1_conflict out of the mempool
Expand Down Expand Up @@ -344,7 +344,7 @@ def test_descendants_with_mempool_conflicts(self):
assert_equal(alice.getrawmempool(), [])

# Alice spends first utxo to bob in tx1
raw_tx = alice.createrawtransaction(inputs=[unspents[0]], outputs=[{bob.getnewaddress() : 24.9999}])
raw_tx = alice.createrawtransaction(inputs=[unspents[0]], outputs=[{bob.getnewaddress() : Decimal("24.9999")}])
tx1 = alice.signrawtransactionwithwallet(raw_tx)['hex']
tx1_txid = alice.sendrawtransaction(tx1)

Expand All @@ -355,7 +355,7 @@ def test_descendants_with_mempool_conflicts(self):

assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"], [])

raw_tx = bob.createrawtransaction(inputs=[bob.listunspent(minconf=0)[0]], outputs=[{carol.getnewaddress() : 24.999}])
raw_tx = bob.createrawtransaction(inputs=[bob.listunspent(minconf=0)[0]], outputs=[{carol.getnewaddress() : Decimal("24.999")}])
# Bob creates a child to tx1
tx1_child = bob.signrawtransactionwithwallet(raw_tx)['hex']
tx1_child_txid = bob.sendrawtransaction(tx1_child)
Expand All @@ -373,7 +373,7 @@ def test_descendants_with_mempool_conflicts(self):
assert_equal(carol.getbalances()["mine"]["untrusted_pending"], Decimal("24.99900000"))

# Alice spends first unspent again, conflicting with tx1
raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[1]], outputs=[{carol.getnewaddress() : 49.99}])
raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[1]], outputs=[{carol.getnewaddress() : Decimal("49.99")}])
tx1_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']
tx1_conflict_txid = alice.sendrawtransaction(tx1_conflict)

Expand All @@ -392,7 +392,7 @@ def test_descendants_with_mempool_conflicts(self):
assert_equal(bob.gettransaction(tx1_child_txid)["mempoolconflicts"], [tx1_conflict_txid])

# Now create a conflict to tx1_conflict, so that it gets kicked out of the mempool
raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{carol.getnewaddress() : 24.9895}])
raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{carol.getnewaddress() : Decimal("24.9895")}])
tx1_conflict_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']
tx1_conflict_conflict_txid = alice.sendrawtransaction(tx1_conflict_conflict)

Expand Down
Loading

0 comments on commit 311ef06

Please sign in to comment.