diff --git a/functional-tests/envs/testenv.py b/functional-tests/envs/testenv.py index 1174306fe3..7699ba662d 100644 --- a/functional-tests/envs/testenv.py +++ b/functional-tests/envs/testenv.py @@ -78,7 +78,7 @@ def __init__(self, datadir_root: str, name: str, env: flexitest.LiveEnv): ) .with_pubkey(agg_pubkey) .with_magic_bytes("".join(chr(b) for b in rollup_cfg.magic_bytes)) - .with_datadir(self.datadir_root) + .with_datadir(os.path.join(self.datadir_root, name)) ) self.alpen_cli = builder.build(self) diff --git a/functional-tests/factory/alpen_client.py b/functional-tests/factory/alpen_client.py index 694063acf4..81c72f36e8 100644 --- a/functional-tests/factory/alpen_client.py +++ b/functional-tests/factory/alpen_client.py @@ -143,12 +143,14 @@ def l1_address(self): # fmt: on return self._run_and_extract_with_re(cmd, r"\b(?:bc1|tb1|bcrt1)[0-9a-z]{25,59}\b") - def deposit(self) -> str | None: + def deposit(self, alpen_address=None) -> str | None: # fmt: off cmd = [ "alpen", "deposit", ] + if alpen_address: + cmd.append(alpen_address) # fmt: on return self._run_and_extract_with_re(cmd, r"Transaction ID:\s*([0-9a-f]{64})") diff --git a/functional-tests/mixins/__init__.py b/functional-tests/mixins/__init__.py index b7c00b9850..3f990360a9 100644 --- a/functional-tests/mixins/__init__.py +++ b/functional-tests/mixins/__init__.py @@ -29,6 +29,7 @@ def premain(self, ctx: flexitest.RunContext): w3 = self._new_w3() funded_acc = FundedAccount(w3) funded_acc.fund_me(genesis_account) + self.funded_acc = funded_acc # Setting transactions api with default DEBUG level. self._txs = EthTransactions(funded_acc, self.debug) self._w3 = w3 diff --git a/functional-tests/mixins/bridge_mixin.py b/functional-tests/mixins/bridge_mixin.py index b388cf67d1..4f01ca0757 100644 --- a/functional-tests/mixins/bridge_mixin.py +++ b/functional-tests/mixins/bridge_mixin.py @@ -50,7 +50,7 @@ def deposit( self.info(f"Initial EL balance: {initial_balance}") # Make DRT (deposit request transaction) - drt_tx_id, raw_drt_bytes = self.make_drt() + drt_tx_id, raw_drt_bytes = self.make_drt(el_address) self.info(f"Deposit Request Transaction ID: {drt_tx_id}") # Create managed DT (deposit transaction) with auto-incremented ID @@ -178,7 +178,7 @@ def check_checkpoint_covers_withdrawal(): return l2_tx_hash, tx_receipt, total_gas_used - def make_drt(self) -> tuple[str, str]: + def make_drt(self, el_address=None) -> tuple[str, str]: """ Creates and matures a Deposit Request Transaction (DRT). @@ -193,7 +193,7 @@ def make_drt(self) -> tuple[str, str]: self.btcrpc.proxy.sendtoaddress(addr, 10.01) self.btcrpc.proxy.generatetoaddress(1, seq_addr) # Create and send deposit request transaction - drt_tx_id = self.alpen_cli.deposit() + drt_tx_id = self.alpen_cli.deposit(el_address) current_height = self.btcrpc.proxy.getblockcount() # time to mature DRT self.btcrpc.proxy.generatetoaddress(6, seq_addr) diff --git a/functional-tests/mixins/bridge_out_precompile_contract_mixin.py b/functional-tests/mixins/bridge_out_precompile_contract_mixin.py index 983f16ea79..ec18de4d2b 100644 --- a/functional-tests/mixins/bridge_out_precompile_contract_mixin.py +++ b/functional-tests/mixins/bridge_out_precompile_contract_mixin.py @@ -4,6 +4,7 @@ from mixins import bridge_mixin from utils import get_bridge_pubkey +from utils.transaction import SmartContracts class BridgePrecompileMixin(bridge_mixin.BridgeMixin): @@ -15,14 +16,27 @@ def premain(self, ctx: flexitest.InitContext): self.withdraw_address = ctx.env.gen_ext_btc_address() self.bridge_pk = get_bridge_pubkey(self.seqrpc) + self.w3.eth.default_account = self.w3.address xonlypk = extract_p2tr_pubkey(self.withdraw_address) bosd = xonlypk_to_descriptor(xonlypk) self.bosd = bytes.fromhex(bosd) + # Extract ABI for compatibility with existing tests + self.abi, _ = SmartContracts.compile_contract( + "IndirectWithdrawalProxy.sol", "WithdrawCaller" + ) + # Deploy contract. self.withdraw_contract_id = "withdraw_contract" - self.txs.deploy_contract( + contract_address, _ = self.txs.deploy_contract( "IndirectWithdrawalProxy.sol", "WithdrawCaller", self.withdraw_contract_id ) + + # Create a simple object to hold the contract address for compatibility + class DeploymentReceipt: + def __init__(self, contract_address): + self.contractAddress = contract_address + + self.deployed_contract_receipt = DeploymentReceipt(contract_address) diff --git a/functional-tests/tests/bridge/bridge_test.py b/functional-tests/tests/bridge/bridge_test.py index 05dc219ea2..e2014eeead 100644 --- a/functional-tests/tests/bridge/bridge_test.py +++ b/functional-tests/tests/bridge/bridge_test.py @@ -1,6 +1,3 @@ -import os - -import base58 import flexitest from strata_utils import ( extract_p2tr_pubkey, @@ -30,24 +27,7 @@ def __init__(self, ctx: flexitest.InitContext): ) def main(self, ctx: flexitest.RunContext): - path = os.path.join(ctx.datadir_root, "_bridge_test", "_init") - print(path) - priv_keys = [] - opkeys = sorted( - filter(lambda file: file.startswith("opkey"), os.listdir(path)), - key=lambda x: int("".join(filter(str.isdigit, x))), - ) - print(opkeys) - for filename in opkeys: - if not filename.startswith("op"): - continue - - full_path = os.path.join(path, filename) - with open(full_path) as f: - content = f.read().strip() - decoded = base58.b58decode(content)[:-4] # remove checksum - priv_keys.append(decoded) - + priv_keys = get_priv_keys(ctx) el_address = self.alpen_cli.l2_address() print("-----------------------") print(el_address) diff --git a/functional-tests/tests/el_bridge_precompile.py b/functional-tests/tests/el_bridge_precompile.py index c73f070c1c..5ac1d0fe96 100644 --- a/functional-tests/tests/el_bridge_precompile.py +++ b/functional-tests/tests/el_bridge_precompile.py @@ -1,42 +1,45 @@ -import os -import time - import flexitest from web3 import Web3 from web3._utils.events import get_event_data -from envs import testenv +from envs import net_settings, testenv +from mixins.bridge_out_precompile_contract_mixin import BridgePrecompileMixin +from utils import * from utils.constants import PRECOMPILE_BRIDGEOUT_ADDRESS +from utils.wait.reth import RethWaiter withdrawal_intent_event_abi = { "anonymous": False, "inputs": [ {"indexed": False, "internalType": "uint64", "name": "amount", "type": "uint64"}, - {"indexed": False, "internalType": "bytes", "name": "dest_pk", "type": "bytes32"}, + {"indexed": False, "internalType": "bytes", "name": "destination", "type": "bytes"}, ], "name": "WithdrawalIntentEvent", "type": "event", } -event_signature_text = "WithdrawalIntentEvent(uint64,bytes32)" +event_signature_text = "WithdrawalIntentEvent(uint64,bytes)" @flexitest.register -class ElBridgePrecompileTest(testenv.StrataTestBase): +class ElBridgePrecompileTest(BridgePrecompileMixin): def __init__(self, ctx: flexitest.InitContext): - ctx.set_env("basic") + ctx.set_env( + testenv.BasicEnvConfig( + 101, + prover_client_settings=ProverClientSettings.new_with_proving(), + rollup_settings=net_settings.get_fast_batch_settings(), + auto_generate_blocks=True, + ) + ) def main(self, ctx: flexitest.RunContext): - self.warning("SKIPPING TEST fn_el_bridge_precompile") - return True - - reth = ctx.get_service("reth") - web3: Web3 = reth.create_web3() + web3: Web3 = self.reth.create_web3() source = web3.address dest = web3.to_checksum_address(PRECOMPILE_BRIDGEOUT_ADDRESS) - # 64 bytes - dest_pk = os.urandom(32).hex() - self.debug(dest_pk) + + priv_keys = get_priv_keys(ctx) + self.deposit(ctx, self.deployed_contract_receipt.contractAddress, priv_keys) assert web3.is_connected(), "cannot connect to reth" @@ -46,24 +49,24 @@ def main(self, ctx: flexitest.RunContext): assert original_bridge_balance == 0 - # 10 rollup btc as wei - to_transfer_wei = 10_000_000_000_000_000_000 + cfg = ctx.env.rollup_cfg() + deposit_amount = cfg.deposit_amount + to_transfer_sats = deposit_amount * 10_000_000_000 + to_transfer_wei = to_transfer_sats # Same value in wei + dest_pk = "0x04db4c79cc3ffca26f51e21241b9332d646b0772dd7e98de9c1de6b10990cab80b" txid = web3.eth.send_transaction( { "to": dest, - "value": hex(to_transfer_wei), - "gas": hex(100000), + "value": hex(to_transfer_sats), "from": source, + "gas": hex(200000), "data": dest_pk, } ) - self.debug(txid.to_0x_hex()) - - # build block - time.sleep(2) - receipt = web3.eth.get_transaction_receipt(txid) + receipt_waiter = RethWaiter(web3.eth, self.logger, 60, 0.5) + receipt = receipt_waiter.wait_until_tx_included_in_block(txid.hex()) assert receipt.status == 1, "precompile transaction failed" assert len(receipt.logs) == 1, "no logs or invalid logs" @@ -74,11 +77,8 @@ def main(self, ctx: flexitest.RunContext): assert log.topics[0].hex() == event_signature_hash event_data = get_event_data(web3.codec, withdrawal_intent_event_abi, log) - # 1 rollup btc = 10**18 wei - to_transfer_sats = to_transfer_wei // 10_000_000_000 - - assert event_data.args.amount == to_transfer_sats - assert event_data.args.dest_pk.hex() == dest_pk + assert event_data.args.amount == deposit_amount + assert event_data.args.destination.hex() == dest_pk[2:] # Remove 0x prefix for comparison final_block_no = web3.eth.block_number final_bridge_balance = web3.eth.get_balance(dest) diff --git a/functional-tests/tests/evm/evm_withdraw_indirect_with_contract_balance.py b/functional-tests/tests/evm/evm_withdraw_indirect_with_contract_balance.py index d8fc0158e3..77a3c07c86 100644 --- a/functional-tests/tests/evm/evm_withdraw_indirect_with_contract_balance.py +++ b/functional-tests/tests/evm/evm_withdraw_indirect_with_contract_balance.py @@ -1,25 +1,27 @@ -import logging - import flexitest from envs import net_settings, testenv from mixins.bridge_out_precompile_contract_mixin import BridgePrecompileMixin +from utils import ProverClientSettings, get_priv_keys @flexitest.register class ContractBridgeOutWithContractBalanceTest(BridgePrecompileMixin): def __init__(self, ctx: flexitest.InitContext): - fast_batch_settings = net_settings.get_fast_batch_settings() ctx.set_env( - testenv.BasicEnvConfig(pre_generate_blocks=101, rollup_settings=fast_batch_settings) + testenv.BasicEnvConfig( + 101, + prover_client_settings=ProverClientSettings.new_with_proving(), + rollup_settings=net_settings.get_fast_batch_settings(), + auto_generate_blocks=True, + ) ) def main(self, ctx: flexitest.RunContext): - logging.warn("test temporarily disabled") - return - # Deposit to contract Address - self.deposit(ctx, self.deployed_contract_receipt.contractAddress, self.bridge_pk) + priv_keys = get_priv_keys(ctx) + self.deposit(ctx, self.deployed_contract_receipt.contractAddress, priv_keys) + self.deposit(ctx, self.web3.address, priv_keys) # withdraw # TODO: use self.txs.deploy and self.txs.call diff --git a/functional-tests/tests/evm/evm_withdraw_indirect_with_msg_sender_value.py b/functional-tests/tests/evm/evm_withdraw_indirect_with_msg_sender_value.py index 16333eb0c8..f765883c2f 100644 --- a/functional-tests/tests/evm/evm_withdraw_indirect_with_msg_sender_value.py +++ b/functional-tests/tests/evm/evm_withdraw_indirect_with_msg_sender_value.py @@ -1,39 +1,44 @@ -import logging - import flexitest from envs import net_settings, testenv from envs.rollup_params_cfg import RollupConfig from mixins.bridge_out_precompile_contract_mixin import BridgePrecompileMixin +from utils import ProverClientSettings, get_priv_keys from utils.constants import SATS_TO_WEI @flexitest.register class ContractBridgeOutWithSenderValueTest(BridgePrecompileMixin): def __init__(self, ctx: flexitest.InitContext): - fast_batch_settings = net_settings.get_fast_batch_settings() ctx.set_env( - testenv.BasicEnvConfig(pre_generate_blocks=101, rollup_settings=fast_batch_settings) + testenv.BasicEnvConfig( + 101, + prover_client_settings=ProverClientSettings.new_with_proving(), + rollup_settings=net_settings.get_fast_batch_settings(), + auto_generate_blocks=True, + ) ) def main(self, ctx: flexitest.RunContext): - logging.warn("test temporarily disabled") - return + priv_keys = get_priv_keys(ctx) - # deposit once - self.deposit(ctx, self.bridge_eth_account.address, self.bridge_pk) + # deposit twice + self.deposit(ctx, self.web3.address, priv_keys) + self.deposit(ctx, self.web3.address, priv_keys) + print(self.web3.address) cfg: RollupConfig = ctx.env.rollup_cfg() deposit_amount = cfg.deposit_amount # Call the contract function # TODO: use self.txs.deploy and self.txs.call - contract_instance = self.w3.eth.contract( + # check balance + contract_instance = self.web3.eth.contract( abi=self.abi, address=self.deployed_contract_receipt.contractAddress ) tx_hash = contract_instance.functions.withdraw(self.bosd).transact( {"gas": 5_000_000, "value": deposit_amount * SATS_TO_WEI} ) - tx_receipt = self.w3.eth.wait_for_transaction_receipt(tx_hash, timeout=30) + tx_receipt = self.web3.eth.wait_for_transaction_receipt(tx_hash, timeout=30) assert tx_receipt.status == 1 diff --git a/functional-tests/tests/prover/prover_el_deposit_withdraw.py b/functional-tests/tests/prover/prover_el_deposit_withdraw.py index 78e9b3bd07..65822f8c5b 100644 --- a/functional-tests/tests/prover/prover_el_deposit_withdraw.py +++ b/functional-tests/tests/prover/prover_el_deposit_withdraw.py @@ -34,7 +34,7 @@ def main(self, ctx: flexitest.RunContext): self.warning("SKIPPING TEST prover_el_deposit_withdraw") return True - evm_addr = self.bridge_eth_account.address + evm_addr = self.eth_account.address bridge_pk = get_bridge_pubkey(self.seqrpc) # Init RPCs. diff --git a/functional-tests/utils/utils.py b/functional-tests/utils/utils.py index f8c5c6aa43..30d17dc85d 100644 --- a/functional-tests/utils/utils.py +++ b/functional-tests/utils/utils.py @@ -8,6 +8,7 @@ from threading import Thread from typing import Any, TypeVar +import base58 from bitcoinlib.services.bitcoind import BitcoindClient from strata_utils import convert_to_xonly_pk, musig_aggregate_pks @@ -652,3 +653,26 @@ def check_initial_eth_balance(rethrpc, address, debug_fn=print): balance = int(rethrpc.eth_getBalance(address), 16) debug_fn(f"Strata Balance before deposits: {balance}") assert balance == 0, "Strata balance is not expected (should be zero initially)" + + +def get_priv_keys(ctx, env=None): + if env is None: + path = os.path.join(ctx.datadir_root, f"_{ctx.name}", "_init") + else: + path = os.path.join(ctx.datadir_root, env, "_init") + + priv_keys = [] + opkeys = sorted( + filter(lambda file: file.startswith("opkey"), os.listdir(path)), + key=lambda x: int("".join(filter(str.isdigit, x))), + ) + for filename in opkeys: + if not filename.startswith("op"): + continue + + full_path = os.path.join(path, filename) + with open(full_path) as f: + content = f.read().strip() + decoded = base58.b58decode(content)[:-4] # remove checksum + priv_keys.append(decoded) + return priv_keys diff --git a/functional-tests/utils/wait/reth.py b/functional-tests/utils/wait/reth.py index 263c6fbc2c..1e6c212750 100644 --- a/functional-tests/utils/wait/reth.py +++ b/functional-tests/utils/wait/reth.py @@ -46,3 +46,19 @@ def wait_until_block_witness_at_blockhash(self, blockhash, timeout: None | int = error_with="Finding non empty witness for blockhash {blockhash} timed out", timeout=timeout or self.timeout, ) + + def wait_until_tx_included_in_block(self, txid: str): + def _query(): + try: + receipt = self.rpc_client.get_transaction_receipt(txid) + return receipt + except Exception as e: + return e + + result = self._wait_until_with_value( + _query, + lambda result: not isinstance(result, Exception), + error_with="Transaction receipt for txid not available", + timeout=self.timeout, + ) + return result