Skip to content

Commit

Permalink
qa: Ensure consistent use of decimals instead of floats
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 Jan 2, 2025
1 parent 228aba2 commit 8a185e1
Show file tree
Hide file tree
Showing 26 changed files with 187 additions and 175 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
4 changes: 2 additions & 2 deletions test/functional/mempool_accept.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,13 @@ def run_test(self):
assert_raises_rpc_error(-3, "Amount out of range", lambda: self.check_mempool_result(
result_expected=None,
rawtxs=[raw_tx_in_block],
maxfeerate=-0.01,
maxfeerate=Decimal("-0.01"),
))
# ... 0.99 passes
self.check_mempool_result(
result_expected=[{'txid': txid_in_block, 'allowed': False, 'reject-reason': 'txn-already-known'}],
rawtxs=[raw_tx_in_block],
maxfeerate=0.99,
maxfeerate=Decimal("0.99"),
)

self.log.info('A transaction not in the mempool')
Expand Down
44 changes: 22 additions & 22 deletions test/functional/rpc_psbt.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,17 @@ def test_psbt_incomplete_after_invalid_modification(self):
node = self.nodes[2]
wallet = node.get_wallet_rpc(self.default_wallet_name)
address = wallet.getnewaddress()
wallet.sendtoaddress(address=address, amount=1.0)
wallet.sendtoaddress(address=address, amount=Decimal("1.0"))
self.generate(node, nblocks=1, sync_fun=lambda: self.sync_all(self.nodes[:2]))

