Motivation
In functional tests, there are numerous instances of conversion code with patterns such as / COIN
and * COIN
that are converting between units satoshis to BTC.
Following details are as of the latest commit on master 2638fdb4f934be96b7c798dbac38ea5ab8a6374a.
Patterns stats
0# satoshis to BTC conversion
1➜ bitcoin git:(2638fdb4f9) ✗ git grep -n "/ COIN" -- '*.py' | wc -l
2 22
3
4# BTC to satoshis conversion
5➜ bitcoin git:(2638fdb4f9) ✗ git grep -n "* COIN" -- '*.py' | wc -l
6 127
Satoshis to BTC conversion instances
0test/functional/feature_fee_estimation.py:128: fee_rate=Decimal(feerate * 1000) / COIN,
1test/functional/feature_fee_estimation.py:301: high_feerate_kvb = Decimal(high_feerate) / COIN * 10 ** 3
2test/functional/feature_rbf.py:211: fee=(Decimal(fee) / COIN) * n,
3test/functional/feature_rbf.py:220: fee=(Decimal(fee) / COIN) * n + Decimal("0.1"),
4test/functional/feature_rbf.py:240: fee=2 * (Decimal(fee) / COIN) * n,
5test/functional/feature_rbf.py:356: fee=Decimal(fee) / COIN,
6test/functional/interface_usdt_mempool.py:172: tx = self.wallet.send_self_transfer(from_node=node, fee=fee / COIN)
7test/functional/interface_usdt_mempool.py:209: tx = self.wallet.send_self_transfer(from_node=node, fee=fee / COIN)
8test/functional/interface_usdt_mempool.py:258: from_node=node, utxo_to_spend=utxo, fee=original_fee / COIN
9test/functional/interface_usdt_mempool.py:265: from_node=node, utxo_to_spend=utxo, fee=replacement_fee / COIN
10test/functional/mempool_ephemeral_dust.py:39: result["new_utxos"][0]["value"] = Decimal(result["tx"].vout[0].nValue) / COIN
11test/functional/mempool_ephemeral_dust.py:48: result["new_utxos"].append({"txid": new_txid, "vout": len(result["tx"].vout) - 1, "value": Decimal(output_value) / COIN, "height": 0, "coinbase": False, "confirmations": 0})
12test/functional/mempool_package_rbf.py:166: incremental_sats_required = Decimal(package_3_size) / COIN
13test/functional/mining_prioritisetransaction.py:97: fee_delta_b = Decimal(9999) / COIN
14test/functional/mining_prioritisetransaction.py:98: fee_delta_c_1 = Decimal(-1234) / COIN
15test/functional/mining_prioritisetransaction.py:99: fee_delta_c_2 = Decimal(8888) / COIN
16test/functional/p2p_ibd_txrelay.py:31:MAX_FEE_FILTER = Decimal(9170997) / COIN
17test/functional/p2p_ibd_txrelay.py:32:NORMAL_FEE_FILTER = Decimal(100) / COIN
18test/functional/test_framework/messages.py:492: % (self.nValue // COIN, self.nValue % COIN,
19test/functional/test_framework/wallet.py:326: fee = Decimal(inputs_value_total - outputs_value_total) / COIN
20test/functional/test_framework/wallet.py:345: value=Decimal(tx.vout[i].nValue) / COIN,
21test/functional/wallet_import_rescan.py:280: variant.initial_amount = get_rand_amount(min_amount=((500 * 20 / COIN) + AMOUNT_DUST))
BTC to Satoshis conversion instances
0test/functional/feature_bip68_sequence.py:94: value = int((utxo["value"] - self.relayfee) * COIN)
1test/functional/feature_bip68_sequence.py:115: tx2.vout = [CTxOut(int(value - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
2test/functional/feature_bip68_sequence.py:203: tx.vout.append(CTxOut(int(value - self.relayfee * tx_size * COIN / 1000), SCRIPT_W0_SH_OP_TRUE))
3test/functional/feature_bip68_sequence.py:233: tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
4test/functional/feature_bip68_sequence.py:253: tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
5test/functional/feature_bip68_sequence.py:365: tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
6test/functional/feature_bip68_sequence.py:383: tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
7test/functional/feature_block.py:812: tx = self.create_and_sign_transaction(out[17], 51 * COIN)
8test/functional/feature_block.py:1162: tx77 = self.create_and_sign_transaction(out[24], 10 * COIN)
9test/functional/feature_block.py:1168: tx78 = self.create_tx(tx77, 0, 9 * COIN)
10test/functional/feature_block.py:1173: tx79 = self.create_tx(tx78, 0, 8 * COIN)
11test/functional/feature_coinstatsindex.py:155: amount=21 * COIN,
12test/functional/feature_coinstatsindex.py:164: tx2.vout = [CTxOut(int(Decimal(tx2_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
13test/functional/feature_coinstatsindex.py:192: cb.vout.append(CTxOut(5 * COIN, CScript([OP_FALSE])))
14test/functional/feature_dbcrash.py:192: input_amount = int(sum([utxo['value'] for utxo in utxos_to_spend]) * COIN)
15test/functional/feature_fee_estimation.py:60: tx.vout[0].nValue = int((total_in - amount - fee) * COIN)
16test/functional/feature_fee_estimation.py:62: tx.vout[1].nValue = int(amount * COIN)
17test/functional/feature_rbf.py:116: tx.vout[0].nValue -= int(0.1 * COIN)
18test/functional/feature_rbf.py:131: initial_nValue = 5 * COIN
19test/functional/feature_rbf.py:137: while remaining_value > 1 * COIN:
20test/functional/feature_rbf.py:138: remaining_value -= int(0.1 * COIN)
21test/functional/feature_rbf.py:160: dbl_tx.vout[0].nValue = int(0.1 * COIN)
22test/functional/feature_rbf.py:171: initial_nValue = 5 * COIN
23test/functional/feature_rbf.py:174: def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.00001 * COIN, _total_txs=None):
24test/functional/feature_rbf.py:202: fee = int(0.00001 * COIN)
25test/functional/feature_rbf.py:232: fee = int(0.00001 * COIN)
26test/functional/feature_rbf.py:250: tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
27test/functional/feature_rbf.py:273: utxo1 = self.make_utxo(self.nodes[0], int(1.2 * COIN))
28test/functional/feature_rbf.py:274: utxo2 = self.make_utxo(self.nodes[0], 3 * COIN)
29test/functional/feature_rbf.py:312: confirmed_utxo = self.make_utxo(self.nodes[0], int(1.1 * COIN))
30test/functional/feature_rbf.py:313: unconfirmed_utxo = self.make_utxo(self.nodes[0], int(0.1 * COIN), confirmed=False)
31test/functional/feature_rbf.py:325: amount_per_output=1 * COIN,
32test/functional/feature_rbf.py:337: initial_nValue = 10 * COIN
33test/functional/feature_rbf.py:339: fee = int(0.0001 * COIN)
34test/functional/feature_rbf.py:466: tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
35test/functional/feature_rbf.py:480: amount_per_output=int(0.00001 * COIN),
36test/functional/feature_rbf.py:487: self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1 * COIN))
37test/functional/feature_rbf.py:495: tx1_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
38test/functional/feature_rbf.py:516: self.nodes[0].prioritisetransaction(txid=tx2b["txid"], fee_delta=int(0.1 * COIN))
39test/functional/feature_rbf.py:557: confirmed_utxo = self.make_utxo(self.nodes[0], int(2 * COIN))
40test/functional/feature_segwit.py:279: tx.vout.append(CTxOut(int(49.99 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
41test/functional/feature_segwit.py:295: tx.vout.append(CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE]))) # Huge fee
42test/functional/interface_rest.py:99: txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=int(0.1
43 * COIN))["txid"]
44test/functional/interface_rest.py:176: txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=int(0.
451 * COIN))["txid"]
46test/functional/interface_usdt_utxocache.py:195: assert_equal(50 * COIN, event.value)
47test/functional/interface_usdt_utxocache.py:301: "value": int(prevout_tx["vout"][vin["vout"]]["value"] * COIN),
48test/functional/interface_usdt_utxocache.py:310: "value": int(vout["value"] * COIN),
49test/functional/mempool_accept.py:91: tx.vout[0].nValue = int(0.3 * COIN)
50test/functional/mempool_accept.py:92: tx.vout[1].nValue = int(49 * COIN)
51test/functional/mempool_accept.py:120: tx.vout[0].nValue = int((Decimal('0.3') - fee) * COIN)
52test/functional/mempool_accept.py:134: tx.vout[0].nValue = int(output_amount * COIN)
53test/functional/mempool_accept.py:156: tx.vout[0].nValue -= int(fee * COIN) # Double the fee
54test/functional/mempool_accept.py:184: tx.vout[0].nValue = int(0.1 * COIN)
55test/functional/mempool_accept.py:202: tx.vout[0].nValue = int(0.05 * COIN)
56test/functional/mempool_accept_wtxid.py:59: parent.vout.append(CTxOut(int(9.99998 * COIN), script_pubkey))
57test/functional/mempool_accept_wtxid.py:76: child_one.vout.append(CTxOut(int(9.99996 * COIN), child_script_pubkey))
58test/functional/mempool_dust.py:53: dust_threshold = int(get_fee(tx_size, dust_relay_fee) * COIN)
59test/functional/mempool_limit.py:254: cpfp_satoshis = int(cpfp_fee * COIN) + magic_satoshis
60test/functional/mempool_limit.py:323: child = self.wallet.create_self_transfer_multi(utxos_to_spend=parent_utxos, fee_per_output=int(cpfp_fee
61 * COIN))
62test/functional/mempool_limit.py:372: node.prioritisetransaction(tx_rich["txid"], 0, int(DEFAULT_FEE * COIN))
63test/functional/mempool_package_rbf.py:65: fee_per_output=int(child_fee * COIN // num_child_outputs),
64test/functional/mempool_package_rbf.py:281: fee_per_output=int(DEFAULT_CHILD_FEE * COIN),
65test/functional/mempool_package_rbf.py:314: fee_per_output=int(DEFAULT_FEE * COIN),
66test/functional/mempool_package_rbf.py:321: fee_per_output=int(DEFAULT_FEE * COIN),
67test/functional/mempool_package_rbf.py:367: fee_per_output=int(DEFAULT_FEE * COIN),
68test/functional/mempool_package_rbf.py:374: fee_per_output=int(DEFAULT_FEE * COIN),
69test/functional/mempool_package_rbf.py:412: fee_per_output=int(DEFAULT_FEE * COIN),
70test/functional/mempool_package_rbf.py:420: fee_per_output=int(DEFAULT_FEE * COIN),
71test/functional/mempool_package_rbf.py:427: fee_per_output=int(DEFAULT_FEE * COIN),
72test/functional/mempool_package_rbf.py:491: fee_per_output=int(DEFAULT_CHILD_FEE * COIN),
73test/functional/mempool_package_rbf.py:579: fee_per_output=int(DEFAULT_CHILD_FEE * COIN),
74test/functional/mempool_persist.py:233: self.nodes[0].prioritisetransaction(tx_node01_secret["txid"], 0, 2 * COIN)
75test/functional/mempool_persist.py:234: self.nodes[1].prioritisetransaction(tx_node01_secret["txid"], 0, 3 * COIN)
76test/functional/mempool_truc.py:551: fee_to_beat = max(int(tx_v3_child_2["fee"] * COIN), int(tx_unrelated_replacee["fee"]*COIN))
77test/functional/mining_prioritisetransaction.py:48: assert_equal(self.nodes[0].getprioritisedtransactions(), { tx_replacee["txid"] : { "fee_d
78elta" : 100, "in_mempool" : True, "modified_fee": int(tx_replacee["fee"] * COIN + 100)}})
79test/functional/mining_prioritisetransaction.py:56: assert_equal(self.nodes[0].getprioritisedtransactions(), { tx_replacee["txid"] : { "fee_d
80elta" : COIN + 100, "in_mempool" : True, "modified_fee": int(tx_replacee["fee"] * COIN + COIN + 100)}})
81test/functional/mining_prioritisetransaction.py:100: self.nodes[0].prioritisetransaction(txid=txid_b, fee_delta=int(fee_delta_b * COIN))
82test/functional/mining_prioritisetransaction.py:101: self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_1 * COIN))
83test/functional/mining_prioritisetransaction.py:102: self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_2 * COIN))
84test/functional/mining_prioritisetransaction.py:114: assert_equal(self.nodes[0].getprioritisedtransactions(), {txid_b: {"fee_delta" : fee_del
85ta_b*COIN, "in_mempool" : True, "modified_fee": int(fee_delta_b*COIN + COIN * tx_o_b["fee"])}, txid_c: {"fee_delta" : (fee_delta_c_1 + fee_delta_c_2
86)*COIN, "in_mempool" : True, "modified_fee": int((fee_delta_c_1 + fee_delta_c_2 ) * COIN + COIN * tx_o_c["fee"])}})
87test/functional/mining_prioritisetransaction.py:124: self.nodes[0].prioritisetransaction(txid=txid_b, fee_delta=int(fee_delta_b * COIN))
88test/functional/mining_prioritisetransaction.py:125: self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_1 * COIN))
89test/functional/mining_prioritisetransaction.py:126: self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_2 * COIN))
90test/functional/mining_prioritisetransaction.py:133: assert_equal(self.nodes[0].getprioritisedtransactions(), {txid_b: {"fee_delta" : fee_del
91ta_b*COIN, "in_mempool" : True, "modified_fee": int(fee_delta_b*COIN + COIN * tx_o_b["fee"])}, txid_c: {"fee_delta" : (fee_delta_c_1 + fee_delta_c_2
92)*COIN, "in_mempool" : True, "modified_fee": int((fee_delta_c_1 + fee_delta_c_2 ) * COIN + COIN * tx_o_c["fee"])}})
93test/functional/p2p_filter.py:141: rel_txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amou
94nt=1 * COIN)["txid"]
95test/functional/p2p_filter.py:142: irr_result = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=2 *
96COIN)
97test/functional/p2p_filter.py:160: self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN
98)
99test/functional/p2p_filter.py:189: self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=7 * COIN)
100test/functional/p2p_filter.py:196: txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9
101 * COIN)["txid"]
102test/functional/p2p_filter.py:203: txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=7 * CO
103IN)["txid"]
104test/functional/p2p_invalid_block.py:72: tx1 = create_tx_with_script(block1.vtx[0], 0, script_sig=bytes([OP_TRUE]), amount=50 * COIN)
105test/functional/p2p_invalid_block.py:73: tx2 = create_tx_with_script(tx1, 0, script_sig=bytes([OP_TRUE]), amount=50 * COIN)
106test/functional/p2p_invalid_block.py:120: tx3 = create_tx_with_script(tx2, 0, script_sig=bytes([OP_TRUE]), amount=50 * COIN)
107test/functional/p2p_invalid_tx.py:95: tx_withhold.vout = [CTxOut(nValue=25 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 2
108test/functional/p2p_invalid_tx.py:101: tx_orphan_1.vout = [CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3
109test/functional/p2p_invalid_tx.py:107: tx_orphan_2_no_fee.vout.append(CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
110test/functional/p2p_invalid_tx.py:112: tx_orphan_2_valid.vout.append(CTxOut(nValue=8 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
111test/functional/p2p_invalid_tx.py:118: tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
112test/functional/p2p_invalid_tx.py:154: orphan_tx_pool[i].vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
113test/functional/p2p_invalid_tx.py:162: rejected_parent.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
114test/functional/p2p_invalid_tx.py:174: tx_withhold_until_block_A.vout = [CTxOut(nValue=12 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 2
115test/functional/p2p_invalid_tx.py:179: tx_orphan_include_by_block_A.vout.append(CTxOut(nValue=12 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_
116OP_TRUE))
117test/functional/p2p_invalid_tx.py:199: tx_withhold_until_block_B.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
118test/functional/p2p_invalid_tx.py:204: tx_orphan_include_by_block_B.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)
119)
120test/functional/p2p_invalid_tx.py:209: tx_orphan_conflict_by_block_B.vout.append(CTxOut(nValue=9 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)
121)
122test/functional/rpc_blockchain.py:679: tx = create_tx_with_script(block.vtx[0], 0, script_sig=bytes([OP_TRUE]), amount=50 * COIN)
123test/functional/rpc_createmultisig.py:159: tx = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=spk, amount=int(value * COIN))
124test/functional/rpc_packages.py:248: fee_per_output=int(DEFAULT_FEE * 5 * COIN),
125test/functional/rpc_rawtransaction.py:362: tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
126test/functional/rpc_rawtransaction.py:369: tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_FALSE] * 10001))]
127test/functional/rpc_rawtransaction.py:376: tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_INVALIDOPCODE]))]
128test/functional/rpc_rawtransaction.py:383: tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
129test/functional/rpc_rawtransaction.py:390: tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
130test/functional/rpc_rawtransaction.py:397: tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
131test/functional/rpc_scanblocks.py:34: wallet.send_to(from_node=node, scriptPubKey=spk_1, amount=1 * COIN)
132test/functional/rpc_scanblocks.py:41: amount=1 * COIN)
133test/functional/rpc_signrawtransactionwithkey.py:53: tx = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=script_pub_key, amount=in
134t(amount * COIN))
135test/functional/test_framework/blocktools.py:137: coinbaseoutput.nValue = nValue * COIN
136test/functional/test_framework/messages.py:40:MAX_MONEY = 21000000 * COIN
137test/functional/test_framework/messages.py:665: if tout.nValue < 0 or tout.nValue > 21000000 * COIN:
138test/functional/wallet_assumeutxo.py:122: self.mini_wallet.send_to(from_node=n0, scriptPubKey=w_skp, amount=1 * COIN)
139test/functional/wallet_assumeutxo.py:123: self.mini_wallet.send_to(from_node=n0, scriptPubKey=w2_skp, amount=10 * COIN)
140test/functional/wallet_basic.py:817: assert_equal(this_unspent['ancestorfees'], ancestor_fees * COIN)
141test/functional/wallet_fundrawtransaction.py:167: tx.vout = [CTxOut(1 * COIN, bytearray(address_to_scriptpubkey(address)))] * 2
142test/functional/wallet_fundrawtransaction.py:1304: assert_equal(fundedtx['fee'] * COIN, tx_size * 10)
143test/functional/wallet_fundrawtransaction.py:1312: assert_equal(fundedtx['fee'] * COIN, tx_size * 10)
144test/functional/wallet_listtransactions.py:198: tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee
Possible solution
A possible solution that has garnered some support is to add utility functions in the functional tests such as sat_to_btc()
and conversely btc_to_sat()
that can get rid of this repetitive inline conversion and make the testing code more readable.
Prior discussion: #30079 (review)
Useful Skills
- Compiling Bitcoin Core from source
- Running the C++ unit tests and the Python functional tests
- Basic Python skills
Guidance for new contributors
Want to work on this issue?
For guidance on contributing, please read CONTRIBUTING.md before opening your pull request.