diff --git a/.gitignore b/.gitignore index f3af31d..8c92e31 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ /Node/target .vscode .env -tools/tx_spammer/venv +venv .DS_Store .sum \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index f66a480..881ab7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/library/rust:1.80 AS builder +FROM docker.io/library/rust:1.83 AS builder # Set the working directory inside the container WORKDIR /usr/src/taiko_preconf_avs_node diff --git a/e2e_tests/.env.example b/e2e_tests/.env.example new file mode 100644 index 0000000..42cf09e --- /dev/null +++ b/e2e_tests/.env.example @@ -0,0 +1,9 @@ +# Ethereum L1 RPC URL (e.g., local node, testnet, or mainnet) +L1_RPC_URL=http://localhost:8545 + +# L2 (Taiko) RPC URL +L2_RPC_URL=http://localhost:8546 + + +# Test account private key (optional, for specific tests) +TEST_L2_PREFUNDED_PRIVATE_KEY=370e47f3c39cf4d03cb87cb71a268776421cdc22c39aa81f1e5ba19df19202f1 diff --git a/e2e_tests/.gitignore b/e2e_tests/.gitignore new file mode 100644 index 0000000..ae90a5f --- /dev/null +++ b/e2e_tests/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.py[cod] +*$py.class +.pytest_cache/ diff --git a/e2e_tests/README.md b/e2e_tests/README.md new file mode 100644 index 0000000..a4295c3 --- /dev/null +++ b/e2e_tests/README.md @@ -0,0 +1,29 @@ +# End to end preconfirmation tests + +This is a collection of end to end tests for the preconfirmation service. + +It requires full stack to be up and running. Usually by running +``` +kurtosis run --enclave taiko-preconf-devnet . --args-file network_params.yaml +``` +from the main branch of https://github.com/NethermindEth/preconfirm-devnet-package. + +It also requires a `.env` file to be present in the root directory. You can copy `.env.example` file into `.env` and fill in the required values. + +Create venv: +``` +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +``` + +To run all tests: + +``` +pytest +``` + +To run a specific test with output printed: +``` +pytest -s -v -k test_name +``` \ No newline at end of file diff --git a/e2e_tests/conftest.py b/e2e_tests/conftest.py new file mode 100644 index 0000000..d63fcac --- /dev/null +++ b/e2e_tests/conftest.py @@ -0,0 +1,22 @@ +import pytest +from web3 import Web3 +from eth_account import Account +import os +from dotenv import load_dotenv + +load_dotenv() + +@pytest.fixture(scope="session") +def l1_client(): + w3 = Web3(Web3.HTTPProvider(os.getenv("L1_RPC_URL"))) + return w3 + +@pytest.fixture(scope="session") +def l2_client_node1(): + w3 = Web3(Web3.HTTPProvider(os.getenv("L2_RPC_URL_NODE1"))) + return w3 + +@pytest.fixture(scope="session") +def l2_client_node2(): + w3 = Web3(Web3.HTTPProvider(os.getenv("L2_RPC_URL_NODE2"))) + return w3 diff --git a/e2e_tests/requirements.txt b/e2e_tests/requirements.txt new file mode 100644 index 0000000..1bb906e --- /dev/null +++ b/e2e_tests/requirements.txt @@ -0,0 +1,3 @@ +web3>=6.0.0 +python-dotenv>=1.0.0 +pytest>=7.4.0 diff --git a/e2e_tests/test_avs_node.py b/e2e_tests/test_avs_node.py new file mode 100644 index 0000000..e11aa5e --- /dev/null +++ b/e2e_tests/test_avs_node.py @@ -0,0 +1,66 @@ +import pytest +import requests +from web3 import Web3 +import os +from dotenv import load_dotenv +import sys +from utils import * + +load_dotenv() + +l2_prefunded_priv_key = os.getenv("TEST_L2_PREFUNDED_PRIVATE_KEY") +if not l2_prefunded_priv_key: + raise Exception("Environment variable TEST_L2_PREFUNDED_PRIVATE_KEY not set") + + +def test_chain_ids(l1_client, l2_client_node1, l2_client_node2): + """Test to verify the chain IDs of L1 and L2 networks""" + l1_chain_id = l1_client.eth.chain_id + l2_chain_id_node1 = l2_client_node1.eth.chain_id + l2_chain_id_node2 = l2_client_node2.eth.chain_id + + print(f"L1 Chain ID: {l1_chain_id}") + print(f"L2 Chain ID Node 1: {l2_chain_id_node1}") + print(f"L2 Chain ID Node 2: {l2_chain_id_node2}") + + assert l1_chain_id > 0, "L1 chain ID should be greater than 0" + assert l2_chain_id_node1 > 0, "L2 chain ID should be greater than 0" + + assert l1_chain_id != l2_chain_id_node1, "L1 and L2 should have different chain IDs" + assert l2_chain_id_node1 == l2_chain_id_node2, "L2 nodes should have the same chain IDs" + +def test_preconfirm_transaction(l1_client, l2_client_node1): + account = l2_client_node1.eth.account.from_key(l2_prefunded_priv_key) + nonce = l2_client_node1.eth.get_transaction_count(account.address) + l2_block_number = l2_client_node1.eth.block_number + + send_transaction(nonce, account, '0.00005', l2_client_node1, l2_prefunded_priv_key) + + wait_for_secs(4) + + l2_block_number_after = l2_client_node1.eth.block_number + + print(f"L2 Block Number: {l2_block_number}") + print(f"L2 Block Number After: {l2_block_number_after}") + + assert l2_block_number_after > l2_block_number, "L2 block number should increase after sending a transaction" + +def test_p2p_preconfirmation(l2_client_node1, l2_client_node2): + account = l2_client_node1.eth.account.from_key(l2_prefunded_priv_key) + nonce = l2_client_node1.eth.get_transaction_count(account.address) + l2_node_2_block_number = l2_client_node2.eth.block_number + + send_transaction(nonce, account, '0.00006', l2_client_node1, l2_prefunded_priv_key) + + wait_for_secs(4) + + l2_node_2_block_number_after = l2_client_node2.eth.block_number + node_1_block_hash = l2_client_node1.eth.get_block(l2_node_2_block_number_after).hash + node_2_block_hash = l2_client_node2.eth.get_block(l2_node_2_block_number_after).hash + + print(f"L2 Node 2 Block Number: {l2_node_2_block_number}") + print(f"L2 Node 2 Block Number After: {l2_node_2_block_number_after}") + + assert l2_node_2_block_number_after > l2_node_2_block_number, "L2 Node 2 block number should increase after sending a transaction" + + assert node_2_block_hash == node_1_block_hash, "L2 Node 1 and L2 Node 2 should have the same block hash after sending a transaction" \ No newline at end of file diff --git a/e2e_tests/utils.py b/e2e_tests/utils.py new file mode 100644 index 0000000..31dba4f --- /dev/null +++ b/e2e_tests/utils.py @@ -0,0 +1,22 @@ +import time + +def send_transaction(nonce : int, account, amount, eth_client, private_key): + tx = { + 'nonce': nonce, + 'to': "0x0000000000000000000000000000000000000001", + 'value': eth_client.to_wei(amount, 'ether'), + 'gas': 40000, + 'gasPrice': eth_client.eth.gas_price, + 'chainId': eth_client.eth.chain_id + } + print(f'RPC URL: {eth_client.provider.endpoint_uri}, Sending from: {account.address}') + signed_tx = eth_client.eth.account.sign_transaction(tx, private_key) + tx_hash = eth_client.eth.send_raw_transaction(signed_tx.raw_transaction) + print(f'Transaction sent: {tx_hash.hex()}') + return tx_hash + +def wait_for_secs(seconds): + for i in range(seconds, 0, -1): + print(f'Waiting for {i:02d} seconds', end='\r') + time.sleep(1) + print('') \ No newline at end of file diff --git a/tools/tx_spammer/tx_spammer.py b/tools/tx_spammer/tx_spammer.py index b541cda..cbbb3de 100644 --- a/tools/tx_spammer/tx_spammer.py +++ b/tools/tx_spammer/tx_spammer.py @@ -94,7 +94,7 @@ def send_transaction(nonce : int): 'nonce': nonce, 'to': recipient, 'value': amount, - 'gas': 21000, + 'gas': 40000, 'gasPrice': w3.to_wei('10', 'gwei'), 'chainId': w3.eth.chain_id }