utxos = wallet.listunspent(addresses=[address])
psbt = wallet.createpsbt([{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{wallet.getnewaddress(): 0.9999}])
psbt = wallet.createpsbt([{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{wallet.getnewaddress(): Decimal("0.9999")}])
signed_psbt = wallet.walletprocesspsbt(psbt)["psbt"]

# Modify the raw transaction by changing the output address, so the signature is no longer valid
signed_psbt_obj = PSBT.from_base64(signed_psbt)
substitute_addr = wallet.getnewaddress()
raw = wallet.createrawtransaction([{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{substitute_addr: 0.9999}])
raw = wallet.createrawtransaction([{"txid": utxos[0]["txid"], "vout": utxos[0]["vout"]}], [{substitute_addr: Decimal("0.9999")}])
signed_psbt_obj.g.map[PSBT_GLOBAL_UNSIGNED_TX] = bytes.fromhex(raw)

# Check that the walletprocesspsbt call succeeds but also recognizes that the transaction is not complete
Expand Down Expand Up @@ -120,7 +120,7 @@ def test_utxo_conversion(self):

# Construct an unsigned PSBT on the online node
utxos = wonline.listunspent(addresses=[offline_addr])
raw = wonline.createrawtransaction([{"txid":utxos[0]["txid"], "vout":utxos[0]["vout"]}],[{online_addr:0.9999}])
raw = wonline.createrawtransaction([{"txid":utxos[0]["txid"], "vout":utxos[0]["vout"]}],[{online_addr:Decimal("0.9999")}])
psbt = wonline.walletprocesspsbt(online_node.converttopsbt(raw))["psbt"]
assert not "not_witness_utxo" in mining_node.decodepsbt(psbt)["inputs"][0]

Expand Down Expand Up @@ -154,11 +154,11 @@ def test_input_confs_control(self):
self.nodes[1].sendmany("", {wallet.getnewaddress():1, wallet.getnewaddress():1})
self.generate(self.nodes[1], 1)

unconfirmed_txid = wallet.sendtoaddress(wallet.getnewaddress(), 0.5)
unconfirmed_txid = wallet.sendtoaddress(wallet.getnewaddress(), Decimal("0.5"))

self.log.info("Crafting PSBT using an unconfirmed input")
target_address = self.nodes[1].getnewaddress()
psbtx1 = wallet.walletcreatefundedpsbt([], {target_address: 0.1}, 0, {'fee_rate': 1, 'maxconf': 0})['psbt']
psbtx1 = wallet.walletcreatefundedpsbt([], {target_address: Decimal("0.1")}, 0, {'fee_rate': 1, 'maxconf': 0})['psbt']

# Make sure we only had the one input
tx1_inputs = self.nodes[0].decodepsbt(psbtx1)['tx']['vin']
Expand Down 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 Down Expand Up @@ -447,7 +447,7 @@ def run_test(self):

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,7 +461,7 @@ 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
Expand Down Expand Up @@ -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 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 Expand Up @@ -928,7 +928,7 @@ def test_psbt_input_keys(psbt_input, keys):
self.log.info("Test that walletprocesspsbt both updates and signs a non-updated psbt containing Taproot inputs")
addr = self.nodes[0].getnewaddress("", "bech32m")
utxo = self.create_outpoints(self.nodes[0], outputs=[{addr: 1}])[0]
psbt = self.nodes[0].createpsbt([utxo], [{self.nodes[0].getnewaddress(): 0.9999}])
psbt = self.nodes[0].createpsbt([utxo], [{self.nodes[0].getnewaddress(): Decimal("0.9999")}])
signed = self.nodes[0].walletprocesspsbt(psbt)
rawtx = signed["hex"]
self.nodes[0].sendrawtransaction(rawtx)
Expand Down Expand Up @@ -1019,7 +1019,7 @@ def test_psbt_input_keys(psbt_input, keys):
utxo = self.create_outpoints(self.nodes[0], outputs=[{address: 1}])[0]
self.sync_all()

psbt = self.nodes[2].createpsbt([utxo], {self.nodes[0].getnewaddress(): 0.99999})
psbt = self.nodes[2].createpsbt([utxo], {self.nodes[0].getnewaddress(): Decimal("0.99999")})
decoded = self.nodes[2].decodepsbt(psbt)
test_psbt_input_keys(decoded['inputs'][0], [])

Expand Down
8 changes: 4 additions & 4 deletions test/functional/rpc_rawtransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ def sendrawtransaction_tests(self):
self.log.info("Test sendrawtransaction with missing input")
inputs = [{'txid': TXID, 'vout': 1}] # won't exist
address = getnewdestination()[2]
outputs = {address: 4.998}
outputs = {address: Decimal("4.998")}
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent", self.nodes[2].sendrawtransaction, rawtx)

Expand Down Expand Up @@ -382,7 +382,7 @@ def sendrawtransaction_tests(self):
tx_val = 0.001
tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
tx_hex = tx.serialize().hex()
assert_raises_rpc_error(-25, max_burn_exceeded, self.nodes[2].sendrawtransaction, tx_hex, 0, 0.0009)
assert_raises_rpc_error(-25, max_burn_exceeded, self.nodes[2].sendrawtransaction, tx_hex, 0, Decimal("0.0009"))

# Test a transaction where our burn falls short of maxburnamount
tx = self.wallet.create_self_transfer()['tx']
Expand All @@ -406,11 +406,11 @@ def sendrawtransaction_testmempoolaccept_tests(self):
# Fee rate is 0.00100000 BTC/kvB
tx = self.wallet.create_self_transfer(fee_rate=Decimal('0.00100000'))
# Thus, testmempoolaccept should reject
testres = self.nodes[2].testmempoolaccept([tx['hex']], 0.00001000)[0]
testres = self.nodes[2].testmempoolaccept([tx['hex']], Decimal("0.00001000"))[0]
assert_equal(testres['allowed'], False)
assert_equal(testres['reject-reason'], 'max-fee-exceeded')
# and sendrawtransaction should throw
assert_raises_rpc_error(-25, fee_exceeds_max, self.nodes[2].sendrawtransaction, tx['hex'], 0.00001000)
assert_raises_rpc_error(-25, fee_exceeds_max, self.nodes[2].sendrawtransaction, tx['hex'], Decimal("0.00001000"))
# and the following calls should both succeed
testres = self.nodes[2].testmempoolaccept(rawtxs=[tx['hex']])[0]
assert_equal(testres['allowed'], True)
Expand Down
2 changes: 1 addition & 1 deletion test/functional/rpc_signrawtransactionwithkey.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
{'txid': '83a4f6a6b73660e13ee6cb3c6063fa3759c50c9b7521d0536022961898f4fb02', 'vout': 0,
'scriptPubKey': '76a914669b857c03a5ed269d5d85a1ffac9ed5d663072788ac'},
]
OUTPUTS = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': 0.1}
OUTPUTS = {'mpLQjfK79b7CCV4VMJWEWAj5Mpx8Up5zxB': Decimal("0.1")}

class SignRawTransactionWithKeyTest(BitcoinTestFramework):
def set_test_params(self):
Expand Down
3 changes: 2 additions & 1 deletion test/functional/wallet_avoid_mixing_output_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
but still know when to expect mixing due to the wallet being close to empty.
"""
from decimal import Decimal

import random
from test_framework.test_framework import BitcoinTestFramework
Expand Down Expand Up @@ -172,7 +173,7 @@ def run_test(self):
self.generate(A, 1)
assert is_same_type(B, tx)

tx = self.make_payment(A, B, 30.99, random.choice(ADDRESS_TYPES))
tx = self.make_payment(A, B, Decimal("30.99"), random.choice(ADDRESS_TYPES))
assert not is_same_type(B, tx)


Expand Down
2 changes: 1 addition & 1 deletion test/functional/wallet_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def test_balances(*, fee_node_1=0):
before = self.nodes[1].getbalances()['mine']['untrusted_pending']
dst = self.nodes[1].getnewaddress()
self.nodes[1].unloadwallet(self.default_wallet_name)
self.nodes[0].sendtoaddress(dst, 0.1)
self.nodes[0].sendtoaddress(dst, Decimal('0.1'))
self.sync_all()
self.nodes[1].loadwallet(self.default_wallet_name)
after = self.nodes[1].getbalances()['mine']['untrusted_pending']
Expand Down
2 changes: 1 addition & 1 deletion test/functional/wallet_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ def run_test(self):
# 4. check if recipient (node0) can list the zero value tx
usp = self.nodes[1].listunspent(query_options={'minimumAmount': '49.998'})[0]
inputs = [{"txid": usp['txid'], "vout": usp['vout']}]
outputs = {self.nodes[1].getnewaddress(): 49.998, self.nodes[0].getnewaddress(): 11.11}
outputs = {self.nodes[1].getnewaddress(): Decimal("49.998"), self.nodes[0].getnewaddress(): Decimal("11.11")}

raw_tx = self.nodes[1].createrawtransaction(inputs, outputs).replace("c0833842", "00000000") # replace 11.11 with 0.0 (int32)
signed_raw_tx = self.nodes[1].signrawtransactionwithwallet(raw_tx)
Expand Down
Loading

0 comments on commit 8a185e1

Please sign in to comment.