From 1027d57077e42b2e63504e4861c6611390b3c8e0 Mon Sep 17 00:00:00 2001 From: diegobianqui Date: Tue, 24 Feb 2026 11:18:08 -0300 Subject: [PATCH 1/2] Easy challenges 00 and 01 solutions --- .gitignore | 3 + test/functional/feature_hello_world.py | 148 +++++++++++++++++++++++++ test/functional/test_runner.py | 2 + test/functional/wallet_rpc_basics.py | 51 +++++++++ 4 files changed, 204 insertions(+) create mode 100755 test/functional/feature_hello_world.py create mode 100755 test/functional/wallet_rpc_basics.py diff --git a/.gitignore b/.gitignore index b92988f6a3..58994508a1 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ target/ /guix-build-* /ci/scratch/ + +# exercises +/_exercises \ No newline at end of file diff --git a/test/functional/feature_hello_world.py b/test/functional/feature_hello_world.py new file mode 100755 index 0000000000..d622912799 --- /dev/null +++ b/test/functional/feature_hello_world.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017-present The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""An example functional test + +The module-level docstring should include a high-level description of +what the test is doing. It's the first thing people see when they open +the file and should give the reader information about *what* the test +is testing and *how* it's being tested +""" +# Imports should be in PEP8 ordering (std library first, then third party +# libraries then local imports). +from collections import defaultdict + +# Avoid wildcard * imports +# Use lexicographically sorted multi-line imports +from test_framework.blocktools import ( + create_block, + create_coinbase, +) +from test_framework.messages import ( + CInv, + MSG_BLOCK, +) +from test_framework.p2p import ( + P2PInterface, + msg_block, + msg_getdata, + p2p_lock, +) +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import ( + assert_equal, +) + +# P2PInterface is a class containing callbacks to be executed when a P2P +# message is received from the node-under-test. Subclass P2PInterface and +# override the on_*() methods if you need custom behaviour. +class BaseNode(P2PInterface): + def __init__(self): + """Initialize the P2PInterface + + Used to initialize custom properties for the Node that aren't + included by default in the base class. Be aware that the P2PInterface + base class already stores a counter for each P2P message type and the + last received message of each type, which should be sufficient for the + needs of most tests. + + Call super().__init__() first for standard initialization and then + initialize custom properties.""" + super().__init__() + # Stores a dictionary of all blocks received + self.block_receive_map = defaultdict(int) + + def on_block(self, message): + """Override the standard on_block callback + + Store the hash of a received block in the dictionary.""" + self.block_receive_map[message.block.hash_int] += 1 + + def on_inv(self, message): + """Override the standard on_inv callback""" + pass + +def custom_function(): + """Do some custom behaviour + + If this function is more generally useful for other tests, consider + moving it to a module in test_framework.""" + # self.log.info("running custom_function") # Oops! Can't run self.log outside the BitcoinTestFramework + pass + + +class ExampleTest(BitcoinTestFramework): + # Each functional test is a subclass of the BitcoinTestFramework class. + + # Override the set_test_params(), skip_test_if_missing_module(), add_options(), setup_chain(), setup_network() + # and setup_nodes() methods to customize the test setup as required. + + def set_test_params(self): + """Override test parameters for your individual test. + + This method must be overridden and num_nodes must be explicitly set.""" + # By default every test loads a pre-mined chain of 200 blocks from cache. + # Set setup_clean_chain to True to skip this and start from the Genesis + # block. + self.setup_clean_chain = True + self.num_nodes = 3 + # Use self.extra_args to change command-line arguments for the nodes + self.extra_args = [[], ["-logips"], []] + + # self.log.info("I've finished set_test_params") # Oops! Can't run self.log before run_test() + + # Use skip_test_if_missing_module() to skip the test if your test requires certain modules to be present. + # This test uses generate which requires wallet to be compiled + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + # Use add_options() to add specific command-line options for your test. + # In practice this is not used very much, since the tests are mostly written + # to be run in automated environments without command-line options. + # def add_options() + # pass + + # Use setup_chain() to customize the node data directories. In practice + # this is not used very much since the default behaviour is almost always + # fine + # def setup_chain(): + # pass + + def setup_network(self): + """Setup the test network topology + + Often you won't need to override this, since the standard network topology + (linear: node0 <-> node1 <-> node2 <-> ...) is fine for most tests. + + If you do override this method, remember to start the nodes, assign + them to self.nodes, connect them and then sync.""" + + self.setup_nodes() + + # In this test, we're not connecting node2 to node0 or node1. Calls to + # sync_all() should not include node2, since we're not expecting it to + # sync. + self.connect_nodes(0, 1) + self.sync_all(self.nodes[0:2]) + + # Use setup_nodes() to customize the node start behaviour (for example if + # you don't want to start all nodes at the start of the test). + # def setup_nodes(): + # pass + + def custom_method(self): + """Do some custom behaviour for this test + + Define it in a method here because you're going to use it repeatedly. + If you think it's useful in general, consider moving it to the base + BitcoinTestFramework class so other tests can use it.""" + + self.log.info("Running custom_method") + + def run_test(self): + self.log.info("Hello Brazil!") + + +if __name__ == '__main__': + ExampleTest(__file__).main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index a4052f9102..5152356aaf 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -122,8 +122,10 @@ 'wallet_bumpfee.py', 'wallet_v3_txs.py', 'wallet_backup.py', + 'wallet_rpc_basics.py', 'feature_segwit.py --v2transport', 'feature_segwit.py --v1transport', + 'feature_hello_world.py', 'p2p_tx_download.py', 'wallet_avoidreuse.py', 'feature_abortnode.py', diff --git a/test/functional/wallet_rpc_basics.py b/test/functional/wallet_rpc_basics.py new file mode 100755 index 0000000000..410bd9857e --- /dev/null +++ b/test/functional/wallet_rpc_basics.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +from decimal import Decimal +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + +class WalletRPCBasics(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 2 + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + + def run_test(self): + # Step 2: Create wallets and get RPC handles + self.nodes[0].createwallet("node0_wallet") + node0_wallet = self.nodes[0].get_wallet_rpc("node0_wallet") + self.nodes[1].createwallet("node1_wallet") + node1_wallet = self.nodes[1].get_wallet_rpc("node1_wallet") + + # Step 3: Mine 101 blocks to node0_wallet + mining_address = node0_wallet.getnewaddress() + self.generatetoaddress(self.nodes[0], 101, mining_address) + + # Step 4: Check node0_wallet balance is 25 BTC (current regtest block reward) + assert_equal(node0_wallet.getbalance(), Decimal("25.00000000")) + + # Step 5: Send 1 BTC to node1_wallet address + address1 = node1_wallet.getnewaddress() + txid = node0_wallet.sendtoaddress(address1, Decimal("1.00000000")) + + # Step 6: Check txid in both mempools + self.sync_mempools() + assert txid in node0_wallet.getrawmempool() + assert txid in node1_wallet.getrawmempool() + + # Step 7: Check node0_wallet balance is less than 49 BTC + assert node0_wallet.getbalance() < Decimal("49.00000000") + + # Step 8: Mine one more block to node0_wallet + self.generate(self.nodes[0], 1) + + # Step 9: Check txid leaves mempool + assert txid not in node0_wallet.getrawmempool() + assert txid not in node1_wallet.getrawmempool() + + # Step 10: Check node1_wallet balance is 1 BTC + assert_equal(node1_wallet.getbalance(), Decimal("1.00000000")) + +if __name__ == '__main__': + WalletRPCBasics(__file__).main() \ No newline at end of file From 3e3fa0a4c17e260961257e1b7c809a677cb6e207 Mon Sep 17 00:00:00 2001 From: diegobianqui Date: Tue, 24 Feb 2026 11:50:27 -0300 Subject: [PATCH 2/2] Solution to challenge 02 --- test/functional/feature_prune_debug_log.py | 32 ++++++++++++++++++++++ test/functional/test_runner.py | 1 + test/functional/wallet_rpc_basics.py | 4 +++ 3 files changed, 37 insertions(+) create mode 100755 test/functional/feature_prune_debug_log.py diff --git a/test/functional/feature_prune_debug_log.py b/test/functional/feature_prune_debug_log.py new file mode 100755 index 0000000000..efd82fbc96 --- /dev/null +++ b/test/functional/feature_prune_debug_log.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# Copyright (c) 2017-present The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#!/usr/bin/env python3 +from test_framework.test_framework import BitcoinTestFramework +from test_framework.util import assert_equal + +class FeaturePruneDebugLog(BitcoinTestFramework): + def set_test_params(self): + self.num_nodes = 1 + + def run_test(self): + # Step 2 & 3: Restart with default args, assert prune log not present + with self.nodes[0].assert_debug_log([], unexpected_msgs=['Prune configured to target']): + self.restart_node(0, extra_args=[]) + + # Step 4: Assert pruning is not enabled via RPC + info = self.nodes[0].getblockchaininfo() + assert_equal(info["pruned"], False) + + # Step 5 & 6: Restart with prune enabled, assert prune log present + with self.nodes[0].assert_debug_log(['Prune configured to target']): + self.restart_node(0, extra_args=["-prune=550"]) + + # Step 7: Assert pruning is enabled via RPC + info = self.nodes[0].getblockchaininfo() + assert_equal(info["pruned"], True) + +if __name__ == '__main__': + FeaturePruneDebugLog(__file__).main() \ No newline at end of file diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 5152356aaf..6bc6f3bfaa 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -104,6 +104,7 @@ 'feature_fee_estimation.py', 'feature_taproot.py', 'feature_block.py', + 'feature_prune_debug_log.py', 'mempool_ephemeral_dust.py', 'wallet_conflicts.py', 'p2p_opportunistic_1p1c.py', diff --git a/test/functional/wallet_rpc_basics.py b/test/functional/wallet_rpc_basics.py index 410bd9857e..e30a0489e5 100755 --- a/test/functional/wallet_rpc_basics.py +++ b/test/functional/wallet_rpc_basics.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +# Copyright (c) 2015-present The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +#!/usr/bin/env python3 from decimal import Decimal from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal