#!/usr/bin/env python3 # Copyright (c) 2026 The Limenka developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. """Pre-fork block sync through the mempool bridge. The fork chain has its own P2P magic, so a fresh fork node cannot download the pre-fork (mainnet) history from spamchain peers through normal net_processing. While the fork tip is below the activation MTP, the mempool bridge's foreign peers serve that history: headers are validated with ProcessNewBlockHeaders and blocks with ProcessNewBlock. This test keeps the fork inactive (activation far in the future), lets node A mine a mainnet-style chain, and verifies node B - whose ONLY link to A is the bridge - syncs the whole chain through it. """ import hashlib from test_framework.address import byte_to_base58, hash160 from test_framework.descriptors import descsum_create from test_framework.key import ECKey from test_framework.segwit_addr import encode_segwit_address from test_framework.test_framework import LimenkaTestFramework from test_framework.util import p2p_port GENESIS_TIME = 1231006505 # Far-future activation MTP: the fork never activates in this test, so all # blocks are pre-fork (mainnet-rule) blocks. ACTIVATION_FAR = 1999999999 class ForkPreforkSyncTest(LimenkaTestFramework): def set_test_params(self): self.setup_clean_chain = True self.num_nodes = 2 self.chain = 'limenka' self.extra_args = [[ '-forkactivationtime=%d' % ACTIVATION_FAR, '-forkdelaysteps=1024', '-forkmineondemand', '-forkstandalone', '-fallbackfee=0.0002', '-addresstype=bech32', ], [ '-forkactivationtime=%d' % ACTIVATION_FAR, '-forkdelaysteps=1024', '-forkmineondemand', '-forkstandalone', '-fallbackfee=0.0002', '-addresstype=bech32', '-mempoolbridge', # Node B's bridge connects to node A (fork network). f'-bridgepeer=fork:127.0.0.1:{p2p_port(0)}', ]] self.rpc_timeout = 240 def skip_test_if_missing_module(self): self.skip_if_no_wallet() def add_options(self, parser): self.add_wallet_options(parser, descriptors=True, legacy=False) def init_wallet(self, *, node): wallet_name = self.default_wallet_name if self.wallet_names is None else self.wallet_names[node] if node < len(self.wallet_names) else False if wallet_name is not False: n = self.nodes[node] if wallet_name is not None: n.createwallet(wallet_name=wallet_name, descriptors=True, load_on_startup=True) if node == 0: seed = hashlib.sha256(b'prefork-sync-funding').digest() key = ECKey() key.set(seed, compressed=True) priv = key.get_bytes() wif = byte_to_base58(priv + b'\x01', 128) desc = descsum_create(f"wpkh({wif})") import_res = n.importdescriptors([{ "desc": desc, "timestamp": GENESIS_TIME + 600, "active": False, "internal": False, }]) assert all(r['success'] for r in import_res) self.funding_key = priv self.funding_pub = key.get_pubkey().get_bytes() def setup_network(self): # Do NOT auto-connect the nodes: the bridge must be the only link # between A and B. self.setup_nodes() def run_test(self): node_a, node_b = self.nodes[0], self.nodes[1] # A mines a mainnet-style (pre-fork) chain to a fixed bech32 addr. p2wpkh_program = hash160(self.funding_pub) addr = encode_segwit_address("bf", 0, p2wpkh_program) self.generatetoaddress(node_a, 100, addr, sync_fun=self.no_op) assert node_a.getblockcount() == 100 # B's bridge connects to A and downloads the whole chain. self.wait_until( lambda: node_b.getblockcount() == 100, timeout=240, ) info_b = node_b.getblockchaininfo() assert info_b['blocks'] == 100, info_b assert info_b['headers'] == 100, info_b # The blocks are consensus-valid on B (same chain hashes). assert node_b.getblockhash(99) == node_a.getblockhash(99) self.log.info("prefork block sync complete: B synced A's chain via the bridge") if __name__ == '__main__': ForkPreforkSyncTest(__file__).main()