Skip to content

Add sat_to_btc() and conversely btc_to_sat() util functions in functional tests #31345

@rkrux

Description

@rkrux

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 2638fdb.

Patterns stats

# satoshis to BTC conversion
➜  bitcoin git:(2638fdb4f9) ✗ git grep -n "/ COIN" -- '*.py' | wc -l
      22

# BTC to satoshis conversion
➜  bitcoin git:(2638fdb4f9) ✗ git grep -n "* COIN" -- '*.py' | wc -l
     127

Satoshis to BTC conversion instances

git grep -n "/ COIN" -- '*.py'
test/functional/feature_fee_estimation.py:128:        fee_rate=Decimal(feerate * 1000) / COIN,
test/functional/feature_fee_estimation.py:301:        high_feerate_kvb = Decimal(high_feerate) / COIN * 10 ** 3
test/functional/feature_rbf.py:211:            fee=(Decimal(fee) / COIN) * n,
test/functional/feature_rbf.py:220:            fee=(Decimal(fee) / COIN) * n + Decimal("0.1"),
test/functional/feature_rbf.py:240:                fee=2 * (Decimal(fee) / COIN) * n,
test/functional/feature_rbf.py:356:                fee=Decimal(fee) / COIN,
test/functional/interface_usdt_mempool.py:172:        tx = self.wallet.send_self_transfer(from_node=node, fee=fee / COIN)
test/functional/interface_usdt_mempool.py:209:        tx = self.wallet.send_self_transfer(from_node=node, fee=fee / COIN)
test/functional/interface_usdt_mempool.py:258:            from_node=node, utxo_to_spend=utxo, fee=original_fee / COIN
test/functional/interface_usdt_mempool.py:265:            from_node=node, utxo_to_spend=utxo, fee=replacement_fee / COIN
test/functional/mempool_ephemeral_dust.py:39:        result["new_utxos"][0]["value"] = Decimal(result["tx"].vout[0].nValue) / COIN
test/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})
test/functional/mempool_package_rbf.py:166:        incremental_sats_required = Decimal(package_3_size) / COIN
test/functional/mining_prioritisetransaction.py:97:        fee_delta_b = Decimal(9999) / COIN
test/functional/mining_prioritisetransaction.py:98:        fee_delta_c_1 = Decimal(-1234) / COIN
test/functional/mining_prioritisetransaction.py:99:        fee_delta_c_2 = Decimal(8888) / COIN
test/functional/p2p_ibd_txrelay.py:31:MAX_FEE_FILTER = Decimal(9170997) / COIN
test/functional/p2p_ibd_txrelay.py:32:NORMAL_FEE_FILTER = Decimal(100) / COIN
test/functional/test_framework/messages.py:492:            % (self.nValue // COIN, self.nValue % COIN,
test/functional/test_framework/wallet.py:326:        fee = Decimal(inputs_value_total - outputs_value_total) / COIN
test/functional/test_framework/wallet.py:345:                value=Decimal(tx.vout[i].nValue) / COIN,
test/functional/wallet_import_rescan.py:280:            variant.initial_amount = get_rand_amount(min_amount=((500 * 20 / COIN) + AMOUNT_DUST))

BTC to Satoshis conversion instances

git grep -n "* COIN" -- '*.py'
test/functional/feature_bip68_sequence.py:94:        value = int((utxo["value"] - self.relayfee) * COIN)
test/functional/feature_bip68_sequence.py:115:        tx2.vout = [CTxOut(int(value - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
test/functional/feature_bip68_sequence.py:203:            tx.vout.append(CTxOut(int(value - self.relayfee * tx_size * COIN / 1000), SCRIPT_W0_SH_OP_TRUE))
test/functional/feature_bip68_sequence.py:233:        tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
test/functional/feature_bip68_sequence.py:253:            tx.vout = [CTxOut(int(orig_tx.vout[0].nValue - relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
test/functional/feature_bip68_sequence.py:365:        tx2.vout = [CTxOut(int(tx1.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
test/functional/feature_bip68_sequence.py:383:        tx3.vout = [CTxOut(int(tx2.vout[0].nValue - self.relayfee * COIN), SCRIPT_W0_SH_OP_TRUE)]
test/functional/feature_block.py:812:        tx = self.create_and_sign_transaction(out[17], 51 * COIN)
test/functional/feature_block.py:1162:        tx77 = self.create_and_sign_transaction(out[24], 10 * COIN)
test/functional/feature_block.py:1168:        tx78 = self.create_tx(tx77, 0, 9 * COIN)
test/functional/feature_block.py:1173:        tx79 = self.create_tx(tx78, 0, 8 * COIN)
test/functional/feature_coinstatsindex.py:155:            amount=21 * COIN,
test/functional/feature_coinstatsindex.py:164:        tx2.vout = [CTxOut(int(Decimal(tx2_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
test/functional/feature_coinstatsindex.py:192:        cb.vout.append(CTxOut(5 * COIN, CScript([OP_FALSE])))
test/functional/feature_dbcrash.py:192:            input_amount = int(sum([utxo['value'] for utxo in utxos_to_spend]) * COIN)
test/functional/feature_fee_estimation.py:60:    tx.vout[0].nValue = int((total_in - amount - fee) * COIN)
test/functional/feature_fee_estimation.py:62:    tx.vout[1].nValue = int(amount * COIN)
test/functional/feature_rbf.py:116:        tx.vout[0].nValue -= int(0.1 * COIN)
test/functional/feature_rbf.py:131:        initial_nValue = 5 * COIN
test/functional/feature_rbf.py:137:        while remaining_value > 1 * COIN:
test/functional/feature_rbf.py:138:            remaining_value -= int(0.1 * COIN)
test/functional/feature_rbf.py:160:        dbl_tx.vout[0].nValue = int(0.1 * COIN)
test/functional/feature_rbf.py:171:        initial_nValue = 5 * COIN
test/functional/feature_rbf.py:174:        def branch(prevout, initial_value, max_txs, tree_width=5, fee=0.00001 * COIN, _total_txs=None):
test/functional/feature_rbf.py:202:        fee = int(0.00001 * COIN)
test/functional/feature_rbf.py:232:            fee = int(0.00001 * COIN)
test/functional/feature_rbf.py:250:        tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
test/functional/feature_rbf.py:273:        utxo1 = self.make_utxo(self.nodes[0], int(1.2 * COIN))
test/functional/feature_rbf.py:274:        utxo2 = self.make_utxo(self.nodes[0], 3 * COIN)
test/functional/feature_rbf.py:312:        confirmed_utxo = self.make_utxo(self.nodes[0], int(1.1 * COIN))
test/functional/feature_rbf.py:313:        unconfirmed_utxo = self.make_utxo(self.nodes[0], int(0.1 * COIN), confirmed=False)
test/functional/feature_rbf.py:325:            amount_per_output=1 * COIN,
test/functional/feature_rbf.py:337:        initial_nValue = 10 * COIN
test/functional/feature_rbf.py:339:        fee = int(0.0001 * COIN)
test/functional/feature_rbf.py:466:        tx0_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
test/functional/feature_rbf.py:480:            amount_per_output=int(0.00001 * COIN),
test/functional/feature_rbf.py:487:        self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1 * COIN))
test/functional/feature_rbf.py:495:        tx1_outpoint = self.make_utxo(self.nodes[0], int(1.1 * COIN))
test/functional/feature_rbf.py:516:        self.nodes[0].prioritisetransaction(txid=tx2b["txid"], fee_delta=int(0.1 * COIN))
test/functional/feature_rbf.py:557:        confirmed_utxo = self.make_utxo(self.nodes[0], int(2 * COIN))
test/functional/feature_segwit.py:279:        tx.vout.append(CTxOut(int(49.99 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))
test/functional/feature_segwit.py:295:        tx.vout.append(CTxOut(int(49.95 * COIN), CScript([OP_TRUE, OP_DROP] * 15 + [OP_TRUE])))  # Huge fee
test/functional/interface_rest.py:99:        txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=int(0.1
 * COIN))["txid"]
test/functional/interface_rest.py:176:        txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=int(0.
1 * COIN))["txid"]
test/functional/interface_usdt_utxocache.py:195:                assert_equal(50 * COIN, event.value)
test/functional/interface_usdt_utxocache.py:301:                        "value": int(prevout_tx["vout"][vin["vout"]]["value"] * COIN),
test/functional/interface_usdt_utxocache.py:310:                        "value": int(vout["value"] * COIN),
test/functional/mempool_accept.py:91:        tx.vout[0].nValue = int(0.3 * COIN)
test/functional/mempool_accept.py:92:        tx.vout[1].nValue = int(49 * COIN)
test/functional/mempool_accept.py:120:        tx.vout[0].nValue = int((Decimal('0.3') - fee) * COIN)
test/functional/mempool_accept.py:134:        tx.vout[0].nValue = int(output_amount * COIN)
test/functional/mempool_accept.py:156:        tx.vout[0].nValue -= int(fee * COIN)  # Double the fee
test/functional/mempool_accept.py:184:        tx.vout[0].nValue = int(0.1 * COIN)
test/functional/mempool_accept.py:202:        tx.vout[0].nValue = int(0.05 * COIN)
test/functional/mempool_accept_wtxid.py:59:        parent.vout.append(CTxOut(int(9.99998 * COIN), script_pubkey))
test/functional/mempool_accept_wtxid.py:76:        child_one.vout.append(CTxOut(int(9.99996 * COIN), child_script_pubkey))
test/functional/mempool_dust.py:53:            dust_threshold = int(get_fee(tx_size, dust_relay_fee) * COIN)
test/functional/mempool_limit.py:254:        cpfp_satoshis = int(cpfp_fee * COIN) + magic_satoshis
test/functional/mempool_limit.py:323:        child = self.wallet.create_self_transfer_multi(utxos_to_spend=parent_utxos, fee_per_output=int(cpfp_fee
 * COIN))
test/functional/mempool_limit.py:372:        node.prioritisetransaction(tx_rich["txid"], 0, int(DEFAULT_FEE * COIN))
test/functional/mempool_package_rbf.py:65:            fee_per_output=int(child_fee * COIN // num_child_outputs),
test/functional/mempool_package_rbf.py:281:            fee_per_output=int(DEFAULT_CHILD_FEE * COIN),
test/functional/mempool_package_rbf.py:314:            fee_per_output=int(DEFAULT_FEE * COIN),
test/functional/mempool_package_rbf.py:321:            fee_per_output=int(DEFAULT_FEE * COIN),
test/functional/mempool_package_rbf.py:367:            fee_per_output=int(DEFAULT_FEE * COIN),
test/functional/mempool_package_rbf.py:374:            fee_per_output=int(DEFAULT_FEE * COIN),
test/functional/mempool_package_rbf.py:412:            fee_per_output=int(DEFAULT_FEE * COIN),
test/functional/mempool_package_rbf.py:420:            fee_per_output=int(DEFAULT_FEE * COIN),
test/functional/mempool_package_rbf.py:427:            fee_per_output=int(DEFAULT_FEE * COIN),
test/functional/mempool_package_rbf.py:491:            fee_per_output=int(DEFAULT_CHILD_FEE * COIN),
test/functional/mempool_package_rbf.py:579:            fee_per_output=int(DEFAULT_CHILD_FEE * COIN),
test/functional/mempool_persist.py:233:        self.nodes[0].prioritisetransaction(tx_node01_secret["txid"], 0, 2 * COIN)
test/functional/mempool_persist.py:234:        self.nodes[1].prioritisetransaction(tx_node01_secret["txid"], 0, 3 * COIN)
test/functional/mempool_truc.py:551:        fee_to_beat = max(int(tx_v3_child_2["fee"] * COIN), int(tx_unrelated_replacee["fee"]*COIN))
test/functional/mining_prioritisetransaction.py:48:        assert_equal(self.nodes[0].getprioritisedtransactions(), { tx_replacee["txid"] : { "fee_d
elta" : 100, "in_mempool" : True, "modified_fee": int(tx_replacee["fee"] * COIN + 100)}})
test/functional/mining_prioritisetransaction.py:56:        assert_equal(self.nodes[0].getprioritisedtransactions(), { tx_replacee["txid"] : { "fee_d
elta" : COIN + 100, "in_mempool" : True, "modified_fee": int(tx_replacee["fee"] * COIN + COIN + 100)}})
test/functional/mining_prioritisetransaction.py:100:        self.nodes[0].prioritisetransaction(txid=txid_b, fee_delta=int(fee_delta_b * COIN))
test/functional/mining_prioritisetransaction.py:101:        self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_1 * COIN))
test/functional/mining_prioritisetransaction.py:102:        self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_2 * COIN))
test/functional/mining_prioritisetransaction.py:114:        assert_equal(self.nodes[0].getprioritisedtransactions(), {txid_b: {"fee_delta" : fee_del
ta_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
)*COIN, "in_mempool" : True, "modified_fee": int((fee_delta_c_1 + fee_delta_c_2 ) * COIN + COIN * tx_o_c["fee"])}})
test/functional/mining_prioritisetransaction.py:124:        self.nodes[0].prioritisetransaction(txid=txid_b, fee_delta=int(fee_delta_b * COIN))
test/functional/mining_prioritisetransaction.py:125:        self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_1 * COIN))
test/functional/mining_prioritisetransaction.py:126:        self.nodes[0].prioritisetransaction(txid=txid_c, fee_delta=int(fee_delta_c_2 * COIN))
test/functional/mining_prioritisetransaction.py:133:        assert_equal(self.nodes[0].getprioritisedtransactions(), {txid_b: {"fee_delta" : fee_del
ta_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
)*COIN, "in_mempool" : True, "modified_fee": int((fee_delta_c_1 + fee_delta_c_2 ) * COIN + COIN * tx_o_c["fee"])}})
test/functional/p2p_filter.py:141:        rel_txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amou
nt=1 * COIN)["txid"]
test/functional/p2p_filter.py:142:        irr_result = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=2 *
COIN)
test/functional/p2p_filter.py:160:        self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9 * COIN
)
test/functional/p2p_filter.py:189:        self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=7 * COIN)
test/functional/p2p_filter.py:196:        txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=filter_peer.watch_script_pubkey, amount=9
 * COIN)["txid"]
test/functional/p2p_filter.py:203:            txid = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=getnewdestination()[1], amount=7 * CO
IN)["txid"]
test/functional/p2p_invalid_block.py:72:        tx1 = create_tx_with_script(block1.vtx[0], 0, script_sig=bytes([OP_TRUE]), amount=50 * COIN)
test/functional/p2p_invalid_block.py:73:        tx2 = create_tx_with_script(tx1, 0, script_sig=bytes([OP_TRUE]), amount=50 * COIN)
test/functional/p2p_invalid_block.py:120:        tx3 = create_tx_with_script(tx2, 0, script_sig=bytes([OP_TRUE]), amount=50 * COIN)
test/functional/p2p_invalid_tx.py:95:        tx_withhold.vout = [CTxOut(nValue=25 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 2
test/functional/p2p_invalid_tx.py:101:        tx_orphan_1.vout = [CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 3
test/functional/p2p_invalid_tx.py:107:        tx_orphan_2_no_fee.vout.append(CTxOut(nValue=8 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
test/functional/p2p_invalid_tx.py:112:        tx_orphan_2_valid.vout.append(CTxOut(nValue=8 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
test/functional/p2p_invalid_tx.py:118:        tx_orphan_2_invalid.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
test/functional/p2p_invalid_tx.py:154:            orphan_tx_pool[i].vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
test/functional/p2p_invalid_tx.py:162:        rejected_parent.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
test/functional/p2p_invalid_tx.py:174:        tx_withhold_until_block_A.vout = [CTxOut(nValue=12 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)] * 2
test/functional/p2p_invalid_tx.py:179:        tx_orphan_include_by_block_A.vout.append(CTxOut(nValue=12 * COIN - 12000, scriptPubKey=SCRIPT_PUB_KEY_
OP_TRUE))
test/functional/p2p_invalid_tx.py:199:        tx_withhold_until_block_B.vout.append(CTxOut(nValue=11 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE))
test/functional/p2p_invalid_tx.py:204:        tx_orphan_include_by_block_B.vout.append(CTxOut(nValue=10 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)
)
test/functional/p2p_invalid_tx.py:209:        tx_orphan_conflict_by_block_B.vout.append(CTxOut(nValue=9 * COIN, scriptPubKey=SCRIPT_PUB_KEY_OP_TRUE)
)
test/functional/rpc_blockchain.py:679:        tx = create_tx_with_script(block.vtx[0], 0, script_sig=bytes([OP_TRUE]), amount=50 * COIN)
test/functional/rpc_createmultisig.py:159:        tx = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=spk, amount=int(value * COIN))
test/functional/rpc_packages.py:248:            fee_per_output=int(DEFAULT_FEE * 5 * COIN),
test/functional/rpc_rawtransaction.py:362:        tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
test/functional/rpc_rawtransaction.py:369:        tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_FALSE] * 10001))]
test/functional/rpc_rawtransaction.py:376:        tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_INVALIDOPCODE]))]
test/functional/rpc_rawtransaction.py:383:        tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
test/functional/rpc_rawtransaction.py:390:        tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
test/functional/rpc_rawtransaction.py:397:        tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
test/functional/rpc_scanblocks.py:34:        wallet.send_to(from_node=node, scriptPubKey=spk_1, amount=1 * COIN)
test/functional/rpc_scanblocks.py:41:                       amount=1 * COIN)
test/functional/rpc_signrawtransactionwithkey.py:53:        tx = self.wallet.send_to(from_node=self.nodes[0], scriptPubKey=script_pub_key, amount=in
t(amount * COIN))
test/functional/test_framework/blocktools.py:137:    coinbaseoutput.nValue = nValue * COIN
test/functional/test_framework/messages.py:40:MAX_MONEY = 21000000 * COIN
test/functional/test_framework/messages.py:665:            if tout.nValue < 0 or tout.nValue > 21000000 * COIN:
test/functional/wallet_assumeutxo.py:122:                self.mini_wallet.send_to(from_node=n0, scriptPubKey=w_skp, amount=1 * COIN)
test/functional/wallet_assumeutxo.py:123:                self.mini_wallet.send_to(from_node=n0, scriptPubKey=w2_skp, amount=10 * COIN)
test/functional/wallet_basic.py:817:            assert_equal(this_unspent['ancestorfees'], ancestor_fees * COIN)
test/functional/wallet_fundrawtransaction.py:167:        tx.vout = [CTxOut(1 * COIN, bytearray(address_to_scriptpubkey(address)))] * 2
test/functional/wallet_fundrawtransaction.py:1304:        assert_equal(fundedtx['fee'] * COIN, tx_size * 10)
test/functional/wallet_fundrawtransaction.py:1312:        assert_equal(fundedtx['fee'] * COIN, tx_size * 10)
test/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 (comment)

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions