├── .dockerignore ├── .gitattributes ├── .github ├── CODEOWNERS ├── SECURITY.md ├── actionlint.yaml ├── config │ └── assertoor │ │ ├── el-stability-check.yaml │ │ ├── network_params_blob.yaml │ │ └── network_params_tx.yaml ├── pull_request_template.md ├── scripts │ ├── compare_ef_tests.sh │ ├── flamegraph_watcher.sh │ ├── formatter.sh │ ├── parse_test_result.sh │ ├── publish_hive.sh │ └── publish_loc.sh └── workflows │ ├── common_hive_reports.yaml │ ├── daily_loc.yaml │ ├── daily_reports.yaml │ ├── main_docker_publish.yaml │ ├── main_flamegraph_report.yaml │ ├── main_perf_blocks_exec.yml │ ├── main_prover.yaml │ ├── main_prover_l1.yaml │ ├── pr-main_l1.yaml │ ├── pr-main_l2.yaml │ ├── pr-main_l2_contracts.yaml │ ├── pr-main_l2_prover.yaml │ ├── pr-main_l2_prover_nightly.yaml.disabled │ ├── pr-main_l2_tdx.yaml │ ├── pr-main_l2_tdx_build.yaml │ ├── pr-main_levm.yaml │ ├── pr_lint_gha.yaml │ ├── pr_lint_pr_title.yml │ ├── pr_lint_readme.yaml │ ├── pr_loc.yaml │ ├── pr_perf_blocks_exec.yaml │ ├── pr_perf_changelog.yml │ ├── pr_perf_levm.yaml │ ├── pr_perf_trie.yml │ └── tag_l2_release.yaml ├── .gitignore ├── .tool-versions ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── clippy.toml ├── cmd ├── ef_tests │ ├── blockchain │ │ ├── .fixtures_url │ │ ├── Cargo.toml │ │ ├── Makefile │ │ ├── README.md │ │ ├── lib.rs │ │ ├── network.rs │ │ ├── test_runner.rs │ │ ├── tests │ │ │ ├── cancun.rs │ │ │ ├── prague.rs │ │ │ └── shanghai.rs │ │ └── types.rs │ └── state │ │ ├── .fixtures_url │ │ ├── Cargo.toml │ │ ├── Makefile │ │ ├── README.md │ │ ├── deserialize.rs │ │ ├── lib.rs │ │ ├── parser.rs │ │ ├── report.rs │ │ ├── runner │ │ ├── levm_runner.rs │ │ ├── mod.rs │ │ └── revm_runner.rs │ │ ├── tests │ │ └── all.rs │ │ ├── types.rs │ │ └── utils.rs ├── ethrex │ ├── Cargo.toml │ ├── bench │ │ └── import_blocks_benchmark.rs │ ├── build.rs │ ├── cli.rs │ ├── decode.rs │ ├── ethrex.rs │ ├── initializers.rs │ ├── l2 │ │ ├── command.rs │ │ ├── mod.rs │ │ └── options.rs │ ├── lib.rs │ ├── networks.rs │ ├── networks │ │ ├── holesky │ │ │ ├── bootnodes.json │ │ │ └── genesis.json │ │ ├── hoodi │ │ │ ├── bootnodes.json │ │ │ └── genesis.json │ │ ├── mainnet │ │ │ ├── bootnodes.json │ │ │ └── genesis.json │ │ └── sepolia │ │ │ ├── bootnodes.json │ │ │ └── genesis.json │ └── utils.rs └── ethrex_replay │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ └── src │ ├── bench.rs │ ├── cache.rs │ ├── cli.rs │ ├── constants.rs │ ├── fetcher.rs │ ├── main.rs │ ├── rpc │ ├── db.rs │ └── mod.rs │ └── run.rs ├── crates ├── blockchain │ ├── Cargo.toml │ ├── blockchain.rs │ ├── constants.rs │ ├── dev │ │ ├── Cargo.toml │ │ ├── Dockerfile │ │ ├── block_producer.rs │ │ ├── dev.rs │ │ └── docker-compose-dev.yaml │ ├── error.rs │ ├── fork_choice.rs │ ├── mempool.rs │ ├── metrics │ │ ├── Cargo.toml │ │ ├── README.md │ │ ├── api.rs │ │ ├── docker-compose-metrics-l2.overrides.yaml │ │ ├── docker-compose-metrics.yaml │ │ ├── metrics_blocks.rs │ │ ├── metrics_l2.rs │ │ ├── metrics_transactions.rs │ │ ├── mod.rs │ │ └── provisioning │ │ │ ├── grafana_provisioning │ │ │ ├── dashboards │ │ │ │ ├── common_dashboards │ │ │ │ │ └── tx_dashboard.json │ │ │ │ ├── dashboard.yaml │ │ │ │ ├── dashboard_config_l1.yaml │ │ │ │ ├── dashboard_config_l2.yaml │ │ │ │ └── l2_dashboards │ │ │ │ │ └── l2_overview.json │ │ │ └── datasources │ │ │ │ └── datasource.yaml │ │ │ └── prometheus │ │ │ ├── prometheus_l1_dev.yaml │ │ │ └── prometheus_l2.yaml │ ├── payload.rs │ ├── smoke_test.rs │ ├── tracing.rs │ └── vm.rs ├── common │ ├── Cargo.toml │ ├── base64.rs │ ├── common.rs │ ├── constants.rs │ ├── rlp │ │ ├── Cargo.toml │ │ ├── constants.rs │ │ ├── decode.rs │ │ ├── encode.rs │ │ ├── error.rs │ │ ├── rlp.rs │ │ └── structs.rs │ ├── serde_utils.rs │ ├── tracing.rs │ ├── trie │ │ ├── Cargo.toml │ │ ├── Makefile │ │ ├── README.md │ │ ├── benches │ │ │ └── trie_bench.rs │ │ ├── db.rs │ │ ├── error.rs │ │ ├── nibbles.rs │ │ ├── node.rs │ │ ├── node │ │ │ ├── branch.rs │ │ │ ├── extension.rs │ │ │ └── leaf.rs │ │ ├── node_hash.rs │ │ ├── rlp.rs │ │ ├── test_utils.rs │ │ ├── trie.rs │ │ ├── trie_iter.rs │ │ └── verify_range.rs │ └── types │ │ ├── account.rs │ │ ├── account_update.rs │ │ ├── batch.rs │ │ ├── blobs_bundle.rs │ │ ├── block.rs │ │ ├── constants.rs │ │ ├── fork_id.rs │ │ ├── genesis.rs │ │ ├── mod.rs │ │ ├── payload.rs │ │ ├── receipt.rs │ │ ├── requests.rs │ │ ├── transaction.rs │ │ └── tx_fields.rs ├── l2 │ ├── .gitignore │ ├── .tool-versions │ ├── Cargo.toml │ ├── Dockerfile │ ├── Makefile │ ├── contracts │ │ ├── .gitignore │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── bin │ │ │ ├── deployer │ │ │ │ ├── cli.rs │ │ │ │ ├── error.rs │ │ │ │ └── main.rs │ │ │ └── system_contracts_updater │ │ │ │ ├── cli.rs │ │ │ │ ├── error.rs │ │ │ │ └── main.rs │ │ └── src │ │ │ ├── l1 │ │ │ ├── CommonBridge.sol │ │ │ ├── OnChainProposer.sol │ │ │ └── interfaces │ │ │ │ ├── ICommonBridge.sol │ │ │ │ ├── IOnChainProposer.sol │ │ │ │ ├── IPicoVerifier.sol │ │ │ │ ├── IRiscZeroVerifier.sol │ │ │ │ ├── ISP1Verifier.sol │ │ │ │ └── ITDXVerifier.sol │ │ │ └── l2 │ │ │ ├── CommonBridgeL2.sol │ │ │ └── interfaces │ │ │ └── ICommonBridgeL2.sol │ ├── docker-compose-l2-store.overrides.yaml │ ├── docker-compose-l2.yaml │ ├── docs │ │ ├── README.md │ │ ├── contracts.md │ │ ├── ethrex_aligned_mode.md │ │ ├── img │ │ │ ├── aligned_mode_proof_sender.png │ │ │ ├── aligned_mode_proof_verifier.png │ │ │ ├── execw_case1.png │ │ │ └── execw_case2.png │ │ ├── overview.md │ │ ├── prover.md │ │ ├── sequencer.md │ │ ├── state_diffs.md │ │ └── withdrawals.md │ ├── errors.rs │ ├── l2.rs │ ├── prover │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── src │ │ │ ├── backends │ │ │ │ ├── exec.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── pico.rs │ │ │ │ ├── risc0.rs │ │ │ │ └── sp1.rs │ │ │ ├── cli.rs │ │ │ ├── config.rs │ │ │ ├── lib.rs │ │ │ ├── main.rs │ │ │ ├── prover.rs │ │ │ └── utils │ │ │ │ └── placeholder.rs │ │ └── zkvm │ │ │ ├── .gitignore │ │ │ ├── Cargo.toml │ │ │ └── interface │ │ │ ├── Cargo.toml │ │ │ ├── Makefile │ │ │ ├── build.rs │ │ │ ├── pico │ │ │ ├── Cargo.toml │ │ │ ├── rust-toolchain │ │ │ └── src │ │ │ │ └── main.rs │ │ │ ├── risc0 │ │ │ ├── Cargo.toml │ │ │ ├── NOTICE │ │ │ └── src │ │ │ │ └── main.rs │ │ │ ├── sp1 │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ ├── NOTICE │ │ │ └── src │ │ │ │ └── main.rs │ │ │ └── src │ │ │ ├── deposits.rs │ │ │ ├── execution.rs │ │ │ ├── io.rs │ │ │ ├── lib.rs │ │ │ ├── methods.rs │ │ │ ├── trie.rs │ │ │ └── withdrawals.rs │ ├── sdk │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── calldata.rs │ │ │ ├── l1_to_l2_tx_data.rs │ │ │ ├── merkle_tree.rs │ │ │ └── sdk.rs │ ├── sequencer │ │ ├── block_producer.rs │ │ ├── block_producer │ │ │ └── payload_builder.rs │ │ ├── configs.rs │ │ ├── errors.rs │ │ ├── execution_cache.rs │ │ ├── l1_committer.rs │ │ ├── l1_proof_sender.rs │ │ ├── l1_proof_verifier.rs │ │ ├── l1_watcher.rs │ │ ├── metrics.rs │ │ ├── mod.rs │ │ ├── proof_coordinator.rs │ │ ├── setup.rs │ │ ├── state_diff.rs │ │ └── utils.rs │ ├── storage │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ └── src │ │ │ ├── api.rs │ │ │ ├── lib.rs │ │ │ ├── rlp.rs │ │ │ ├── store.rs │ │ │ └── store_db │ │ │ ├── in_memory.rs │ │ │ ├── libmdbx.rs │ │ │ ├── mod.rs │ │ │ └── redb.rs │ ├── tee │ │ ├── DOCS.md │ │ ├── contracts │ │ │ ├── .gitignore │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── assets │ │ │ │ ├── p256.hex │ │ │ │ └── platform_ca.hex │ │ │ └── src │ │ │ │ └── TDXVerifier.sol │ │ ├── quote-gen │ │ │ ├── .gitignore │ │ │ ├── Cargo.lock │ │ │ ├── Cargo.toml │ │ │ ├── Makefile │ │ │ ├── hypervisor.nix │ │ │ ├── image.nix │ │ │ ├── service.nix │ │ │ └── src │ │ │ │ ├── main.rs │ │ │ │ └── sender.rs │ │ └── quote.example │ ├── tests │ │ ├── state_reconstruct.rs │ │ └── tests.rs │ └── utils │ │ ├── error.rs │ │ ├── helpers.rs │ │ ├── mod.rs │ │ ├── parse.rs │ │ ├── prover │ │ ├── db.rs │ │ ├── errors.rs │ │ ├── mod.rs │ │ ├── proving_systems.rs │ │ └── save_state.rs │ │ └── test_data_io.rs ├── networking │ ├── docs │ │ ├── Network.md │ │ ├── Sync.md │ │ └── diagrams │ │ │ ├── SnapSync.drawio.png │ │ │ ├── StateSyncAndHealing.drawio.png │ │ │ └── bytecode_fetcher.jpg │ ├── p2p │ │ ├── Cargo.toml │ │ ├── discv4 │ │ │ ├── helpers.rs │ │ │ ├── lookup.rs │ │ │ ├── messages.rs │ │ │ ├── mod.rs │ │ │ └── server.rs │ │ ├── kademlia.rs │ │ ├── network.rs │ │ ├── p2p.rs │ │ ├── peer_handler.rs │ │ ├── rlpx.rs │ │ ├── rlpx │ │ │ ├── connection.rs │ │ │ ├── error.rs │ │ │ ├── eth │ │ │ │ ├── backend.rs │ │ │ │ ├── blocks.rs │ │ │ │ ├── eth68 │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── receipts.rs │ │ │ │ │ └── status.rs │ │ │ │ ├── eth69 │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── receipts.rs │ │ │ │ │ └── status.rs │ │ │ │ ├── mod.rs │ │ │ │ ├── receipts.rs │ │ │ │ ├── status.rs │ │ │ │ ├── transactions.rs │ │ │ │ └── update.rs │ │ │ ├── frame.rs │ │ │ ├── handshake.rs │ │ │ ├── message.rs │ │ │ ├── p2p.rs │ │ │ ├── snap.rs │ │ │ └── utils.rs │ │ ├── snap.rs │ │ ├── sync.rs │ │ ├── sync │ │ │ ├── bytecode_fetcher.rs │ │ │ ├── fetcher_queue.rs │ │ │ ├── state_healing.rs │ │ │ ├── state_sync.rs │ │ │ ├── storage_fetcher.rs │ │ │ ├── storage_healing.rs │ │ │ └── trie_rebuild.rs │ │ ├── sync_manager.rs │ │ └── types.rs │ └── rpc │ │ ├── Cargo.toml │ │ ├── admin │ │ ├── mod.rs │ │ └── peers.rs │ │ ├── authentication.rs │ │ ├── clients │ │ ├── auth │ │ │ ├── errors.rs │ │ │ └── mod.rs │ │ ├── beacon │ │ │ ├── errors.rs │ │ │ ├── mod.rs │ │ │ └── types.rs │ │ ├── eth │ │ │ ├── errors.rs │ │ │ ├── eth_sender.rs │ │ │ └── mod.rs │ │ └── mod.rs │ │ ├── engine │ │ ├── exchange_transition_config.rs │ │ ├── fork_choice.rs │ │ ├── mod.rs │ │ └── payload.rs │ │ ├── eth │ │ ├── account.rs │ │ ├── block.rs │ │ ├── client.rs │ │ ├── fee_market.rs │ │ ├── filter.rs │ │ ├── gas_price.rs │ │ ├── gas_tip_estimator.rs │ │ ├── logs.rs │ │ ├── max_priority_fee.rs │ │ ├── mod.rs │ │ └── transaction.rs │ │ ├── l2 │ │ ├── mod.rs │ │ ├── transaction.rs │ │ └── withdrawal.rs │ │ ├── lib.rs │ │ ├── mempool.rs │ │ ├── net │ │ └── mod.rs │ │ ├── rpc.rs │ │ ├── tracing.rs │ │ ├── types │ │ ├── account_proof.rs │ │ ├── block.rs │ │ ├── block_identifier.rs │ │ ├── fork_choice.rs │ │ ├── mod.rs │ │ ├── payload.rs │ │ ├── receipt.rs │ │ └── transaction.rs │ │ └── utils.rs ├── storage │ ├── Cargo.toml │ ├── api.rs │ ├── clippy.toml │ ├── error.rs │ ├── lib.rs │ ├── rlp.rs │ ├── store.rs │ ├── store_db │ │ ├── in_memory.rs │ │ ├── libmdbx.rs │ │ ├── mod.rs │ │ └── redb.rs │ ├── trie_db │ │ ├── libmdbx.rs │ │ ├── libmdbx_dupsort.rs │ │ ├── mod.rs │ │ ├── redb.rs │ │ ├── redb_multitable.rs │ │ ├── test_utils.rs │ │ └── utils.rs │ └── utils.rs └── vm │ ├── Cargo.toml │ ├── backends │ ├── levm │ │ ├── db.rs │ │ ├── mod.rs │ │ └── tracing.rs │ ├── mod.rs │ └── revm │ │ ├── db.rs │ │ ├── helpers.rs │ │ ├── mod.rs │ │ └── tracing.rs │ ├── constants.rs │ ├── db.rs │ ├── errors.rs │ ├── execution_result.rs │ ├── helpers.rs │ ├── levm │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── bench │ │ └── revm_comparison │ │ │ ├── .gitignore │ │ │ ├── Cargo.toml │ │ │ ├── README.md │ │ │ ├── contracts │ │ │ ├── BubbleSort.sol │ │ │ ├── Factorial.sol │ │ │ ├── FactorialRecursive.sol │ │ │ ├── Fibonacci.sol │ │ │ ├── ManyHashes.sol │ │ │ └── erc20 │ │ │ │ ├── ERC20ApprovalTransfer.sol │ │ │ │ ├── ERC20Mint.sol │ │ │ │ ├── ERC20Transfer.sol │ │ │ │ └── lib │ │ │ │ ├── Context.sol │ │ │ │ ├── ERC20.sol │ │ │ │ ├── IERC20.sol │ │ │ │ └── IERC20Metadata.sol │ │ │ └── src │ │ │ ├── benchmark.rs │ │ │ ├── compile.rs │ │ │ └── lib.rs │ ├── docs │ │ ├── callframe.md │ │ ├── database.md │ │ ├── faq.md │ │ ├── forks.md │ │ └── validations.md │ ├── src │ │ ├── call_frame.rs │ │ ├── constants.rs │ │ ├── db │ │ │ ├── cache.rs │ │ │ ├── error.rs │ │ │ ├── gen_db.rs │ │ │ └── mod.rs │ │ ├── environment.rs │ │ ├── errors.rs │ │ ├── execution_handlers.rs │ │ ├── gas_cost.rs │ │ ├── hooks │ │ │ ├── default_hook.rs │ │ │ ├── hook.rs │ │ │ ├── l2_hook.rs │ │ │ └── mod.rs │ │ ├── lib.rs │ │ ├── memory.rs │ │ ├── opcode_handlers │ │ │ ├── arithmetic.rs │ │ │ ├── bitwise_comparison.rs │ │ │ ├── block.rs │ │ │ ├── dup.rs │ │ │ ├── environment.rs │ │ │ ├── exchange.rs │ │ │ ├── keccak.rs │ │ │ ├── logging.rs │ │ │ ├── mod.rs │ │ │ ├── push.rs │ │ │ ├── stack_memory_storage_flow.rs │ │ │ └── system.rs │ │ ├── opcodes.rs │ │ ├── precompiles.rs │ │ ├── tracing.rs │ │ ├── utils.rs │ │ └── vm.rs │ └── tests │ │ ├── lib.rs │ │ ├── p_256_verify.json │ │ └── tests.rs │ ├── lib.rs │ ├── prover_db.rs │ └── tracing.rs ├── perf_reports └── 2025-04-03.md ├── quick-guide.md ├── rust-toolchain.toml ├── test_data ├── 2000-blocks.rlp ├── ERC20 │ ├── ERC20.bin │ │ └── TestToken.bin │ ├── ERC20.sol │ └── deps.sol ├── blobs │ ├── 1-1.blob │ ├── 2-1.blob │ ├── 3-1.blob │ ├── 4-1.blob │ └── 5-1.blob ├── chain.rlp ├── genesis-execution-api.json ├── genesis-hive.json ├── genesis-kurtosis.json ├── genesis-l1-dev.json ├── genesis-l1.json ├── genesis-l2.json ├── genesis-load-test.json ├── genesis-perf-ci.json ├── hive_clients.yml ├── l2-1k-erc20.rlp ├── l2-loadtest.rlp ├── load_test │ └── IOHeavyContract.sol ├── network_params.yaml ├── private_keys.txt ├── private_keys_l1.txt ├── rpc_prover │ └── cache_22240087.json └── rsp │ └── input │ └── 1 │ └── 21272632.bin └── tooling ├── genesis ├── Cargo.lock ├── Cargo.toml └── src │ ├── genesis.rs │ └── lib.rs ├── hive_report ├── Cargo.toml └── src │ └── main.rs ├── load_test ├── Cargo.toml ├── README.md └── src │ └── main.rs ├── loc ├── Cargo.toml ├── Makefile └── src │ ├── main.rs │ └── report.rs └── sync ├── Makefile └── readme.MD /.dockerignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | 3 | # Generated by Cargo 4 | # will have compiled files and executables 5 | **/target/ 6 | 7 | # These are backup files generated by rustfmt 8 | **/*.rs.bk 9 | 10 | cmd/ef_tests/blockchain/vectors 11 | cmd/ef_tests/state/vectors 12 | 13 | **/.DS_Store 14 | **/.vscode 15 | 16 | # EVM Mlir stuff 17 | **/*.mlir 18 | **/*.ll 19 | **/*.o 20 | **/*.asm 21 | crates/levm_mlir/output 22 | 23 | crates/levm_mlir/ethtests 24 | crates/levm_mlir/*.tar.gz 25 | 26 | # used by Flamegraph and Samply 27 | cmd/ef_tests/state/levm_perfgraphs 28 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | test_data/2000-blocks.rlp filter=lfs diff=lfs merge=lfs -text 2 | test_data/l2-1k-erc20.rlp filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Lambdaclass core team 2 | * @lambdaclass/lambda-execution-reviewers 3 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | We take the security of our project seriously. If you discover a vulnerability, we encourage you to report it responsibly so we can address it promptly. 6 | 7 | ### How to Report 8 | 9 | 1. Navigate to the **Security** tab of this repository. 10 | 2. Click on **"Report a Vulnerability"** to open the GitHub Security Advisories form. 11 | 3. Fill out the form with as much detail as possible, including: 12 | - A clear description of the issue. 13 | - Steps to reproduce the vulnerability. 14 | - The affected versions or components. 15 | - Any potential impact or severity details. 16 | 17 | Alternatively, you can send an email to **[security@lambdaclass.com](mailto:security@lambdaclass.com)** with the same details. 18 | 19 | ### Guidelines for Reporting 20 | 21 | - **Do not publicly disclose vulnerabilities** until we have confirmed and fixed the issue. 22 | - Include any proof-of-concept code, if possible, to help us verify the vulnerability more efficiently. 23 | - If applicable, specify if the vulnerability is already being exploited. 24 | 25 | ### Our Response Process 26 | 27 | - We commit to handling reports with diligence. 28 | - We will investigate all reported vulnerabilities thoroughly and transparently. 29 | - Once the vulnerability has been fixed, we will disclose the details publicly to ensure awareness and understanding. 30 | 31 | 32 | ### Reward Program 33 | 34 | While we do not currently offer a formal bug bounty program, we value your contribution and will recognize your efforts in our changelog or release notes (if you consent). 35 | 36 | Thank you for helping us improve the security of our project! 37 | -------------------------------------------------------------------------------- /.github/actionlint.yaml: -------------------------------------------------------------------------------- 1 | self-hosted-runner: 2 | # Labels of self-hosted runner in array of strings. 3 | labels: 4 | - gpu 5 | -------------------------------------------------------------------------------- /.github/config/assertoor/el-stability-check.yaml: -------------------------------------------------------------------------------- 1 | # This file is mix and match based upon the basic `assertoor-tests` playbooks: 2 | # - https://github.com/ethpandaops/assertoor-test/blob/master/assertoor-tests/stability-check.yaml 3 | # - https://github.com/ethpandaops/assertoor-test/blob/master/assertoor-tests/block-proposal-check.yaml 4 | # 5 | # For reference on each individual check see: https://github.com/ethpandaops/assertoor/wiki#supported-tasks-in-assertoor 6 | 7 | id: el-stability-check 8 | name: "Check Execution Stability" 9 | timeout: 18m 10 | tasks: 11 | - name: check_clients_are_healthy 12 | title: "Check if all clients are ready" 13 | timeout: 1m 14 | 15 | - name: run_tasks_concurrent 16 | title: "Check if all EL & CL clients are synced and the tx spammer is working" 17 | timeout: 5m 18 | config: 19 | tasks: 20 | - name: check_consensus_sync_status 21 | title: "Check if CL clients are synced" 22 | - name: check_execution_sync_status 23 | title: "Check if EL clients are synced" 24 | 25 | - name: run_task_matrix 26 | title: "Check block proposals from all client pairs with tx spammer working and >= 240 transactions" 27 | timeout: 8m 28 | configVars: 29 | matrixValues: "validatorPairNames" 30 | config: 31 | runConcurrent: true 32 | matrixVar: "validatorPairName" 33 | task: 34 | minTransactionCount: 240 35 | name: check_consensus_block_proposals 36 | title: "Wait for block proposal from ${validatorPairName}" 37 | configVars: 38 | validatorNamePattern: "validatorPairName" 39 | 40 | - name: run_tasks_concurrent 41 | title: "Check chain stability (reorgs and forks)" 42 | timeout: 7m 43 | config: 44 | tasks: 45 | - name: check_consensus_reorgs 46 | title: "Check consensus reorgs" 47 | - name: check_consensus_forks 48 | title: "Check consensus forks" 49 | 50 | 51 | -------------------------------------------------------------------------------- /.github/config/assertoor/network_params_blob.yaml: -------------------------------------------------------------------------------- 1 | participants: 2 | - el_type: geth 3 | el_image: ethereum/client-go:v1.15.2 4 | cl_type: lighthouse 5 | cl_image: sigp/lighthouse:v7.0.0-beta.0 6 | validator_count: 32 7 | count: 2 8 | - el_type: ethrex 9 | cl_type: lighthouse 10 | cl_image: sigp/lighthouse:v7.0.0-beta.0 11 | validator_count: 32 12 | 13 | network_params: 14 | # The address of the staking contract address on the Eth1 chain 15 | deposit_contract_address: "0x4242424242424242424242424242424242424242" 16 | 17 | additional_services: 18 | - assertoor 19 | - tx_fuzz 20 | - dora 21 | 22 | network_params: 23 | electra_fork_epoch: 1 24 | 25 | assertoor_params: 26 | run_stability_check: false 27 | run_block_proposal_check: false 28 | tests: 29 | - https://raw.githubusercontent.com/ethpandaops/assertoor/refs/heads/master/playbooks/stable/blob-transactions-test.yaml 30 | - https://raw.githubusercontent.com/lambdaclass/ethrex/refs/heads/main/.github/config/assertoor/el-stability-check.yaml 31 | 32 | tx_fuzz_params: 33 | tx_fuzz_extra_args: ["--txcount=3", "--accounts=80"] 34 | -------------------------------------------------------------------------------- /.github/config/assertoor/network_params_tx.yaml: -------------------------------------------------------------------------------- 1 | participants: 2 | - el_type: geth 3 | el_image: ethereum/client-go:v1.15.2 4 | cl_type: lighthouse 5 | cl_image: sigp/lighthouse:v7.0.0-beta.0 6 | validator_count: 32 7 | - el_type: ethrex 8 | cl_type: lighthouse 9 | cl_image: sigp/lighthouse:v7.0.0-beta.0 10 | validator_count: 32 11 | 12 | additional_services: 13 | - assertoor 14 | - dora 15 | 16 | network_params: 17 | electra_fork_epoch: 1 18 | 19 | # The address of the staking contract address on the Eth1 chain 20 | deposit_contract_address: "0x4242424242424242424242424242424242424242" 21 | 22 | assertoor_params: 23 | run_stability_check: false 24 | run_block_proposal_check: false 25 | tests: 26 | - https://raw.githubusercontent.com/ethpandaops/assertoor/refs/heads/master/playbooks/stable/eoa-transactions-test.yaml 27 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | **Motivation** 2 | 3 | 4 | 5 | **Description** 6 | 7 | 8 | 9 | 10 | 11 | Closes #issue_number 12 | 13 | -------------------------------------------------------------------------------- /.github/scripts/compare_ef_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # $1 Main branch tests results 4 | # $2 PR branch tests results 5 | main_results=$(cat "$1") 6 | IFS=$'\n' read -rd '' -a main_results <<<"${main_results}" 7 | 8 | 9 | pr_results=$(cat "$2") 10 | IFS=$'\n' read -rd '' -a pr_results <<<"${pr_results}" 11 | 12 | 13 | echo "# EF Tests Comparison" 14 | echo "|Test Name | MAIN | PR | DIFF | " 15 | echo "|----------|----------|----|------|" 16 | 17 | num=0 18 | for i in "${main_results[@]}" 19 | do 20 | name_main=$(echo "$i" | awk -F " " '{print $1}') 21 | result_main=$(echo "$i" | awk -F " " '{print $2}') 22 | result_main=${result_main%(*} 23 | percentage_main=$(echo "$i" | awk -F " " '{print $3}') 24 | 25 | name_pr=$(echo "${pr_results[num]}" | awk -F " " '{print $1}') 26 | result_pr=$(echo "${pr_results[num]}" | awk -F " " '{print $2}') 27 | result_pr=${result_pr%(*} 28 | percentage_pr=$(echo "${pr_results[num]}" | awk -F " " '{print $3}') 29 | 30 | passing_pr=${result_pr%/*} 31 | passing_main=${result_main%/*} 32 | difference=$(echo "$passing_pr - $passing_main" | bc) 33 | if [ $difference == "0" ]; then 34 | difference="" 35 | fi 36 | 37 | emoji="" 38 | if (( $(echo "$result_main > $result_pr" |bc -l) )); then 39 | emoji="⬇️️" 40 | elif (( $(echo "$result_main < $result_pr" |bc -l) )); then 41 | emoji="⬆️" 42 | else 43 | emoji="➖️" 44 | fi 45 | 46 | 47 | echo "|$name_main|$result_main $percentage_main |$result_pr $percentage_pr| $emoji $difference |" 48 | 49 | num=$((num + 1)) 50 | 51 | done 52 | -------------------------------------------------------------------------------- /.github/scripts/flamegraph_watcher.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # This script runs a load test and then kills the node under test. The load test sends a 5 | # transaction from each rich account to a random one, so we can check their nonce to 6 | # determine that the load test finished. 7 | # 8 | # Usage: 9 | # ./flamegraph_watcher.sh 10 | # Requires a PROGRAM variable to be set (e.g. ethrex). This $PROGRAM will be killed when the 11 | # load test finishes. Must be run from the context of the repo root. 12 | 13 | # TODO(#2486): Move this to a cached build outside. 14 | echo "Building load test" 15 | cargo build --release --manifest-path ./tooling/load_test/Cargo.toml 16 | 17 | echo "Starting load test" 18 | start_time=$(date +%s) 19 | RUST_BACKTRACE=1 ./target/release/load_test -k ./test_data/private_keys.txt -t eth-transfers -N 1000 -n http://localhost:1729 -w 1 20 | end_time=$(date +%s) 21 | 22 | elapsed=$((end_time - start_time)) 23 | minutes=$((elapsed / 60)) 24 | seconds=$((elapsed % 60)) 25 | echo "All load test transactions included in $minutes min $seconds s, killing node process." 26 | 27 | echo killing "$PROGRAM" 28 | sudo pkill "$PROGRAM" 29 | 30 | while pgrep -l "perf" >/dev/null; do 31 | echo "perf still alive, waiting for it to exit..." 32 | sleep 10 33 | done 34 | echo "perf exited" 35 | 36 | # We need this for the following job, to add to the static page 37 | echo "time=$minutes minutes $seconds seconds" >>"$GITHUB_OUTPUT" 38 | -------------------------------------------------------------------------------- /.github/scripts/formatter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # benches/formatter.sh 4 | 5 | FILE1=$1 6 | FILE2=$2 7 | 8 | cat <> $GITHUB_STEP_SUMMARY 50 | cat tooling/loc/loc_report_github.txt >> $GITHUB_STEP_SUMMARY 51 | 52 | - name: Post results to Slack 53 | env: 54 | SLACK_WEBHOOKS: > 55 | ${{ github.event_name == 'workflow_dispatch' 56 | && secrets.TEST_CHANNEL_SLACK 57 | || format( 58 | '{0} {1}', 59 | secrets.ETHREX_L1_SLACK_WEBHOOK, 60 | secrets.ETHREX_L2_SLACK_WEBHOOK 61 | ) 62 | }} 63 | run: | 64 | for webhook in $SLACK_WEBHOOKS; do 65 | sh .github/scripts/publish_loc.sh "$webhook" 66 | done 67 | -------------------------------------------------------------------------------- /.github/workflows/daily_reports.yaml: -------------------------------------------------------------------------------- 1 | name: Daily Reports 2 | 3 | on: 4 | schedule: 5 | # Every day at UTC midnight 6 | - cron: "0 0 * * 1,2,3,4,5" 7 | workflow_dispatch: 8 | 9 | jobs: 10 | hive-report-creation: 11 | name: Run Hive tests 12 | uses: ./.github/workflows/common_hive_reports.yaml 13 | with: 14 | evm: levm 15 | job_type: daily 16 | 17 | post-daily-report: 18 | name: Post report to slack 19 | needs: [hive-report-creation] 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout sources 23 | uses: actions/checkout@v4 24 | 25 | - name: Download hive results 26 | uses: actions/download-artifact@v4 27 | with: 28 | name: ${{ needs.hive-report-creation.outputs.report_artifact_name }} 29 | 30 | - name: Post Hive results to Slack 31 | env: 32 | SLACK_WEBHOOKS: > 33 | ${{ github.event_name == 'workflow_dispatch' 34 | && secrets.TEST_CHANNEL_SLACK 35 | || format( 36 | '{0} {1}', 37 | secrets.ETHREX_L1_SLACK_WEBHOOK, 38 | secrets.ETHREX_L2_SLACK_WEBHOOK 39 | ) 40 | }} 41 | run: | 42 | for webhook in $SLACK_WEBHOOKS; do 43 | sh .github/scripts/publish_hive.sh "$webhook" results.md 44 | done 45 | echo "Sending Results" >> $GITHUB_STEP_SUMMARY 46 | -------------------------------------------------------------------------------- /.github/workflows/main_docker_publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish Docker 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | workflow_dispatch: 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: ${{ github.repository }} 11 | 12 | jobs: 13 | build-and-push-image: 14 | name: Build and push Docker image 15 | runs-on: ubuntu-latest 16 | 17 | # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. 18 | permissions: 19 | contents: read 20 | packages: write 21 | attestations: write 22 | id-token: write 23 | 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v4 27 | 28 | - name: Log in to the Container registry 29 | uses: docker/login-action@v3 30 | with: 31 | registry: ${{ env.REGISTRY }} 32 | username: ${{ github.actor }} 33 | password: ${{ secrets.GITHUB_TOKEN }} 34 | 35 | # Generates the tags and labels based on the image name. The id allows using it in the next step. 36 | - name: Extract metadata (tags, labels) for Docker 37 | id: meta 38 | uses: docker/metadata-action@v5 39 | with: 40 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 41 | 42 | # Pushes to ghcr.io/ethrex 43 | - name: Build and push Docker image 44 | id: push 45 | uses: docker/build-push-action@v6 46 | with: 47 | context: . 48 | push: true 49 | tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest 50 | 51 | - name: Generate artifact attestation 52 | uses: actions/attest-build-provenance@v2 53 | with: 54 | subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 55 | subject-digest: ${{ steps.push.outputs.digest }} 56 | push-to-registry: true 57 | -------------------------------------------------------------------------------- /.github/workflows/main_perf_blocks_exec.yml: -------------------------------------------------------------------------------- 1 | name: Block Import Benchmark 2 | 3 | on: 4 | workflow_call: 5 | push: 6 | branches: [main] 7 | 8 | permissions: 9 | # deployments permission to deploy GitHub pages website 10 | deployments: write 11 | # contents permission to update benchmark contents in gh-pages branch 12 | contents: write 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - name: Install Rust 19 | uses: dtolnay/rust-toolchain@1.85.0 20 | - uses: actions/checkout@v4 21 | with: 22 | lfs: true 23 | - name: Checkout LFS objects 24 | run: git lfs checkout 25 | - name: Update package lists 26 | run: sudo apt-get update 27 | - name: Install gnuplot 28 | run: sudo apt-get install -y gnuplot 29 | - name: Run benchmark 30 | run: cd cmd/ethrex && cargo bench --bench import_blocks_benchmark -- --output-format bencher |sed 2d | tee output.txt 31 | - name: Store benchmark result 32 | uses: benchmark-action/github-action-benchmark@v1 33 | with: 34 | tool: "cargo" 35 | output-file-path: cmd/ethrex/output.txt 36 | benchmark-data-dir-path: "." 37 | # Access token to deploy GitHub Pages branch 38 | github-token: ${{ secrets.GITHUB_TOKEN }} 39 | # Push and deploy GitHub pages branch automatically 40 | auto-push: true 41 | alert-threshold: "130%" 42 | comment-on-alert: true 43 | -------------------------------------------------------------------------------- /.github/workflows/pr-main_l2_contracts.yaml: -------------------------------------------------------------------------------- 1 | name: L2 Contracts 2 | on: 3 | push: 4 | branches: ["main"] 5 | pull_request: 6 | branches: ["**"] 7 | paths: 8 | - "crates/l2/**" 9 | - ".github/workflows/pr-main_l2_contracts.yaml" 10 | 11 | concurrency: 12 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | test: 17 | # "Test" is a required check, don't change the name 18 | name: Test 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Checkout sources 22 | uses: actions/checkout@v4 23 | - name: Rust toolchain install 24 | uses: dtolnay/rust-toolchain@1.82.0 25 | - name: Install solc 26 | uses: pontem-network/get-solc@master 27 | with: 28 | version: v0.8.29 29 | - name: Caching 30 | uses: Swatinem/rust-cache@v2 31 | - name: Run test of deployer.rs 32 | run: | 33 | cd crates/l2/contracts 34 | cargo test 35 | -------------------------------------------------------------------------------- /.github/workflows/pr-main_l2_prover.yaml: -------------------------------------------------------------------------------- 1 | name: L2 Prover 2 | on: 3 | push: 4 | branches: ["main"] 5 | pull_request: 6 | branches: ["**"] 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | lint: 14 | # "Lint" is a required check, don't change the name 15 | name: Lint 16 | runs-on: ubuntu-latest 17 | strategy: 18 | fail-fast: true 19 | # backend: ["sp1", "risc0", "exec"] # TODO: fix Risc0 (https://github.com/lambdaclass/ethrex/issues/2145) 20 | steps: 21 | - name: Checkout sources 22 | uses: actions/checkout@v4 23 | - name: Add Rust Cache 24 | uses: Swatinem/rust-cache@v2 25 | - name: RISC-V SP1 toolchain install 26 | run: | 27 | curl -L https://sp1.succinct.xyz | bash 28 | ~/.sp1/bin/sp1up --version 4.1.7 29 | - name: Check sp1 30 | run: | 31 | cargo check -p ethrex-prover -F sp1 32 | cargo check -p ethrex-replay -F sp1 33 | - name: Clippy sp1 34 | run: | 35 | cargo clippy -p ethrex-prover --all-targets -F sp1 36 | - name: Check exec 37 | run: | 38 | cargo check -p ethrex-prover 39 | - name: Clippy exec 40 | run: | 41 | cargo clippy -p ethrex-prover --all-targets 42 | - name: Check tdx 43 | run: | 44 | cd crates/l2/tee/quote-gen 45 | cargo check 46 | - name: Clippy tdx 47 | run: | 48 | cd crates/l2/tee/quote-gen 49 | cargo clippy --all-targets 50 | -------------------------------------------------------------------------------- /.github/workflows/pr-main_l2_prover_nightly.yaml.disabled: -------------------------------------------------------------------------------- 1 | # DISABLED: Temporarily disabled due to Pico dependency issues 2 | 3 | # The reason this exists is because the Pico zkVM compiles in nightly only. 4 | name: L2 Prover (nightly) 5 | on: 6 | push: 7 | branches: ["main"] 8 | pull_request: 9 | branches: ["**"] 10 | paths: 11 | - "crates/l2/prover/**" 12 | - "test_data/**" 13 | - "crates/blockchain/dev/**" 14 | - "crates/vm/levm/**" 15 | - ".github/workflows/pr-main_l2_prover_nightly.yaml" 16 | 17 | concurrency: 18 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | lint: 23 | # "Lint" is a required check, don't change the name 24 | name: Lint 25 | runs-on: ubuntu-latest 26 | strategy: 27 | fail-fast: true 28 | matrix: 29 | action: 30 | - command: check 31 | args: -p ethrex-prover -F pico 32 | - command: clippy 33 | args: -p ethrex-prover -F pico --all-targets 34 | steps: 35 | - name: Checkout sources 36 | uses: actions/checkout@v4 37 | - name: Rust toolchain install 38 | uses: dtolnay/rust-toolchain@nightly 39 | with: 40 | toolchain: nightly-2024-11-27 41 | components: rust-src, clippy 42 | - name: Add Rust Cache 43 | uses: Swatinem/rust-cache@v2 44 | # https://pico-docs.brevis.network/getting-started/installation 45 | - name: Install pico-cli 46 | run: cargo +nightly install --git https://github.com/brevis-network/pico pico-cli 47 | - name: ${{ matrix.action.command }} Command 48 | run: cargo +nightly-2024-11-27 ${{ matrix.action.command }} ${{ matrix.action.args }} 49 | -------------------------------------------------------------------------------- /.github/workflows/pr-main_l2_tdx_build.yaml: -------------------------------------------------------------------------------- 1 | name: L2 TDX build 2 | on: 3 | push: 4 | branches: ["main"] 5 | pull_request: 6 | branches: ["**"] 7 | paths: 8 | - "**.nix" 9 | - "**/Cargo.toml" 10 | - "**/Cargo.lock" 11 | - ".github/workflows/pr-main_l2_tdx_build.yaml" 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | build_tdx: 19 | # "Test" is a required check, don't change the name 20 | name: Test 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout sources 24 | uses: actions/checkout@v4 25 | 26 | - name: Set up Nix 27 | uses: cachix/install-nix-action@v31 28 | 29 | - name: Build image 30 | run: | 31 | sudo sysctl kernel.unprivileged_userns_apparmor_policy=0 32 | sudo sysctl kernel.apparmor_restrict_unprivileged_userns=0 33 | cd crates/l2/tee/quote-gen 34 | make image.raw 35 | -------------------------------------------------------------------------------- /.github/workflows/pr_lint_gha.yaml: -------------------------------------------------------------------------------- 1 | name: Github Actions 2 | on: 3 | pull_request: 4 | branches: ["**"] 5 | paths: 6 | - ".github/**.yaml" 7 | - ".github/*.yml" 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | lint: 15 | name: Lint 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Checkout sources 19 | uses: actions/checkout@v4 20 | 21 | - name: actionlint 22 | uses: raven-actions/actionlint@v2 23 | with: 24 | flags: "-ignore SC2086 -ignore SC2006 -ignore SC2046" 25 | -------------------------------------------------------------------------------- /.github/workflows/pr_perf_changelog.yml: -------------------------------------------------------------------------------- 1 | name: Perf Changelog 2 | on: 3 | pull_request: 4 | types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] 5 | 6 | jobs: 7 | # Enforces the update of a changelog file on every pull request 8 | # We only want this for perf-related changes for now, so we add a few labels 9 | # for which the check is skipped 10 | changelog: 11 | if: contains(github.event.pull_request.labels.*.name, 'performance') 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: dangoslen/changelog-enforcer@v3 15 | -------------------------------------------------------------------------------- /.github/workflows/pr_perf_trie.yml: -------------------------------------------------------------------------------- 1 | name: Benchmark Trie 2 | 3 | on: 4 | workflow_call: 5 | pull_request: 6 | branches: ["**"] 7 | paths: 8 | - "crates/common/trie/**" 9 | 10 | permissions: 11 | pull-requests: write 12 | 13 | jobs: 14 | build-ethrex-trie: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | 20 | - name: Fetch Rust cache 21 | uses: Swatinem/rust-cache@v2 22 | 23 | - name: Install Rust 24 | uses: dtolnay/rust-toolchain@stable 25 | 26 | - name: Benchmarks 27 | uses: boa-dev/criterion-compare-action@v3 28 | with: 29 | cwd: "crates/common/trie" 30 | benchName: "trie_bench" 31 | branchName: ${{ github.base_ref }} 32 | token: ${{ secrets.GITHUB_TOKEN }} 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | **/target/ 5 | 6 | # These are backup files generated by rustfmt 7 | **/*.rs.bk 8 | 9 | # MSVC Windows builds of rustc generate these, which store debugging information 10 | *.pdb 11 | 12 | cmd/ef_tests/blockchain/vectors 13 | 14 | cmd/ef_tests/state/vectors 15 | 16 | # Repos checked out by make target 17 | hive/ 18 | ethereum-package/ 19 | 20 | .docker_build_stamp 21 | .ef_tests_stamp_file 22 | 23 | **/.DS_Store 24 | **/.vscode 25 | **/.idea 26 | **/.zed 27 | 28 | 29 | # EVM Mlir stuff 30 | **/*.mlir 31 | **/*.ll 32 | **/*.o 33 | **/*.asm 34 | crates/levm_mlir/output 35 | 36 | crates/levm_mlir/ethtests 37 | crates/levm_mlir/*.tar.gz 38 | 39 | # ethrex L2 stuff 40 | volumes 41 | jwt.hex 42 | 43 | # Used by the make test target 44 | /tmp 45 | 46 | tests*.tar.gz 47 | 48 | .env 49 | 50 | levm_ef_tests_report.txt 51 | levm_ef_tests_summary_slack.txt 52 | levm_ef_tests_summary_github.txt 53 | levm_ef_tests_summary.txt 54 | 55 | results.md 56 | loc_report.md 57 | loc_report_slack.txt 58 | loc_report_github.txt 59 | loc_report.json 60 | ethrex.redb 61 | 62 | # used by Flamegraph and Samply 63 | cmd/ef_tests/state/levm_perfgraphs 64 | *.svg 65 | prof_*.json 66 | prof.json 67 | profile.json.gz 68 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | rust 1.82.0 2 | # golang 1.23.2 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.82 AS chef 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | build-essential \ 5 | libclang-dev \ 6 | libc6 \ 7 | libssl-dev \ 8 | ca-certificates \ 9 | && rm -rf /var/lib/apt/lists/* 10 | RUN cargo install cargo-chef 11 | 12 | WORKDIR /ethrex 13 | 14 | FROM chef AS planner 15 | COPY crates ./crates 16 | COPY tooling ./tooling 17 | COPY cmd ./cmd 18 | COPY Cargo.* . 19 | # Determine the crates that need to be built from dependencies 20 | RUN cargo chef prepare --recipe-path recipe.json 21 | 22 | FROM chef AS builder 23 | COPY --from=planner /ethrex/recipe.json recipe.json 24 | # Build dependencies only, these remained cached 25 | RUN cargo chef cook --release --recipe-path recipe.json 26 | 27 | # Optional build flags 28 | ARG BUILD_FLAGS="" 29 | COPY crates ./crates 30 | COPY cmd ./cmd 31 | COPY Cargo.* ./ 32 | RUN cargo build --release $BUILD_FLAGS 33 | 34 | FROM ubuntu:24.04 35 | WORKDIR /usr/local/bin 36 | 37 | COPY cmd/ethrex/networks ./cmd/ethrex/networks 38 | COPY --from=builder ethrex/target/release/ethrex . 39 | EXPOSE 8545 40 | ENTRYPOINT [ "./ethrex" ] 41 | -------------------------------------------------------------------------------- /clippy.toml: -------------------------------------------------------------------------------- 1 | allow-unwrap-in-tests = true 2 | -------------------------------------------------------------------------------- /cmd/ef_tests/blockchain/.fixtures_url: -------------------------------------------------------------------------------- 1 | https://github.com/ethereum/execution-spec-tests/releases/download/v4.5.0/fixtures_develop.tar.gz 2 | -------------------------------------------------------------------------------- /cmd/ef_tests/blockchain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ef_tests-blockchain" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | ethrex-blockchain.workspace = true 8 | ethrex-common.workspace = true 9 | ethrex-storage.workspace = true 10 | ethrex-rlp.workspace = true 11 | ethrex-vm.workspace = true 12 | 13 | serde.workspace = true 14 | serde_json.workspace = true 15 | bytes.workspace = true 16 | hex.workspace = true 17 | lazy_static.workspace = true 18 | tokio = { workspace = true , features = ["full"] } 19 | 20 | [dev-dependencies] 21 | datatest-stable = "0.2.9" 22 | 23 | [lib] 24 | path = "./lib.rs" 25 | 26 | [features] 27 | default = ["c-kzg", "blst"] 28 | blst = ["ethrex-vm/blst"] 29 | c-kzg = ["ethrex-blockchain/c-kzg"] 30 | levm = [] 31 | 32 | [[test]] 33 | name = "cancun" 34 | harness = false 35 | 36 | [[test]] 37 | name = "shanghai" 38 | harness = false 39 | 40 | [[test]] 41 | name = "prague" 42 | harness = false 43 | -------------------------------------------------------------------------------- /cmd/ef_tests/blockchain/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: download-test-vectors clean-vectors test 2 | 3 | FIXTURES_FILE := .fixtures_url 4 | SPECTEST_ARTIFACT := tests.tar.gz 5 | SPECTEST_VECTORS_DIR := vectors 6 | 7 | SPECTEST_URL := $(shell cat $(FIXTURES_FILE)) 8 | 9 | $(SPECTEST_ARTIFACT): $(FIXTURES_FILE) 10 | $(MAKE) clean-vectors 11 | curl -L -o $(SPECTEST_ARTIFACT) $(SPECTEST_URL) 12 | 13 | $(SPECTEST_VECTORS_DIR): $(SPECTEST_ARTIFACT) 14 | mkdir -p $(SPECTEST_VECTORS_DIR) 15 | tar -xzf $(SPECTEST_ARTIFACT) --strip-components=2 -C $(SPECTEST_VECTORS_DIR) fixtures/blockchain_tests 16 | 17 | help: ## 📚 Show help for each of the Makefile recipes 18 | @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 19 | 20 | download-test-vectors: $(SPECTEST_VECTORS_DIR) ## 📥 Download test vectors 21 | 22 | clean-vectors: ## 🗑️ Clean test vectors 23 | rm -rf $(SPECTEST_VECTORS_DIR) 24 | rm -f $(SPECTEST_ARTIFACT) 25 | 26 | test-levm: $(SPECTEST_VECTORS_DIR) ## 🧪 Run blockchain tests with LEVM 27 | cargo test --release --features levm 28 | 29 | test-revm: $(SPECTEST_VECTORS_DIR) ## 🧪 Run blockchain tests with REVM 30 | cargo test --release 31 | 32 | test: $(SPECTEST_VECTORS_DIR) ## 🧪 Run blockchain tests with both VMs 33 | $(MAKE) test-levm 34 | $(MAKE) test-revm 35 | -------------------------------------------------------------------------------- /cmd/ef_tests/blockchain/README.md: -------------------------------------------------------------------------------- 1 | # Blockchain Tests 2 | The blockchain tests test block validation and the consensus rules of the Ethereum blockchain. Tests are usually run for multiple forks. 3 | Some [docs](https://ethereum.github.io/execution-spec-tests/main/consuming_tests/blockchain_test/). 4 | 5 | ## Running the tests 6 | 7 | ```bash 8 | make test 9 | ``` 10 | 11 | ## Running the tests for either levm or revm 12 | 13 | ```bash 14 | make test-levm 15 | ``` 16 | or 17 | ```bash 18 | make test-revm 19 | ``` 20 | -------------------------------------------------------------------------------- /cmd/ef_tests/blockchain/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod network; 2 | pub mod test_runner; 3 | pub mod types; 4 | -------------------------------------------------------------------------------- /cmd/ef_tests/blockchain/tests/cancun.rs: -------------------------------------------------------------------------------- 1 | use ef_tests_blockchain::test_runner::parse_and_execute; 2 | use ethrex_vm::EvmEngine; 3 | use std::path::Path; 4 | 5 | #[cfg(not(feature = "levm"))] 6 | fn parse_and_execute_with_revm(path: &Path) -> datatest_stable::Result<()> { 7 | parse_and_execute(path, EvmEngine::REVM, None); 8 | Ok(()) 9 | } 10 | 11 | #[cfg(feature = "levm")] 12 | fn parse_and_execute_with_levm(path: &Path) -> datatest_stable::Result<()> { 13 | parse_and_execute(path, EvmEngine::LEVM, None); 14 | Ok(()) 15 | } 16 | 17 | // REVM execution 18 | #[cfg(not(feature = "levm"))] 19 | datatest_stable::harness!( 20 | parse_and_execute_with_revm, 21 | "vectors/cancun/", 22 | r".*/.*\.json", 23 | ); 24 | 25 | // LEVM execution 26 | #[cfg(feature = "levm")] 27 | datatest_stable::harness!( 28 | parse_and_execute_with_levm, 29 | "vectors/cancun/", 30 | r".*/.*\.json", 31 | ); 32 | -------------------------------------------------------------------------------- /cmd/ef_tests/blockchain/tests/prague.rs: -------------------------------------------------------------------------------- 1 | use std::path::Path; 2 | 3 | use ef_tests_blockchain::test_runner::parse_and_execute; 4 | use ethrex_vm::EvmEngine; 5 | 6 | #[cfg(not(feature = "levm"))] 7 | fn parse_and_execute_with_revm(path: &Path) -> datatest_stable::Result<()> { 8 | parse_and_execute(path, EvmEngine::REVM, None); 9 | Ok(()) 10 | } 11 | 12 | #[cfg(feature = "levm")] 13 | fn parse_and_execute_with_levm(path: &Path) -> datatest_stable::Result<()> { 14 | parse_and_execute(path, EvmEngine::LEVM, None); 15 | Ok(()) 16 | } 17 | 18 | // REVM execution 19 | #[cfg(not(feature = "levm"))] 20 | datatest_stable::harness!( 21 | parse_and_execute_with_revm, 22 | "vectors/prague/", 23 | r".*/.*\.json", 24 | ); 25 | 26 | // LEVM execution 27 | #[cfg(feature = "levm")] 28 | datatest_stable::harness!( 29 | parse_and_execute_with_levm, 30 | "vectors/prague/", 31 | r".*/.*\.json", 32 | ); 33 | -------------------------------------------------------------------------------- /cmd/ef_tests/blockchain/tests/shanghai.rs: -------------------------------------------------------------------------------- 1 | use ef_tests_blockchain::test_runner::parse_and_execute; 2 | use ethrex_vm::EvmEngine; 3 | use std::path::Path; 4 | 5 | #[cfg(not(feature = "levm"))] 6 | fn parse_and_execute_with_revm(path: &Path) -> datatest_stable::Result<()> { 7 | parse_and_execute(path, EvmEngine::REVM, None); 8 | Ok(()) 9 | } 10 | 11 | #[cfg(feature = "levm")] 12 | fn parse_and_execute_with_levm(path: &Path) -> datatest_stable::Result<()> { 13 | parse_and_execute(path, EvmEngine::LEVM, None); 14 | Ok(()) 15 | } 16 | 17 | // REVM execution 18 | #[cfg(not(feature = "levm"))] 19 | datatest_stable::harness!( 20 | parse_and_execute_with_revm, 21 | "vectors/shanghai/", 22 | r".*/.*/.*\.json", 23 | ); 24 | 25 | // LEVM execution 26 | #[cfg(feature = "levm")] 27 | datatest_stable::harness!( 28 | parse_and_execute_with_levm, 29 | "vectors/shanghai/", 30 | r".*/.*/.*\.json", 31 | ); 32 | -------------------------------------------------------------------------------- /cmd/ef_tests/state/.fixtures_url: -------------------------------------------------------------------------------- 1 | https://github.com/ethereum/execution-spec-tests/releases/download/v4.5.0/fixtures_develop.tar.gz 2 | -------------------------------------------------------------------------------- /cmd/ef_tests/state/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ef_tests-state" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | ethrex-blockchain.workspace = true 8 | ethrex-common.workspace = true 9 | ethrex-storage.workspace = true 10 | ethrex-rlp.workspace = true 11 | ethrex-vm.workspace = true 12 | ethrex-levm.workspace = true 13 | 14 | serde.workspace = true 15 | serde_json.workspace = true 16 | bytes.workspace = true 17 | hex.workspace = true 18 | keccak-hash.workspace = true 19 | colored = "2.1.0" 20 | spinoff = "0.8.0" 21 | thiserror.workspace = true 22 | clap.workspace = true 23 | clap_complete.workspace = true 24 | itertools = "0.13.0" 25 | revm = { version = "19.0.0", features = [ 26 | "serde", 27 | "std", 28 | "serde-json", 29 | "optional_no_base_fee", 30 | "optional_block_gas_limit", 31 | ], default-features = false } 32 | alloy-rlp = "0.3.12" 33 | tokio = { workspace = true, features = ["full"] } 34 | 35 | [dev-dependencies] 36 | hex = "0.4.3" 37 | 38 | [lib] 39 | path = "./lib.rs" 40 | 41 | [features] 42 | default = ["c-kzg", "blst"] 43 | c-kzg = ["ethrex-vm/c-kzg", "ethrex-levm/c-kzg", "ethrex-common/c-kzg"] 44 | blst = ["ethrex-vm/blst"] 45 | l2 = ["ethrex-vm/l2", "ethrex-levm/l2"] 46 | 47 | [[test]] 48 | name = "all" 49 | harness = false 50 | -------------------------------------------------------------------------------- /cmd/ef_tests/state/lib.rs: -------------------------------------------------------------------------------- 1 | mod deserialize; 2 | pub mod parser; 3 | mod report; 4 | pub mod runner; 5 | pub mod types; 6 | mod utils; 7 | -------------------------------------------------------------------------------- /cmd/ef_tests/state/tests/all.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use ef_tests_state::{ 3 | parser, 4 | runner::{self, EFTestRunnerOptions}, 5 | }; 6 | use std::error::Error; 7 | 8 | #[tokio::main] 9 | async fn main() -> Result<(), Box> { 10 | let opts = EFTestRunnerOptions::parse(); 11 | dbg!(&opts); // Useful for testing. 12 | let ef_tests = parser::parse_ef_tests(&opts)?; 13 | runner::run_ef_tests(ef_tests, &opts).await?; 14 | Ok(()) 15 | } 16 | -------------------------------------------------------------------------------- /cmd/ethrex/bench/import_blocks_benchmark.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use ethrex::{ 3 | cli::{import_blocks, remove_db}, 4 | networks::Network, 5 | utils::set_datadir, 6 | DEFAULT_DATADIR, 7 | }; 8 | use ethrex_vm::EvmEngine; 9 | 10 | #[inline] 11 | fn block_import() { 12 | let data_dir = DEFAULT_DATADIR; 13 | set_datadir(data_dir); 14 | remove_db(data_dir, true); 15 | 16 | let evm_engine = EvmEngine::default(); 17 | 18 | let network = Network::from("../../test_data/genesis-perf-ci.json"); 19 | 20 | let rt = tokio::runtime::Runtime::new().unwrap(); 21 | rt.block_on(import_blocks( 22 | "../../test_data/l2-1k-erc20.rlp", 23 | data_dir, 24 | network.get_genesis(), 25 | evm_engine, 26 | )) 27 | .expect("Failed to import blocks on the Tokio runtime"); 28 | } 29 | 30 | pub fn import_blocks_benchmark(c: &mut Criterion) { 31 | let mut group = c.benchmark_group("Block import"); 32 | group.sample_size(10); 33 | group.bench_function("Block import ERC20 transfers", |b| b.iter(block_import)); 34 | group.finish(); 35 | } 36 | 37 | criterion_group!(runner, import_blocks_benchmark); 38 | criterion_main!(runner); 39 | -------------------------------------------------------------------------------- /cmd/ethrex/build.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use vergen::{Emitter, RustcBuilder}; 3 | 4 | // This build code is needed to add some env vars in order to construct the node client version 5 | // VERGEN_RUSTC_COMMIT_HASH to get the commit hash 6 | // VERGEN_RUSTC_HOST_TRIPLE to get the build OS 7 | // VERGEN_RUSTC_SEMVER to get the rustc version 8 | 9 | fn main() -> Result<(), Box> { 10 | let rustc = RustcBuilder::all_rustc()?; 11 | 12 | Emitter::default().add_instructions(&rustc)?.emit()?; 13 | Ok(()) 14 | } 15 | -------------------------------------------------------------------------------- /cmd/ethrex/l2/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod command; 2 | pub mod options; 3 | 4 | pub use command::Command; 5 | pub use options::{ 6 | BlockProducerOptions, CommitterOptions, EthOptions, Options as L2Options, 7 | ProofCoordinatorOptions, SequencerOptions, WatcherOptions, 8 | }; 9 | -------------------------------------------------------------------------------- /cmd/ethrex/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod initializers; 2 | pub mod utils; 3 | 4 | pub mod cli; 5 | 6 | #[cfg(feature = "l2")] 7 | pub mod l2; 8 | 9 | mod decode; 10 | pub mod networks; 11 | 12 | pub const DEFAULT_DATADIR: &str = "ethrex"; 13 | pub const DEFAULT_L2_DATADIR: &str = "ethrex-l2"; 14 | -------------------------------------------------------------------------------- /cmd/ethrex/networks/holesky/bootnodes.json: -------------------------------------------------------------------------------- 1 | [ 2 | "enode://ac906289e4b7f12df423d654c5a962b6ebe5b3a74cc9e06292a85221f9a64a6f1cfdd6b714ed6dacef51578f92b34c60ee91e9ede9c7f8fadc4d347326d95e2b@146.190.13.128:30303", 3 | "enode://a3435a0155a3e837c02f5e7f5662a2f1fbc25b48e4dc232016e1c51b544cb5b4510ef633ea3278c0e970fa8ad8141e2d4d0f9f95456c537ff05fdf9b31c15072@178.128.136.233:30303", 4 | "enode://7fa09f1e8bb179ab5e73f45d3a7169a946e7b3de5ef5cea3a0d4546677e4435ee38baea4dd10b3ddfdc1f1c5e869052932af8b8aeb6f9738598ec4590d0b11a6@65.109.94.124:30303", 5 | "enode://3524632a412f42dee4b9cc899b946912359bb20103d7596bddb9c8009e7683b7bff39ea20040b7ab64d23105d4eac932d86b930a605e632357504df800dba100@172.174.35.249:30303" 6 | ] 7 | -------------------------------------------------------------------------------- /cmd/ethrex/networks/mainnet/bootnodes.json: -------------------------------------------------------------------------------- 1 | [ 2 | "enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@18.138.108.67:30303", 3 | "enode://22a8232c3abc76a16ae9d6c3b164f98775fe226f0917b0ca871128a74a8e9630b458460865bab457221f1d448dd9791d24c4e5d88786180ac185df813a68d4de@3.209.45.79:30303", 4 | "enode://2b252ab6a1d0f971d9722cb839a42cb81db019ba44c08754628ab4a823487071b5695317c8ccd085219c3a03af063495b2f1da8d18218da2d6a82981b45e6ffc@65.108.70.101:30303", 5 | "enode://4aeb4ab6c14b23e2c4cfdce879c04b0748a20d8e9b59e25ded2a08143e265c6c25936e74cbc8e641e3312ca288673d91f2f93f8e277de3cfa444ecdaaf982052@157.90.35.166:30303" 6 | ] 7 | -------------------------------------------------------------------------------- /cmd/ethrex/networks/sepolia/bootnodes.json: -------------------------------------------------------------------------------- 1 | [ 2 | "enode://4e5e92199ee224a01932a377160aa432f31d0b351f84ab413a8e0a42f4f36476f8fb1cbe914af0d9aef0d51665c214cf653c651c4bbd9d5550a934f241f1682b@138.197.51.181:30303", 3 | "enode://143e11fb766781d22d92a2e33f8f104cddae4411a122295ed1fdb6638de96a6ce65f5b7c964ba3763bba27961738fef7d3ecc739268f3e5e771fb4c87b6234ba@146.190.1.103:30303", 4 | "enode://8b61dc2d06c3f96fddcbebb0efb29d60d3598650275dc469c22229d3e5620369b0d3dedafd929835fe7f489618f19f456fe7c0df572bf2d914a9f4e006f783a9@170.64.250.88:30303", 5 | "enode://10d62eff032205fcef19497f35ca8477bea0eadfff6d769a147e895d8b2b8f8ae6341630c645c30f5df6e67547c03494ced3d9c5764e8622a26587b083b028e8@139.59.49.206:30303", 6 | "enode://9e9492e2e8836114cc75f5b929784f4f46c324ad01daf87d956f98b3b6c5fcba95524d6e5cf9861dc96a2c8a171ea7105bb554a197455058de185fa870970c7c@138.68.123.152:30303" 7 | ] 8 | -------------------------------------------------------------------------------- /cmd/ethrex_replay/.gitignore: -------------------------------------------------------------------------------- 1 | cache_*.json 2 | bench_latest.json 3 | -------------------------------------------------------------------------------- /cmd/ethrex_replay/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-replay" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | ethrex-common.workspace = true 8 | ethrex-vm.workspace = true 9 | ethrex-storage.workspace = true 10 | ethrex-rlp.workspace = true 11 | ethrex-trie.workspace = true 12 | ethrex-levm.workspace = true 13 | 14 | zkvm_interface = { path = "../../crates/l2/prover/zkvm/interface", default-features = false } 15 | ethrex-prover = { path = "../../crates/l2/prover", default-features = false } 16 | ethrex-l2 = { path = "../../crates/l2", default-features = false } 17 | 18 | serde.workspace = true 19 | hex.workspace = true 20 | serde_json.workspace = true 21 | bytes.workspace = true 22 | 23 | tracing-subscriber = { workspace = true, features = ["env-filter"] } 24 | tracing.workspace = true 25 | eyre.workspace = true 26 | 27 | tokio = { version = "1.21", default-features = false, features = ["full"] } 28 | reqwest = { version = "0.12.9", features = [ 29 | "json", 30 | "rustls-tls", 31 | ], default-features = false } 32 | clap.workspace = true 33 | 34 | revm = { version = "19.0.0", features = [ 35 | "serde", 36 | "std", 37 | "serde-json", 38 | "optional_no_base_fee", 39 | "optional_block_gas_limit", 40 | ], default-features = false } 41 | 42 | # These dependencies must be kept up to date with the corresponding revm version, otherwise errors may pop up because of trait implementation mismatches 43 | revm-inspectors = { version = "0.15.0" } 44 | derive_more = { version = "1.0.0", features = ["full"] } 45 | revm-primitives = { version = "15.2.0", features = [ 46 | "std", 47 | ], default-features = false } 48 | futures-util = "0.3.31" 49 | tokio-utils = "0.1.2" 50 | again = "0.1.2" 51 | lazy_static = "1.5.0" 52 | bincode = "1.3.3" 53 | 54 | 55 | [features] 56 | pico = ["zkvm_interface/pico", "ethrex-prover/pico"] 57 | risc0 = ["zkvm_interface/risc0", "ethrex-prover/risc0"] 58 | sp1 = ["zkvm_interface/sp1", "ethrex-prover/sp1"] 59 | gpu = ["ethrex-prover/gpu"] 60 | l2 = ["ethrex-vm/l2", "zkvm_interface/l2", "ethrex-l2/l2"] 61 | ci = [] 62 | -------------------------------------------------------------------------------- /cmd/ethrex_replay/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: sp1 2 | 3 | NETWORK ?= mainnet 4 | ifdef BLOCK_NUMBER 5 | REPLAY_ARGS = ${BLOCK_NUMBER} 6 | endif 7 | REPLAY_ARGS += --rpc-url ${RPC_URL} --network ${NETWORK} --bench 8 | 9 | # Execution only 10 | sp1: 11 | SP1_PROVER=cpu cargo r -r --features sp1 -- execute block ${REPLAY_ARGS} 12 | sp1-gpu: 13 | SP1_PROVER=cuda cargo r -r --features "sp1,gpu" -- execute block ${REPLAY_ARGS} 14 | risc0: 15 | cargo r -r --no-default-features --features risc0 -- execute block ${REPLAY_ARGS} 16 | risc0-gpu: 17 | cargo r -r --no-default-features --features "risc0,gpu" -- execute block ${REPLAY_ARGS} 18 | pico: 19 | cargo +nightly r -r --features pico -- execute block ${REPLAY_ARGS} 20 | pico-gpu: 21 | cargo +nightly r -r --features "pico,gpu" -- execute block ${REPLAY_ARGS} 22 | 23 | # Proving 24 | prove-sp1: 25 | SP1_PROVER=cpu cargo r -r --features sp1 -- prove block ${REPLAY_ARGS} 26 | prove-sp1-gpu: 27 | SP1_PROVER=cuda cargo r -r --features "sp1,gpu" -- prove block ${REPLAY_ARGS} 28 | prove-sp1-gpu-ci: 29 | SP1_PROVER=cuda cargo r -r --features "sp1,gpu,ci" -- prove block ${REPLAY_ARGS} 30 | prove-risc0: 31 | cargo r -r --no-default-features --features risc0 -- prove block ${REPLAY_ARGS} 32 | prove-risc0-gpu: 33 | cargo r -r --no-default-features --features "risc0,gpu" -- prove block ${REPLAY_ARGS} 34 | pico: 35 | cargo +nightly r -r --features pico -- prove block ${REPLAY_ARGS} 36 | pico-gpu: 37 | cargo +nightly r -r --features "pico,gpu" -- prove block ${REPLAY_ARGS} 38 | -------------------------------------------------------------------------------- /cmd/ethrex_replay/README.md: -------------------------------------------------------------------------------- 1 | # L1 block prover 2 | 3 | ## Usage 4 | 5 | 1. For now we only support SP1. Install their [toolchain](https://docs.succinct.xyz/docs/sp1/introduction) first (version 4.1.7). 6 | 2. Run: 7 | 1. `make sp1 RPC_URL= BLOCK_NUMBER=` for execution without proving 8 | 2. `make prove-sp1 RPC_URL= BLOCK_NUMBER=` for generating a proof. 9 | 3. `make prove-sp1-gpu RPC_URL= BLOCK_NUMBER=` for generating a proof with GPU acceleration. 10 | 11 | If `BLOCK_NUMBER` is not defined then the latest block will be selected. 12 | -------------------------------------------------------------------------------- /cmd/ethrex_replay/src/bench.rs: -------------------------------------------------------------------------------- 1 | use serde_json::json; 2 | use std::fs::File; 3 | 4 | pub fn write_benchmark_file(gas_used: f64, elapsed: f64) { 5 | let rate = gas_used / 1e6 / elapsed; 6 | 7 | let backend = if cfg!(feature = "sp1") { 8 | "SP1" 9 | } else if cfg!(feature = "risc0") { 10 | "Risc0" 11 | } else if cfg!(feature = "pico") { 12 | "Pico" 13 | } else { 14 | "Exec" 15 | }; 16 | 17 | let processor = if cfg!(feature = "ci") { 18 | "RTX A6000" 19 | } else if cfg!(feature = "gpu") { 20 | "GPU" 21 | } else { 22 | "CPU" 23 | }; 24 | 25 | let benchmark_json = &json!([{ 26 | "name": format!("{backend}, {}", processor), 27 | "unit": "Mgas/s", 28 | "value": rate 29 | }]); 30 | let file = File::create("bench_latest.json").expect("failed to create bench_latest.json"); 31 | serde_json::to_writer(file, benchmark_json).expect("failed to write to bench_latest.json"); 32 | } 33 | 34 | pub async fn run_and_measure(write_to_file: bool, future: Ft) -> eyre::Result 35 | where 36 | Ft: std::future::Future>, 37 | { 38 | let now = std::time::Instant::now(); 39 | let (gas_used, ret) = future.await?; 40 | let elapsed = now.elapsed().as_secs(); 41 | if write_to_file { 42 | write_benchmark_file(gas_used, elapsed as f64); 43 | } 44 | Ok(ret) 45 | } 46 | -------------------------------------------------------------------------------- /cmd/ethrex_replay/src/cache.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::File, 3 | io::{BufReader, BufWriter}, 4 | }; 5 | 6 | use ethrex_common::types::{Block, BlockHeader}; 7 | use ethrex_vm::ProverDB; 8 | 9 | use serde::{Deserialize, Serialize}; 10 | 11 | #[derive(Serialize, Deserialize)] 12 | pub struct Cache { 13 | pub blocks: Vec, 14 | pub parent_block_header: BlockHeader, 15 | pub db: ProverDB, 16 | } 17 | 18 | pub fn load_cache(block_number: usize) -> eyre::Result { 19 | let file_name = format!("cache_{}.json", block_number); 20 | let file = BufReader::new(File::open(file_name)?); 21 | Ok(serde_json::from_reader(file)?) 22 | } 23 | 24 | pub fn write_cache(cache: &Cache) -> eyre::Result<()> { 25 | if cache.blocks.is_empty() { 26 | return Err(eyre::Error::msg("cache can't be empty")); 27 | } 28 | if cache.blocks.len() > 1 { 29 | return Err(eyre::Error::msg("trying to save a multi-block cache")); 30 | } 31 | let file_name = format!("cache_{}.json", cache.blocks[0].header.number); 32 | let file = BufWriter::new(File::create(file_name)?); 33 | Ok(serde_json::to_writer(file, cache)?) 34 | } 35 | 36 | pub fn load_cache_batch(from: usize, to: usize) -> eyre::Result { 37 | let file_name = format!("cache_{}-{}.json", from, to); 38 | let file = BufReader::new(File::open(file_name)?); 39 | Ok(serde_json::from_reader(file)?) 40 | } 41 | 42 | pub fn write_cache_batch(cache: &Cache) -> eyre::Result<()> { 43 | let from = cache 44 | .blocks 45 | .first() 46 | .ok_or(eyre::Error::msg("cache is empty"))? 47 | .header 48 | .number; 49 | let to = cache 50 | .blocks 51 | .last() 52 | .ok_or(eyre::Error::msg("cache is empty"))? 53 | .header 54 | .number; 55 | let file_name = format!("cache_{}-{}.json", from, to); 56 | let file = BufWriter::new(File::create(file_name)?); 57 | Ok(serde_json::to_writer(file, cache)?) 58 | } 59 | -------------------------------------------------------------------------------- /cmd/ethrex_replay/src/main.rs: -------------------------------------------------------------------------------- 1 | mod bench; 2 | mod cache; 3 | mod cli; 4 | mod constants; 5 | mod fetcher; 6 | mod rpc; 7 | mod run; 8 | 9 | #[tokio::main] 10 | async fn main() { 11 | tracing_subscriber::fmt() 12 | .with_max_level(tracing::Level::ERROR) 13 | .init(); 14 | 15 | match cli::start().await { 16 | Ok(_) => {} 17 | Err(err) => { 18 | tracing::error!("{err:?}"); 19 | std::process::exit(1); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /crates/blockchain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-blockchain" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ethrex-rlp.workspace = true 10 | ethrex-common.workspace = true 11 | ethrex-storage.workspace = true 12 | ethrex-vm.workspace = true 13 | 14 | thiserror.workspace = true 15 | sha3.workspace = true 16 | tracing.workspace = true 17 | bytes.workspace = true 18 | cfg-if = "1.0.0" 19 | tokio = { workspace = true, features = ["time", "rt"] } 20 | 21 | k256 = { version = "0.13.3", features = ["ecdh"] } 22 | 23 | ethrex-metrics = { path = "./metrics", default-features = false } 24 | 25 | [dev-dependencies] 26 | serde_json.workspace = true 27 | hex = "0.4.3" 28 | tokio.workspace = true 29 | 30 | [lib] 31 | path = "./blockchain.rs" 32 | 33 | [features] 34 | default = [] 35 | l2 = [] 36 | c-kzg = ["ethrex-common/c-kzg", "ethrex-vm/c-kzg"] 37 | metrics = ["ethrex-metrics/transactions"] 38 | -------------------------------------------------------------------------------- /crates/blockchain/constants.rs: -------------------------------------------------------------------------------- 1 | // === YELLOW PAPER constants === 2 | 3 | /// Base gas cost for each non contract creating transaction 4 | pub const TX_GAS_COST: u64 = 21000; 5 | 6 | /// Base gas cost for each contract creating transaction 7 | pub const TX_CREATE_GAS_COST: u64 = 53000; 8 | 9 | // Gas cost for each zero byte on transaction data 10 | pub const TX_DATA_ZERO_GAS_COST: u64 = 4; 11 | 12 | // Gas cost for each init code word on transaction data 13 | pub const TX_INIT_CODE_WORD_GAS_COST: u64 = 2; 14 | 15 | // Gas cost for each address specified on access lists 16 | pub const TX_ACCESS_LIST_ADDRESS_GAS: u64 = 2400; 17 | 18 | // Gas cost for each storage key specified on access lists 19 | pub const TX_ACCESS_LIST_STORAGE_KEY_GAS: u64 = 1900; 20 | 21 | // Gas cost for each non zero byte on transaction data 22 | pub const TX_DATA_NON_ZERO_GAS: u64 = 68; 23 | 24 | // === EIP-170 constants === 25 | 26 | // Max bytecode size 27 | pub const MAX_CODE_SIZE: usize = 0x6000; 28 | 29 | // === EIP-3860 constants === 30 | 31 | // Max contract creation bytecode size 32 | pub const MAX_INITCODE_SIZE: usize = 2 * MAX_CODE_SIZE; 33 | 34 | // === EIP-2028 constants === 35 | 36 | // Gas cost for each non zero byte on transaction data 37 | pub const TX_DATA_NON_ZERO_GAS_EIP2028: u64 = 16; 38 | 39 | // === EIP-4844 constants === 40 | 41 | pub const GAS_LIMIT_BOUND_DIVISOR: u64 = 1024; 42 | 43 | pub const MIN_GAS_LIMIT: u64 = 5000; 44 | -------------------------------------------------------------------------------- /crates/blockchain/dev/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-dev" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ethrex-rpc.workspace = true 10 | 11 | tokio.workspace = true 12 | tracing.workspace = true 13 | thiserror.workspace = true 14 | jsonwebtoken.workspace = true 15 | serde_json.workspace = true 16 | serde.workspace = true 17 | bytes.workspace = true 18 | ethereum-types.workspace = true 19 | hex.workspace = true 20 | reqwest = { version = "0.12.7", features = ["json"] } 21 | envy = "0.4.2" 22 | keccak-hash.workspace = true 23 | sha2 = "0.10.8" 24 | 25 | [lib] 26 | path = "./dev.rs" 27 | -------------------------------------------------------------------------------- /crates/blockchain/dev/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.82 AS chef 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | build-essential \ 5 | libclang-dev \ 6 | libc6 \ 7 | libssl-dev \ 8 | ca-certificates \ 9 | && rm -rf /var/lib/apt/lists/* 10 | RUN cargo install cargo-chef 11 | 12 | WORKDIR /ethrex 13 | 14 | FROM chef AS planner 15 | COPY . . 16 | # Determine the crates that need to be built from dependencies 17 | RUN cargo chef prepare --recipe-path recipe.json 18 | 19 | FROM chef AS builder 20 | COPY --from=planner /ethrex/recipe.json recipe.json 21 | # Build dependencies only, these remained cached 22 | RUN cargo chef cook --release --recipe-path recipe.json 23 | 24 | COPY . . 25 | RUN cargo build --release --features "dev" 26 | 27 | FROM ubuntu:24.04 28 | WORKDIR /usr/local/bin 29 | 30 | COPY --from=builder ethrex/target/release/ethrex . 31 | EXPOSE 8545 32 | ENTRYPOINT [ "./ethrex" ] 33 | -------------------------------------------------------------------------------- /crates/blockchain/dev/dev.rs: -------------------------------------------------------------------------------- 1 | pub mod block_producer; 2 | -------------------------------------------------------------------------------- /crates/blockchain/dev/docker-compose-dev.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | ethrex: 3 | restart: always 4 | container_name: ethrex_l1 5 | image: "ethrex_dev" 6 | build: 7 | context: ../../../ 8 | dockerfile: ./crates/blockchain/dev/Dockerfile 9 | ports: 10 | - 127.0.0.1:8545:8545 11 | volumes: 12 | - ../../../test_data/genesis-l1-dev.json:/genesis-l1-dev.json 13 | command: --network /genesis-l1-dev.json --http.addr 0.0.0.0 --http.port 8545 --dev 14 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-metrics" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { workspace = true, optional = true } 10 | tracing = { workspace = true, optional = true } 11 | thiserror.workspace = true 12 | serde_json.workspace = true 13 | serde.workspace = true 14 | 15 | ethrex-common.workspace = true 16 | 17 | 18 | prometheus = { version = "0.13.4", optional = true } 19 | axum = { workspace = true, optional = true } 20 | 21 | 22 | [lib] 23 | path = "./mod.rs" 24 | 25 | [features] 26 | default = ["api"] 27 | transactions = [] 28 | l2 = [] 29 | api = ["dep:axum", "dep:prometheus", "dep:tokio", "dep:tracing"] 30 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/README.md: -------------------------------------------------------------------------------- 1 | # Metrics 2 | 3 | A `docker-compose` is used to bundle prometheus and grafana services, the `*overrides` files define the ports and mounts the prometheus' configuration file. 4 | If a new dashboard is designed, it can be mounted only in that `*overrides` file. 5 | 6 | To run the node with metrics, the next steps should be followed: 7 | 1. Build the `ethrex` binary with the `metrics` feature enabled. 8 | 2. Enable metrics by using the `--metrics` flag when starting the node. 9 | 3. Set the `--metrics.port` cli arg of the ethrex binary to match the port defined in `metrics/provisioning/prometheus/prometheus*.yaml` 10 | 4. Run the docker containers, example with the L2: 11 | 12 | ```sh 13 | docker compose -f docker-compose-metrics.yaml -f docker-compose-metrics-l2.override.yaml up 14 | ``` 15 | 16 | >[!NOTE] 17 | > The L2's Makefile automatically starts the prometheus and grafana services with `make init` for the L2. 18 | 19 | 20 | - For the L2 we use the following files in conjunction: 21 | - `docker-compose-metrics.yaml` 22 | - `docker-compose-metrics-l2.overrides.yaml` 23 | - The defaults are: 24 | - PORT `3702` → metrics API (used by prometheus) 25 | - PORT `3802` → Grafana 26 | - usr: `admin` 27 | - pwd: `admin` 28 | - PORT `9092` → Prometheus 29 | 30 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/api.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, Router}; 2 | 3 | #[cfg(feature = "l2")] 4 | use crate::metrics_l2::METRICS_L2; 5 | 6 | use crate::{metrics_blocks::METRICS_BLOCKS, metrics_transactions::METRICS_TX, MetricsApiError}; 7 | 8 | pub async fn start_prometheus_metrics_api( 9 | address: String, 10 | port: String, 11 | ) -> Result<(), MetricsApiError> { 12 | let app = Router::new() 13 | .route("/metrics", get(get_metrics)) 14 | .route("/health", get("Service Up")); 15 | 16 | // Start the axum app 17 | let listener = tokio::net::TcpListener::bind(&format!("{address}:{port}")).await?; 18 | axum::serve(listener, app).await?; 19 | 20 | Ok(()) 21 | } 22 | 23 | #[allow(unused_mut)] 24 | async fn get_metrics() -> String { 25 | let mut ret_string = match METRICS_TX.gather_metrics() { 26 | Ok(string) => string, 27 | Err(_) => { 28 | tracing::error!("Failed to register METRICS_TX"); 29 | String::new() 30 | } 31 | }; 32 | 33 | ret_string.push('\n'); 34 | match METRICS_BLOCKS.gather_metrics() { 35 | Ok(string) => ret_string.push_str(&string), 36 | Err(_) => { 37 | tracing::error!("Failed to register METRICS_BLOCKS"); 38 | return String::new(); 39 | } 40 | } 41 | 42 | #[cfg(feature = "l2")] 43 | { 44 | ret_string.push('\n'); 45 | match METRICS_L2.gather_metrics() { 46 | Ok(string) => ret_string.push_str(&string), 47 | Err(_) => { 48 | tracing::error!("Failed to register METRICS_L2"); 49 | return String::new(); 50 | } 51 | } 52 | } 53 | 54 | ret_string 55 | } 56 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/docker-compose-metrics-l2.overrides.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | prometheus: 3 | volumes: 4 | - ../metrics/provisioning/prometheus/prometheus_l2.yaml:/etc/prometheus/prometheus.yaml 5 | ports: 6 | - "9092:9090" 7 | grafana: 8 | ports: 9 | - "3802:3000" 10 | volumes: 11 | - ../metrics/provisioning/grafana_provisioning/dashboards/l2_dashboards:/etc/grafana/provisioning/dashboards/l2_dashboards 12 | - ../metrics/provisioning/grafana_provisioning/dashboards/dashboard_config_l2.yaml:/etc/grafana/provisioning/dashboards/dashboard.yaml 13 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/docker-compose-metrics.yaml: -------------------------------------------------------------------------------- 1 | # example: docker compose -f docker-compose-metrics.yaml -f docker-compose-metrics-l2.override.yaml up 2 | services: 3 | prometheus: 4 | image: prom/prometheus 5 | command: --config.file=/etc/prometheus/prometheus.yaml 6 | #volumes: defined in the .overrides file 7 | #ports: defined in the .overrides file 8 | grafana: 9 | image: grafana/grafana 10 | volumes: 11 | - ./provisioning/grafana_provisioning/datasources:/etc/grafana/provisioning/datasources 12 | #ports: defined in the .overrides file 13 | depends_on: 14 | - prometheus 15 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/metrics_blocks.rs: -------------------------------------------------------------------------------- 1 | use prometheus::{Encoder, Gauge, IntGauge, Registry, TextEncoder}; 2 | use std::sync::LazyLock; 3 | 4 | use crate::MetricsError; 5 | 6 | pub static METRICS_BLOCKS: LazyLock = LazyLock::new(MetricsBlocks::default); 7 | 8 | #[derive(Debug, Clone)] 9 | pub struct MetricsBlocks { 10 | gas_limit: Gauge, 11 | block_number: IntGauge, 12 | } 13 | 14 | impl Default for MetricsBlocks { 15 | fn default() -> Self { 16 | Self::new() 17 | } 18 | } 19 | 20 | impl MetricsBlocks { 21 | pub fn new() -> Self { 22 | MetricsBlocks { 23 | gas_limit: Gauge::new( 24 | "gas_limit", 25 | "Keeps track of the percentage of gas limit used by the last block", 26 | ) 27 | .unwrap(), 28 | block_number: IntGauge::new( 29 | "block_number", 30 | "Keeps track of the block number for the head of the chain", 31 | ) 32 | .unwrap(), 33 | } 34 | } 35 | 36 | pub fn set_latest_block_gas_limit(&self, gas_limit: f64) { 37 | self.gas_limit.set(gas_limit); 38 | } 39 | 40 | pub fn set_block_number(&self, block_number: u64) -> Result<(), MetricsError> { 41 | self.block_number.set(block_number.try_into()?); 42 | Ok(()) 43 | } 44 | 45 | pub fn gather_metrics(&self) -> Result { 46 | let r = Registry::new(); 47 | 48 | r.register(Box::new(self.gas_limit.clone())) 49 | .map_err(|e| MetricsError::PrometheusErr(e.to_string()))?; 50 | r.register(Box::new(self.block_number.clone())) 51 | .map_err(|e| MetricsError::PrometheusErr(e.to_string()))?; 52 | 53 | let encoder = TextEncoder::new(); 54 | let metric_families = r.gather(); 55 | 56 | let mut buffer = Vec::new(); 57 | encoder 58 | .encode(&metric_families, &mut buffer) 59 | .map_err(|e| MetricsError::PrometheusErr(e.to_string()))?; 60 | 61 | let res = String::from_utf8(buffer)?; 62 | 63 | Ok(res) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/dashboard.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: "Custom Prometheus Dashboards" 5 | orgId: 1 6 | folder: "" 7 | type: file 8 | disableDeletion: true 9 | editable: true 10 | options: 11 | path: /etc/grafana/provisioning/dashboards/demo_dashboards 12 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/dashboard_config_l1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: "Ethrex Common Dashboards" 5 | orgId: 1 6 | folder: "" 7 | type: file 8 | disableDeletion: true 9 | editable: true 10 | options: 11 | path: /etc/grafana/provisioning/dashboards/common_dashboards 12 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/provisioning/grafana_provisioning/dashboards/dashboard_config_l2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | providers: 4 | - name: "EthrexL2 Dashboards" 5 | orgId: 1 6 | folder: "" 7 | type: file 8 | disableDeletion: true 9 | editable: true 10 | options: 11 | path: /etc/grafana/provisioning/dashboards/l2_dashboards 12 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/provisioning/grafana_provisioning/datasources/datasource.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: 1 2 | 3 | datasources: 4 | - name: Prometheus 5 | type: prometheus 6 | uid: prom-001 7 | access: proxy 8 | url: http://prometheus:9090 9 | isDefault: true 10 | jsonData: 11 | httpMethod: POST 12 | readOnly: false 13 | editable: true 14 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/provisioning/prometheus/prometheus_l1_dev.yaml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | 4 | scrape_configs: 5 | - job_name: "ethrex L1" 6 | static_configs: 7 | # Use the name defined in the docker-compose.yaml 8 | - targets: ["ethrex:3701"] 9 | -------------------------------------------------------------------------------- /crates/blockchain/metrics/provisioning/prometheus/prometheus_l2.yaml: -------------------------------------------------------------------------------- 1 | global: 2 | scrape_interval: 15s 3 | 4 | scrape_configs: 5 | - job_name: "ethrex L2" 6 | static_configs: 7 | # Use the name defined in the docker-compose.yaml 8 | - targets: ["host.docker.internal:3702"] 9 | -------------------------------------------------------------------------------- /crates/common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ethrex-rlp.workspace = true 10 | ethrex-trie.workspace = true 11 | 12 | tracing.workspace = true 13 | tinyvec = "1.6.0" 14 | ethereum-types.workspace = true 15 | serde.workspace = true 16 | serde_json.workspace = true 17 | thiserror.workspace = true 18 | k256.workspace = true 19 | # TODO(#1102): Move to Lambdaworks in the future 20 | c-kzg = { version = "^1.0.3", optional = true } 21 | keccak-hash.workspace = true 22 | sha3.workspace = true 23 | secp256k1.workspace = true 24 | once_cell = "1.20.2" 25 | crc32fast.workspace = true 26 | bytes.workspace = true 27 | hex.workspace = true 28 | lazy_static.workspace = true 29 | rayon = "1.5" 30 | 31 | [dev-dependencies] 32 | hex-literal.workspace = true 33 | 34 | 35 | [features] 36 | default = [] 37 | c-kzg = ["dep:c-kzg"] 38 | 39 | [lib] 40 | path = "./common.rs" 41 | -------------------------------------------------------------------------------- /crates/common/common.rs: -------------------------------------------------------------------------------- 1 | pub use ethereum_types::*; 2 | pub mod constants; 3 | pub mod serde_utils; 4 | pub mod types; 5 | pub use bytes::Bytes; 6 | pub mod base64; 7 | pub mod tracing; 8 | -------------------------------------------------------------------------------- /crates/common/constants.rs: -------------------------------------------------------------------------------- 1 | // === EIP-4844 constants === 2 | 3 | /// Gas consumption of a single data blob (== blob byte size). 4 | pub const GAS_PER_BLOB: u64 = 1 << 17; 5 | 6 | // Minimum base fee per blob 7 | pub const MIN_BASE_FEE_PER_BLOB_GAS: u64 = 1; 8 | -------------------------------------------------------------------------------- /crates/common/rlp/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-rlp" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | tinyvec = "1.6.0" 8 | thiserror.workspace = true 9 | bytes.workspace = true 10 | hex.workspace = true 11 | lazy_static.workspace = true 12 | ethereum-types.workspace = true 13 | snap.workspace = true 14 | 15 | [dev-dependencies] 16 | hex-literal.workspace = true 17 | [lib] 18 | path = "./rlp.rs" 19 | -------------------------------------------------------------------------------- /crates/common/rlp/constants.rs: -------------------------------------------------------------------------------- 1 | pub const RLP_NULL: u8 = 0x80; 2 | pub const RLP_EMPTY_LIST: u8 = 0xC0; 3 | -------------------------------------------------------------------------------- /crates/common/rlp/error.rs: -------------------------------------------------------------------------------- 1 | use thiserror::Error; 2 | 3 | // TODO: improve errors 4 | #[derive(Debug, Error, PartialEq, Eq)] 5 | pub enum RLPDecodeError { 6 | #[error("InvalidLength")] 7 | InvalidLength, 8 | #[error("MalformedData")] 9 | MalformedData, 10 | #[error("MalformedBoolean")] 11 | MalformedBoolean, 12 | #[error("UnexpectedList")] 13 | UnexpectedList, 14 | #[error("UnexpectedString")] 15 | UnexpectedString, 16 | #[error("InvalidCompression")] 17 | InvalidCompression(#[from] snap::Error), 18 | #[error("IncompatibleProtocol")] 19 | IncompatibleProtocol, 20 | #[error("{0}")] 21 | Custom(String), 22 | } 23 | 24 | // TODO: improve errors 25 | #[derive(Debug, Error)] 26 | pub enum RLPEncodeError { 27 | #[error("InvalidCompression")] 28 | InvalidCompression(#[from] snap::Error), 29 | #[error("{0}")] 30 | Custom(String), 31 | } 32 | -------------------------------------------------------------------------------- /crates/common/rlp/rlp.rs: -------------------------------------------------------------------------------- 1 | pub mod constants; 2 | pub mod decode; 3 | pub mod encode; 4 | pub mod error; 5 | pub mod structs; 6 | -------------------------------------------------------------------------------- /crates/common/tracing.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use ethereum_types::H256; 3 | use ethereum_types::{Address, U256}; 4 | use serde::Serialize; 5 | 6 | /// Collection of traces of each call frame as defined in geth's `callTracer` output 7 | /// https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#call-tracer 8 | pub type CallTrace = Vec; 9 | 10 | /// Trace of each call frame as defined in geth's `callTracer` output 11 | /// https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers#call-tracer 12 | #[derive(Debug, Serialize, Default)] 13 | #[serde(rename_all = "camelCase")] 14 | pub struct CallTraceFrame { 15 | /// Type of the Call 16 | #[serde(rename = "type")] 17 | pub call_type: CallType, 18 | /// Address that initiated the call 19 | pub from: Address, 20 | /// Address that received the call 21 | pub to: Address, 22 | /// Amount transfered 23 | pub value: U256, 24 | /// Gas provided for the call 25 | #[serde(with = "crate::serde_utils::u64::hex_str")] 26 | pub gas: u64, 27 | /// Gas used by the call 28 | #[serde(with = "crate::serde_utils::u64::hex_str")] 29 | pub gas_used: u64, 30 | /// Call data 31 | #[serde(with = "crate::serde_utils::bytes")] 32 | pub input: Bytes, 33 | /// Return data 34 | #[serde(with = "crate::serde_utils::bytes")] 35 | pub output: Bytes, 36 | /// Error returned if the call failed 37 | pub error: Option, 38 | /// Revert reason if the call reverted 39 | pub revert_reason: Option, 40 | /// List of nested sub-calls 41 | pub calls: Vec, 42 | /// Logs (if enabled) 43 | #[serde(skip_serializing_if = "Vec::is_empty")] 44 | pub logs: Vec, 45 | } 46 | 47 | #[derive(Serialize, Debug, Default)] 48 | pub enum CallType { 49 | #[default] 50 | CALL, 51 | CALLCODE, 52 | STATICCALL, 53 | DELEGATECALL, 54 | CREATE, 55 | CREATE2, 56 | SELFDESTRUCT, 57 | } 58 | 59 | #[derive(Serialize, Debug)] 60 | #[serde(rename_all = "camelCase")] 61 | pub struct CallLog { 62 | pub address: Address, 63 | pub topics: Vec, 64 | #[serde(with = "crate::serde_utils::bytes")] 65 | pub data: Bytes, 66 | pub position: u64, 67 | } 68 | -------------------------------------------------------------------------------- /crates/common/trie/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-trie" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | ethrex-rlp.workspace = true 8 | 9 | ethereum-types.workspace = true 10 | anyhow = "1.0.86" 11 | bytes.workspace = true 12 | tracing.workspace = true 13 | thiserror.workspace = true 14 | sha3.workspace = true 15 | hex.workspace = true 16 | serde = { version = "1.0.203", features = ["derive"] } 17 | serde_json = "1.0.117" 18 | libmdbx = { workspace = true, optional = true } 19 | smallvec = { version = "1.10.0", features = ["const_generics", "union"] } 20 | digest = "0.10.6" 21 | lazy_static.workspace = true 22 | 23 | [features] 24 | default = [] 25 | libmdbx = ["dep:libmdbx"] 26 | 27 | [dev-dependencies] 28 | hex.workspace = true 29 | hex-literal.workspace = true 30 | proptest = "1.0.0" 31 | tempdir = "0.3.7" 32 | cita_trie = "4.0.0" # used for proptest comparisons 33 | hasher = "0.1.4" # cita_trie needs this 34 | criterion = "0.5.1" 35 | rand.workspace = true 36 | 37 | [lib] 38 | path = "./trie.rs" 39 | 40 | [[bench]] 41 | name = "trie_bench" 42 | harness = false 43 | 44 | -------------------------------------------------------------------------------- /crates/common/trie/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: bench build-bench 2 | 3 | build-bench: 4 | @cargo build --bench trie_bench 5 | 6 | bench: build-bench 7 | @cargo bench --bench trie_bench 8 | -------------------------------------------------------------------------------- /crates/common/trie/README.md: -------------------------------------------------------------------------------- 1 | ## Ethrex-Trie 2 | This is the implementation of the State Trie (a Merkle Patricia Trie) used 3 | by Ethrex. 4 | 5 | ### Benchmarking 6 | To measure the performance of our implementation, we have a simple benchmark 7 | that compares against [citahub's cita_trie implementation](https://github.com/citahub/cita_trie/tree/master). 8 | 9 | To run it, you'll need rust installed of course, and you 10 | can run a comparison with: 11 | ```bash 12 | make bench 13 | ``` 14 | Benches are in the `benches` folder. 15 | 16 | ### Useful Links 17 | - [Ethereum.org -- Merkle Patricia Trie](https://ethereum.org/es/developers/docs/data-structures-and-encoding/patricia-merkle-trie/) 18 | - [Stack Exchange Discussion](https://ethereum.stackexchange.com/questions/130017/merkle-patricia-trie-in-ethereum) 19 | -------------------------------------------------------------------------------- /crates/common/trie/db.rs: -------------------------------------------------------------------------------- 1 | use crate::{error::TrieError, NodeHash}; 2 | use std::{ 3 | collections::HashMap, 4 | sync::{Arc, Mutex}, 5 | }; 6 | 7 | pub trait TrieDB: Send + Sync { 8 | fn get(&self, key: NodeHash) -> Result>, TrieError>; 9 | fn put_batch(&self, key_values: Vec<(NodeHash, Vec)>) -> Result<(), TrieError>; 10 | fn put(&self, key: NodeHash, value: Vec) -> Result<(), TrieError> { 11 | self.put_batch(vec![(key, value)]) 12 | } 13 | } 14 | 15 | /// InMemory implementation for the TrieDB trait, with get and put operations. 16 | pub struct InMemoryTrieDB { 17 | inner: Arc>>>, 18 | } 19 | 20 | impl InMemoryTrieDB { 21 | pub const fn new(map: Arc>>>) -> Self { 22 | Self { inner: map } 23 | } 24 | pub fn new_empty() -> Self { 25 | Self { 26 | inner: Default::default(), 27 | } 28 | } 29 | } 30 | 31 | impl TrieDB for InMemoryTrieDB { 32 | fn get(&self, key: NodeHash) -> Result>, TrieError> { 33 | Ok(self 34 | .inner 35 | .lock() 36 | .map_err(|_| TrieError::LockError)? 37 | .get(&key) 38 | .cloned()) 39 | } 40 | 41 | fn put_batch(&self, key_values: Vec<(NodeHash, Vec)>) -> Result<(), TrieError> { 42 | let mut db = self.inner.lock().map_err(|_| TrieError::LockError)?; 43 | 44 | for (key, value) in key_values { 45 | db.insert(key, value); 46 | } 47 | 48 | Ok(()) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /crates/common/trie/error.rs: -------------------------------------------------------------------------------- 1 | use ethrex_rlp::error::RLPDecodeError; 2 | use thiserror::Error; 3 | 4 | #[derive(Debug, Error)] 5 | pub enum TrieError { 6 | #[error(transparent)] 7 | RLPDecode(#[from] RLPDecodeError), 8 | #[error("Verification Error: {0}")] 9 | Verify(String), 10 | #[error("Inconsistent internal tree structure")] 11 | InconsistentTree, 12 | #[error("Lock Error: Panicked when trying to acquire a lock")] 13 | LockError, 14 | #[error("Database error: {0}")] 15 | DbError(anyhow::Error), 16 | } 17 | -------------------------------------------------------------------------------- /crates/common/types/account_update.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{types::AccountInfo, Address, H256, U256}; 4 | use bytes::Bytes; 5 | use serde::{Deserialize, Serialize}; 6 | 7 | #[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] 8 | pub struct AccountUpdate { 9 | pub address: Address, 10 | pub removed: bool, 11 | pub info: Option, 12 | pub code: Option, 13 | pub added_storage: HashMap, 14 | // Matches TODO in code 15 | // removed_storage_keys: Vec, 16 | } 17 | 18 | impl AccountUpdate { 19 | /// Creates new empty update for the given account 20 | pub fn new(address: Address) -> AccountUpdate { 21 | AccountUpdate { 22 | address, 23 | ..Default::default() 24 | } 25 | } 26 | 27 | /// Creates new update representing an account removal 28 | pub fn removed(address: Address) -> AccountUpdate { 29 | AccountUpdate { 30 | address, 31 | removed: true, 32 | ..Default::default() 33 | } 34 | } 35 | 36 | pub fn merge(&mut self, other: AccountUpdate) { 37 | self.removed = other.removed; 38 | if let Some(info) = other.info { 39 | self.info = Some(info); 40 | } 41 | if let Some(code) = other.code { 42 | self.code = Some(code); 43 | } 44 | for (key, value) in other.added_storage { 45 | self.added_storage.insert(key, value); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /crates/common/types/batch.rs: -------------------------------------------------------------------------------- 1 | use crate::H256; 2 | 3 | use super::BlobsBundle; 4 | 5 | #[derive(Clone)] 6 | pub struct Batch { 7 | pub number: u64, 8 | pub first_block: u64, 9 | pub last_block: u64, 10 | pub state_root: H256, 11 | pub deposit_logs_hash: H256, 12 | pub withdrawal_hashes: Vec, 13 | pub blobs_bundle: BlobsBundle, 14 | } 15 | -------------------------------------------------------------------------------- /crates/common/types/constants.rs: -------------------------------------------------------------------------------- 1 | // Fee related 2 | pub const ELASTICITY_MULTIPLIER: u64 = 2; 3 | pub const BASE_FEE_MAX_CHANGE_DENOMINATOR: u64 = 8; 4 | pub const GAS_LIMIT_ADJUSTMENT_FACTOR: u64 = 1024; 5 | pub const GAS_LIMIT_MINIMUM: u64 = 5000; 6 | pub const GWEI_TO_WEI: u64 = 1_000_000_000; 7 | pub const INITIAL_BASE_FEE: u64 = 1_000_000_000; //Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) 8 | pub const MIN_BASE_FEE_PER_BLOB_GAS: u64 = 1; // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) 9 | pub const BLOB_BASE_FEE_UPDATE_FRACTION: u64 = 3338477; // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) 10 | pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) 11 | /// Minimum tip, obtained from geth's default miner config (https://github.com/ethereum/go-ethereum/blob/f750117ad19d623622cc4a46ea361a716ba7407e/miner/miner.go#L56) 12 | /// TODO: This should be configurable along with the tip filter on https://github.com/lambdaclass/ethrex/issues/680 13 | pub const MIN_GAS_TIP: u64 = 1000000; 14 | 15 | // Blob size related 16 | // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) 17 | pub const BYTES_PER_FIELD_ELEMENT: usize = 32; 18 | pub const FIELD_ELEMENTS_PER_BLOB: usize = 4096; 19 | pub const BYTES_PER_BLOB: usize = BYTES_PER_FIELD_ELEMENT * FIELD_ELEMENTS_PER_BLOB; 20 | pub const BYTES_PER_BLOB_F64: f64 = BYTES_PER_BLOB as f64; 21 | /// The maximum number of bytes that can be "safely" stored in a blob. This is, prepend 22 | /// a zero byte for every 32 bytes of data to ensure they not exceed the field modulus. 23 | pub const SAFE_BYTES_PER_BLOB: usize = BYTES_PER_BLOB * 31 / 32; 24 | -------------------------------------------------------------------------------- /crates/common/types/mod.rs: -------------------------------------------------------------------------------- 1 | mod account; 2 | mod account_update; 3 | pub mod batch; 4 | pub mod blobs_bundle; 5 | mod block; 6 | mod constants; 7 | mod fork_id; 8 | mod genesis; 9 | pub mod payload; 10 | mod receipt; 11 | pub mod requests; 12 | pub mod transaction; 13 | pub mod tx_fields; 14 | 15 | pub use account::*; 16 | pub use account_update::*; 17 | pub use blobs_bundle::*; 18 | pub use block::*; 19 | pub use constants::*; 20 | pub use fork_id::*; 21 | pub use genesis::*; 22 | pub use receipt::*; 23 | pub use transaction::*; 24 | pub use tx_fields::*; 25 | -------------------------------------------------------------------------------- /crates/common/types/payload.rs: -------------------------------------------------------------------------------- 1 | use super::{requests::EncodedRequests, BlobsBundle, Block}; 2 | use ethereum_types::U256; 3 | use ethrex_rlp::{ 4 | decode::RLPDecode, 5 | encode::RLPEncode, 6 | error::RLPDecodeError, 7 | structs::{Decoder, Encoder}, 8 | }; 9 | 10 | #[derive(Debug, Clone)] 11 | pub struct PayloadBundle { 12 | pub block: Block, 13 | pub block_value: U256, 14 | pub blobs_bundle: BlobsBundle, 15 | pub requests: Vec, 16 | pub completed: bool, 17 | } 18 | 19 | impl PayloadBundle { 20 | pub fn from_block(block: Block) -> Self { 21 | PayloadBundle { 22 | block, 23 | block_value: U256::zero(), 24 | blobs_bundle: BlobsBundle::empty(), 25 | requests: Vec::default(), 26 | completed: false, 27 | } 28 | } 29 | } 30 | 31 | impl RLPEncode for PayloadBundle { 32 | fn encode(&self, buf: &mut dyn bytes::BufMut) { 33 | Encoder::new(buf) 34 | .encode_field(&self.block) 35 | .encode_field(&self.block_value) 36 | .encode_field(&self.blobs_bundle) 37 | .encode_field(&self.requests) 38 | .encode_field(&self.completed) 39 | .finish(); 40 | } 41 | } 42 | 43 | impl RLPDecode for PayloadBundle { 44 | fn decode_unfinished(rlp: &[u8]) -> Result<(PayloadBundle, &[u8]), RLPDecodeError> { 45 | let decoder = Decoder::new(rlp)?; 46 | let (block, decoder) = decoder.decode_field("block")?; 47 | let (block_value, decoder) = decoder.decode_field("block_value")?; 48 | let (blobs_bundle, decoder) = decoder.decode_field("blobs_bundle")?; 49 | let (requests, decoder) = decoder.decode_field("requests")?; 50 | let (completed, decoder) = decoder.decode_field("completed")?; 51 | let state = PayloadBundle { 52 | block, 53 | block_value, 54 | blobs_bundle, 55 | requests, 56 | completed, 57 | }; 58 | Ok((state, decoder.finish()?)) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /crates/l2/.gitignore: -------------------------------------------------------------------------------- 1 | .env* 2 | 3 | store/ 4 | 5 | solc_out 6 | 7 | # qpl-tool temp files 8 | out/ 9 | -------------------------------------------------------------------------------- /crates/l2/.tool-versions: -------------------------------------------------------------------------------- 1 | solidity 0.8.29 2 | -------------------------------------------------------------------------------- /crates/l2/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.82 AS chef 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | build-essential \ 5 | libclang-dev \ 6 | libc6 \ 7 | libssl-dev \ 8 | ca-certificates \ 9 | && rm -rf /var/lib/apt/lists/* 10 | RUN cargo install cargo-chef 11 | 12 | WORKDIR /ethrex 13 | 14 | FROM chef AS planner 15 | COPY . . 16 | # Determine the crates that need to be built from dependencies 17 | RUN cargo chef prepare --recipe-path recipe.json 18 | 19 | FROM chef AS builder 20 | COPY --from=planner /ethrex/recipe.json recipe.json 21 | # Build dependencies only, these remained cached 22 | RUN cargo chef cook --release --recipe-path recipe.json 23 | 24 | COPY . . 25 | RUN cargo build --release --features "l2,rollup_storage_libmdbx" 26 | 27 | FROM ubuntu:24.04 28 | ENV CI_ETHREX_WORKDIR=$CI_ETHREX_WORKDIR 29 | WORKDIR $CI_ETHREX_WORKDIR 30 | 31 | COPY --from=builder ethrex/target/release/ethrex . 32 | EXPOSE 1729 33 | ENTRYPOINT [ "./ethrex" ] 34 | -------------------------------------------------------------------------------- /crates/l2/contracts/.gitignore: -------------------------------------------------------------------------------- 1 | # Dotenv file 2 | .env 3 | 4 | # Libs 5 | lib/ 6 | -------------------------------------------------------------------------------- /crates/l2/contracts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex_l2-contracts" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | tokio = { version = "1.38.0", features = ["full"] } 10 | ethereum-types = { workspace = true, features = ["serialize"] } 11 | bytes = { version = "1.6.0", features = ["serde"] } 12 | secp256k1.workspace = true 13 | keccak-hash.workspace = true 14 | hex = "0.4.3" 15 | serde_json = "1.0.138" 16 | colored = "2.1.0" 17 | lazy_static = "1.5.0" 18 | tracing.workspace = true 19 | tracing-subscriber = { version = "0.3", features = ["fmt"] } 20 | thiserror.workspace = true 21 | clap.workspace = true 22 | clap_complete.workspace = true 23 | eyre.workspace = true 24 | 25 | ethrex-l2 = { path = "../../l2" } 26 | ethrex-sdk = { path = "../../l2/sdk" } 27 | ethrex-common = { path = "../../common" } 28 | ethrex-rpc = { path = "../../networking/rpc" } 29 | genesis-tool = { path = "../../../tooling/genesis" } 30 | 31 | [[bin]] 32 | name = "ethrex_l2_l1_deployer" 33 | path = "bin/deployer/main.rs" 34 | 35 | [[bin]] 36 | name = "ethrex_l2_system_contracts_updater" 37 | path = "bin/system_contracts_updater/main.rs" 38 | 39 | [lints.clippy] 40 | unwrap_used = "deny" 41 | expect_used = "deny" 42 | indexing_slicing = "deny" 43 | as_conversions = "deny" 44 | unnecessary_cast = "warn" 45 | panic = "deny" 46 | -------------------------------------------------------------------------------- /crates/l2/contracts/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rust:1.82 AS chef 2 | 3 | RUN apt-get update && apt-get install -y \ 4 | build-essential \ 5 | libclang-dev \ 6 | libc6 \ 7 | libssl-dev \ 8 | ca-certificates \ 9 | && rm -rf /var/lib/apt/lists/* 10 | RUN cargo install cargo-chef 11 | 12 | WORKDIR /ethrex 13 | 14 | FROM chef AS planner 15 | 16 | COPY . . 17 | # Determine the crates that need to be built from dependencies 18 | RUN cargo chef prepare --bin ethrex_l2_l1_deployer --recipe-path recipe.json 19 | 20 | FROM chef AS builder 21 | COPY --from=planner /ethrex/recipe.json recipe.json 22 | # Build dependencies only, these remained cached 23 | RUN cargo chef cook --release --recipe-path recipe.json --manifest-path crates/l2/contracts/Cargo.toml --bin ethrex_l2_l1_deployer 24 | 25 | COPY . . 26 | RUN cargo build --release --manifest-path crates/l2/contracts/Cargo.toml 27 | 28 | FROM --platform=${BUILDPLATFORM} ubuntu:24.04 29 | WORKDIR /usr/local/bin 30 | 31 | RUN apt-get update && apt-get -y install git gnupg software-properties-common curl 32 | RUN add-apt-repository ppa:ethereum/ethereum 33 | RUN curl -L -o /usr/bin/solc https://github.com/ethereum/solidity/releases/download/v0.8.29/solc-static-linux 34 | RUN chmod +x /usr/bin/solc 35 | 36 | COPY --from=builder ethrex/target/release/ethrex_l2_l1_deployer . 37 | 38 | EXPOSE 1729 39 | ENTRYPOINT [ "./ethrex_l2_l1_deployer" ] 40 | -------------------------------------------------------------------------------- /crates/l2/contracts/README.md: -------------------------------------------------------------------------------- 1 | # Ethrex L2 contracts 2 | 3 | There are two L1 contracts: OnChainProposer and CommonBridge. Both contracts are deployed using UUPS proxies, so they are upgradeables. 4 | 5 | ### Upgrade the contracts 6 | 7 | To upgrade a contract, you have to create the new contract and, as the original one, inherit from OpenZeppelin's `UUPSUpgradeable`. Make sure to implement the `_authorizeUpgrade` function and follow the [proxy pattern restrictions](https://docs.openzeppelin.com/upgrades-plugins/writing-upgradeable). 8 | 9 | Once you have the new contract, you need to do the following three steps: 10 | 11 | 1. Deploy the new contract 12 | ```sh 13 | rex deploy 0 14 | ``` 15 | 2. Upgrade the proxy by calling the method `upgradeToAndCall(address newImplementation, bytes memory data)`. The `data` parameter is the calldata to call on the new implementation as an initialization, you can pass an empty stream. 16 | ```sh 17 | rex send 0 -- 'upgradeToAndCall(address,bytes)' 18 | ``` 19 | 3. Check the proxy updated the pointed address to the new implementation. It should return the address of the new implementation: 20 | ```sh 21 | curl http://localhost:8545 -d '{"jsonrpc": "2.0", "id": "1", "method": "eth_getStorageAt", "params": [, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc", "latest"]}' 22 | ``` 23 | 24 | ### Transfer ownership 25 | 26 | The contracts are `Ownable2Step`, that means that whenever you want to transfer the ownership, the new owner have to accept it to effectively apply the change. This is an extra step of security, to avoid accidentally transfer ownership to a wrong account. You can make the transfer in these steps: 27 | 28 | 1. Start the transfer: 29 | ```sh 30 | rex send 0 -- 'transferOwnership(address)' 31 | ``` 32 | 2. Accept the ownership: 33 | ```sh 34 | rex send 0 -- 'acceptOwnership()' 35 | ``` 36 | -------------------------------------------------------------------------------- /crates/l2/contracts/bin/deployer/error.rs: -------------------------------------------------------------------------------- 1 | use ethrex_l2_sdk::{ContractCompilationError, DeployError}; 2 | use ethrex_rpc::clients::{eth::errors::CalldataEncodeError, EthClientError}; 3 | 4 | #[derive(Debug, thiserror::Error)] 5 | pub enum DeployerError { 6 | #[error("Failed to lock SALT: {0}")] 7 | FailedToLockSALT(String), 8 | #[error("The path is not a valid utf-8 string")] 9 | FailedToGetStringFromPath, 10 | #[error("Deployer setup error: {0} not set")] 11 | ConfigValueNotSet(String), 12 | #[error("Deployer setup parse error: {0}")] 13 | ParseError(String), 14 | #[error("Deployer dependency error: {0}")] 15 | DependencyError(String), 16 | #[error("Deployer EthClient error: {0}")] 17 | EthClientError(#[from] EthClientError), 18 | #[error("Deployer decoding error: {0}")] 19 | DecodingError(String), 20 | #[error("Failed to encode calldata: {0}")] 21 | CalldataEncodeError(#[from] CalldataEncodeError), 22 | #[error("Failed to compile contract: {0}")] 23 | FailedToCompileContract(#[from] ContractCompilationError), 24 | #[error("Failed to deploy contract: {0}")] 25 | FailedToDeployContract(#[from] DeployError), 26 | #[error("Internal error: {0}")] 27 | InternalError(String), 28 | #[error("IO error: {0}")] 29 | IO(#[from] std::io::Error), 30 | } 31 | -------------------------------------------------------------------------------- /crates/l2/contracts/bin/system_contracts_updater/cli.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use clap::Parser; 4 | 5 | #[derive(Parser, Default)] 6 | pub struct SystemContractsUpdaterOptions { 7 | #[arg( 8 | long, 9 | value_name = "PATH", 10 | env = "ETHREX_SYSTEM_CONTRACTS_UPDATER_CONTRACTS_PATH", 11 | help_heading = "Deployer options", 12 | help = "Path to the contracts directory. The default is the current directory." 13 | )] 14 | pub contracts_path: PathBuf, 15 | #[arg( 16 | long, 17 | value_name = "PATH", 18 | env = "ETHREX_DEPLOYER_GENESIS_L1_PATH", 19 | help_heading = "Deployer options", 20 | help = "Path to the genesis file. The default is ../../test_data/genesis-l1-dev.json" 21 | )] 22 | pub l2_genesis_path: PathBuf, 23 | } 24 | -------------------------------------------------------------------------------- /crates/l2/contracts/bin/system_contracts_updater/error.rs: -------------------------------------------------------------------------------- 1 | use ethrex_l2_sdk::ContractCompilationError; 2 | 3 | #[allow(clippy::enum_variant_names)] 4 | #[derive(Debug, thiserror::Error)] 5 | pub enum SystemContractsUpdaterError { 6 | #[error("Failed to compile contract: {0}")] 7 | FailedToCompileContract(#[from] ContractCompilationError), 8 | #[error("Failed to deploy contract: {0}")] 9 | FailedToDecodeRuntimeCode(#[from] hex::FromHexError), 10 | #[error("Failed to serialize modified genesis: {0}")] 11 | FailedToSerializeModifiedGenesis(#[from] serde_json::Error), 12 | #[error("Failed to write modified genesis file: {0}")] 13 | FailedToWriteModifiedGenesisFile(#[from] std::io::Error), 14 | #[error("Failed to read path: {0}")] 15 | InvalidPath(String), 16 | } 17 | -------------------------------------------------------------------------------- /crates/l2/contracts/bin/system_contracts_updater/main.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, io::ErrorKind, path::Path, path::PathBuf}; 2 | 3 | use bytes::Bytes; 4 | use clap::Parser; 5 | use cli::SystemContractsUpdaterOptions; 6 | use error::SystemContractsUpdaterError; 7 | use ethrex_common::types::GenesisAccount; 8 | use ethrex_common::U256; 9 | use ethrex_l2::utils::test_data_io::read_genesis_file; 10 | use ethrex_l2_sdk::{compile_contract, COMMON_BRIDGE_L2_ADDRESS}; 11 | use genesis_tool::genesis::write_genesis_as_json; 12 | mod cli; 13 | mod error; 14 | 15 | fn main() -> Result<(), SystemContractsUpdaterError> { 16 | let opts = SystemContractsUpdaterOptions::parse(); 17 | compile_contract(&opts.contracts_path, "src/l2/CommonBridgeL2.sol", true)?; 18 | update_genesis_file(&opts.l2_genesis_path)?; 19 | Ok(()) 20 | } 21 | 22 | fn update_genesis_file(l2_genesis_path: &PathBuf) -> Result<(), SystemContractsUpdaterError> { 23 | let mut genesis = read_genesis_file(l2_genesis_path.to_str().ok_or( 24 | SystemContractsUpdaterError::InvalidPath( 25 | "Failed to convert l2 genesis path to string".to_string(), 26 | ), 27 | )?); 28 | 29 | let runtime_code = std::fs::read("contracts/solc_out/CommonBridgeL2.bin-runtime")?; 30 | 31 | genesis.alloc.insert( 32 | COMMON_BRIDGE_L2_ADDRESS, 33 | GenesisAccount { 34 | code: Bytes::from(hex::decode(runtime_code)?), 35 | storage: HashMap::new(), 36 | balance: U256::zero(), 37 | nonce: 1, 38 | }, 39 | ); 40 | 41 | write_genesis_as_json(genesis, Path::new(l2_genesis_path)) 42 | .map_err(|err_msg| std::io::Error::new(ErrorKind::Other, err_msg))?; 43 | 44 | println!("Updated L2 genesis file."); 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /crates/l2/contracts/src/l1/interfaces/IPicoVerifier.sol: -------------------------------------------------------------------------------- 1 | // Source: https://github.com/brevis-network/pico-zkapp-template/blob/evm/contracts/src/IPicoVerifier.sol 2 | // SPDX-License-Identifier: MIT 3 | pragma solidity =0.8.29; 4 | 5 | /// @title Pico Verifier Interface 6 | /// @author Brevis Network 7 | /// @notice This contract is the interface for the Pico Verifier. 8 | interface IPicoVerifier { 9 | /// @notice Verifies a proof with given public values and riscv verification key. 10 | /// @param riscvVkey The verification key for the RISC-V program. 11 | /// @param publicValues The public values encoded as bytes. 12 | /// @param proof The proof of the riscv program execution in the Pico. 13 | function verifyPicoProof( 14 | bytes32 riscvVkey, 15 | bytes calldata publicValues, 16 | uint256[8] calldata proof 17 | ) external view; 18 | } 19 | -------------------------------------------------------------------------------- /crates/l2/contracts/src/l1/interfaces/ISP1Verifier.sol: -------------------------------------------------------------------------------- 1 | // From: https://github.com/succinctlabs/sp1-contracts/blob/65a1d96b720ffa2e6d167fb62527c9c8a534abbe/contracts/src/ISP1Verifier.sol 2 | // SPDX-License-Identifier: MIT 3 | pragma solidity =0.8.29; 4 | 5 | /// @title SP1 Verifier Interface 6 | /// @author Succinct Labs 7 | /// @notice This contract is the interface for the SP1 Verifier. 8 | interface ISP1Verifier { 9 | /// @notice Verifies a proof with given public values and vkey. 10 | /// @dev It is expected that the first 4 bytes of proofBytes must match the first 4 bytes of 11 | /// target verifier's VERIFIER_HASH. 12 | /// @param programVKey The verification key for the RISC-V program. 13 | /// @param publicValues The public values encoded as bytes. 14 | /// @param proofBytes The proof of the program execution the SP1 zkVM encoded as bytes. 15 | function verifyProof( 16 | bytes32 programVKey, 17 | bytes calldata publicValues, 18 | bytes calldata proofBytes 19 | ) external view; 20 | } 21 | 22 | interface ISP1VerifierWithHash is ISP1Verifier { 23 | /// @notice Returns the hash of the verifier. 24 | function VERIFIER_HASH() external pure returns (bytes32); 25 | } 26 | -------------------------------------------------------------------------------- /crates/l2/contracts/src/l1/interfaces/ITDXVerifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.8.29; 3 | 4 | /// @title TDX Verifier Interface 5 | /// @author LambdaClass 6 | /// @notice Interface for the TDX Verifier 7 | interface ITDXVerifier { 8 | /// @notice Verifies a proof with given payload and signature 9 | /// @dev The signature should correspond to an address previously registered with the verifier 10 | /// @param payload The payload to be verified 11 | /// @param signature The associated signature 12 | function verify( 13 | bytes calldata payload, 14 | bytes memory signature 15 | ) external view; 16 | 17 | /// @notice Registers the quote 18 | /// @dev The data required to verify the quote must be loaded to the PCCS contracts beforehand 19 | /// @param quote The TDX quote, which includes the address being registered 20 | function register( 21 | bytes calldata quote 22 | ) external; 23 | } 24 | -------------------------------------------------------------------------------- /crates/l2/contracts/src/l2/CommonBridgeL2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.8.29; 3 | 4 | import "./interfaces/ICommonBridgeL2.sol"; 5 | 6 | /// @title CommonBridge L2 contract. 7 | /// @author LambdaClass 8 | contract CommonBridgeL2 is ICommonBridgeL2 { 9 | address public constant BURN_ADDRESS = 10 | 0x0000000000000000000000000000000000000000; 11 | 12 | function withdraw(address _receiverOnL1) external payable { 13 | require(msg.value > 0, "Withdrawal amount must be positive"); 14 | 15 | (bool success, ) = BURN_ADDRESS.call{value: msg.value}(""); 16 | require(success, "Failed to burn Ether"); 17 | 18 | // This event gets pushed to L1, the sequencer monitors 19 | // them on every block. 20 | emit WithdrawalInitiated(msg.sender, _receiverOnL1, msg.value); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /crates/l2/contracts/src/l2/interfaces/ICommonBridgeL2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity =0.8.29; 3 | 4 | /// @title Interface for the L2 side of the CommonBridge contract. 5 | /// @author LambdaClass 6 | /// @notice A CommonBridge contract is a contract that allows L1<->L2 communication 7 | /// It handles user withdrawals and message sending to L1. 8 | interface ICommonBridgeL2 { 9 | /// @notice A withdrawal to L1 has initiated. 10 | /// @dev Event emitted when a withdrawal is initiated. 11 | /// @param senderOnL2 the sender of the transaction on L2. 12 | /// @param receiverOnL1 the address on L1 that will receive the funds back. 13 | /// @param amount the amount of ether being withdrawn. 14 | event WithdrawalInitiated( 15 | address indexed senderOnL2, 16 | address indexed receiverOnL1, 17 | uint256 indexed amount 18 | ); 19 | 20 | /// @notice Initiates the withdrawal of funds to the L1. 21 | /// @dev This is the first step in the two step process of a user withdrawal. 22 | /// @dev It burns funds on L2 and sends a message to the L1 so users 23 | /// @dev can claim those funds on L1. 24 | /// @param _receiverOnL1 the address that can claim the funds on L1. 25 | function withdraw(address _receiverOnL1) external payable; 26 | } 27 | -------------------------------------------------------------------------------- /crates/l2/docker-compose-l2-store.overrides.yaml: -------------------------------------------------------------------------------- 1 | # Mount and use database created from blobs, for the state reconstruct test. 2 | services: 3 | ethrex_l2: 4 | volumes: 5 | - ./store:/store 6 | command: > 7 | l2 init 8 | --network /genesis-l2.json 9 | --http.addr 0.0.0.0 10 | --http.port 1729 11 | --authrpc.port 8552 12 | --datadir /store 13 | --proof-coordinator.addr 0.0.0.0 14 | --l1.bridge-address ${ETHREX_WATCHER_BRIDGE_ADDRESS} 15 | --l1.on-chain-proposer-address ${ETHREX_COMMITTER_ON_CHAIN_PROPOSER_ADDRESS} 16 | --block-producer.coinbase-address 0x0007a881CD95B1484fca47615B64803dad620C8d 17 | --committer.l1-private-key 0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924 18 | --proof-coordinator.l1-private-key 0x39725efee3fb28614de3bacaffe4cc4bd8c436257e2c8bb887c4b5c4be45e76d 19 | -------------------------------------------------------------------------------- /crates/l2/docs/contracts.md: -------------------------------------------------------------------------------- 1 | # ethrex L2 Contracts 2 | 3 | ## ToC 4 | 5 | - [ethrex L2 Contracts](#ethrex-l2-contracts) 6 | - [ToC](#toc) 7 | - [L1 side](#l1-side) 8 | - [`CommonBridge`](#commonbridge) 9 | - [`OnChainOperator`](#onchainoperator) 10 | - [`Verifier`](#verifier) 11 | - [L2 side](#l2-side) 12 | - [`L1MessageSender`](#l1messagesender) 13 | 14 | ## L1 side 15 | 16 | ### `CommonBridge` 17 | 18 | Allows L1<->L2 communication from L1. It both sends messages from L1 to L2 and receives messages from L2. 19 | 20 | #### Deposit Functions 21 | 22 | ##### Simple Deposits 23 | 24 | - Send ETH directly to the contract address using a standard transfer 25 | - The contract's `receive()` function automatically forwards funds to your identical address on L2 26 | - No additional parameters needed 27 | 28 | ##### Deposits with Contract Interaction 29 | 30 | ```solidity 31 | function deposit(DepositValues calldata depositValues) public payable 32 | ``` 33 | 34 | Parameters: 35 | 36 | - `to`: Target address on L2 37 | - `recipient`: Address that will receive the ETH on L2 (can differ from sender) 38 | - `gasLimit`: Maximum gas for L2 execution 39 | - `data`: Calldata to execute on the target L2 contract 40 | 41 | This method enables atomic operations like: 42 | 43 | - Depositing ETH while simultaneously interacting with L2 contracts 44 | - Funding another user's L2 account 45 | 46 | ### `OnChainOperator` 47 | 48 | Ensures the advancement of the L2. It is used by the operator to commit batches of blocks and verify batch proofs. 49 | 50 | ### `Verifier` 51 | 52 | TODO 53 | 54 | ## L2 side 55 | 56 | ### `L1MessageSender` 57 | 58 | TODO 59 | -------------------------------------------------------------------------------- /crates/l2/docs/img/aligned_mode_proof_sender.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/crates/l2/docs/img/aligned_mode_proof_sender.png -------------------------------------------------------------------------------- /crates/l2/docs/img/aligned_mode_proof_verifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/crates/l2/docs/img/aligned_mode_proof_verifier.png -------------------------------------------------------------------------------- /crates/l2/docs/img/execw_case1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/crates/l2/docs/img/execw_case1.png -------------------------------------------------------------------------------- /crates/l2/docs/img/execw_case2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/crates/l2/docs/img/execw_case2.png -------------------------------------------------------------------------------- /crates/l2/errors.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /crates/l2/l2.rs: -------------------------------------------------------------------------------- 1 | pub mod errors; 2 | pub mod sequencer; 3 | pub mod utils; 4 | 5 | pub use sequencer::configs::{ 6 | BlockProducerConfig, CommitterConfig, EthConfig, L1WatcherConfig, ProofCoordinatorConfig, 7 | SequencerConfig, 8 | }; 9 | pub use sequencer::start_l2; 10 | -------------------------------------------------------------------------------- /crates/l2/prover/.gitignore: -------------------------------------------------------------------------------- 1 | rsp/ 2 | target/ 3 | -------------------------------------------------------------------------------- /crates/l2/prover/src/backends/exec.rs: -------------------------------------------------------------------------------- 1 | use ethrex_l2::utils::prover::proving_systems::{BatchProof, ProofCalldata, ProverType}; 2 | use ethrex_l2_sdk::calldata::Value; 3 | use tracing::warn; 4 | 5 | use zkvm_interface::io::{ProgramInput, ProgramOutput}; 6 | 7 | pub struct ProveOutput(pub ProgramOutput); 8 | 9 | pub fn execute(input: ProgramInput) -> Result<(), Box> { 10 | execution_program(input)?; 11 | Ok(()) 12 | } 13 | 14 | pub fn prove( 15 | input: ProgramInput, 16 | _aligned_mode: bool, 17 | ) -> Result> { 18 | warn!("\"exec\" prover backend generates no proof, only executes"); 19 | let output = execution_program(input)?; 20 | Ok(ProveOutput(output)) 21 | } 22 | 23 | pub fn verify(_proof: &ProveOutput) -> Result<(), Box> { 24 | warn!("\"exec\" prover backend generates no proof, verification always succeeds"); 25 | Ok(()) 26 | } 27 | 28 | fn to_calldata(proof: ProveOutput) -> ProofCalldata { 29 | let public_inputs = proof.0.encode(); 30 | ProofCalldata { 31 | prover_type: ProverType::Exec, 32 | calldata: vec![Value::Bytes(public_inputs.into())], 33 | } 34 | } 35 | 36 | pub fn to_batch_proof( 37 | proof: ProveOutput, 38 | _aligned_mode: bool, 39 | ) -> Result> { 40 | Ok(BatchProof::ProofCalldata(to_calldata(proof))) 41 | } 42 | 43 | pub fn execution_program(input: ProgramInput) -> Result> { 44 | zkvm_interface::execution::execution_program(input).map_err(|e| e.into()) 45 | } 46 | -------------------------------------------------------------------------------- /crates/l2/prover/src/backends/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod exec; 2 | 3 | #[cfg(feature = "pico")] 4 | pub mod pico; 5 | 6 | #[cfg(feature = "risc0")] 7 | pub mod risc0; 8 | 9 | #[cfg(feature = "sp1")] 10 | pub mod sp1; 11 | -------------------------------------------------------------------------------- /crates/l2/prover/src/config.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | 3 | #[derive(Deserialize, Debug)] 4 | pub struct ProverConfig { 5 | pub http_addr: String, 6 | pub http_port: u16, 7 | pub proving_time_ms: u64, 8 | pub aligned_mode: bool, 9 | } 10 | -------------------------------------------------------------------------------- /crates/l2/prover/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod backends; 2 | pub mod prover; 3 | 4 | pub mod config; 5 | use config::ProverConfig; 6 | use tracing::warn; 7 | 8 | pub async fn init_client(config: ProverConfig) { 9 | prover::start_prover(config).await; 10 | warn!("Prover finished!"); 11 | } 12 | 13 | #[cfg(feature = "pico")] 14 | pub use backends::pico::*; 15 | 16 | #[cfg(feature = "risc0")] 17 | pub use backends::risc0::*; 18 | 19 | #[cfg(feature = "sp1")] 20 | pub use backends::sp1::*; 21 | 22 | #[cfg(not(any(feature = "pico", feature = "risc0", feature = "sp1")))] 23 | pub use backends::exec::*; 24 | -------------------------------------------------------------------------------- /crates/l2/prover/src/main.rs: -------------------------------------------------------------------------------- 1 | pub mod cli; 2 | use crate::cli::ProverCLI; 3 | use clap::Parser; 4 | use ethrex_prover_lib::init_client; 5 | use tracing::{self, debug, error}; 6 | 7 | #[tokio::main] 8 | async fn main() { 9 | let options = ProverCLI::parse(); 10 | 11 | let subscriber = tracing_subscriber::FmtSubscriber::builder() 12 | .with_max_level(options.prover_client_options.log_level) 13 | .finish(); 14 | if let Err(e) = tracing::subscriber::set_global_default(subscriber) { 15 | error!("Failed setting tracing::subscriber: {e}"); 16 | return; 17 | } 18 | 19 | debug!("Prover Client has started"); 20 | init_client(options.prover_client_options.into()).await; 21 | } 22 | -------------------------------------------------------------------------------- /crates/l2/prover/src/utils/placeholder.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/crates/l2/prover/src/utils/placeholder.rs -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | Cargo.lock 3 | target/ 4 | interface/sp1/out 5 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["interface"] 4 | 5 | # Always optimize; building and running the guest takes much longer without optimization. 6 | [profile.dev] 7 | opt-level = 3 8 | 9 | [profile.release] 10 | debug = 1 11 | lto = true 12 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zkvm_interface" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | serde = { version = "1.0.203", features = ["derive"] } 8 | serde_with = "3.11.0" 9 | serde_json = "1.0.117" 10 | thiserror = "2.0.9" 11 | keccak-hash.workspace = true 12 | 13 | ethrex-common = { path = "../../../../common/", default-features = false } 14 | ethrex-blockchain = { path = "../../../../blockchain/", default-features = false } 15 | ethrex-vm = { path = "../../../../vm", default-features = false } 16 | ethrex-rlp = { path = "../../../../common/rlp", default-features = false } 17 | ethrex-storage = { path = "../../../../storage", default-features = false } 18 | ethrex-trie = { path = "../../../../common/trie", default-features = false } 19 | 20 | # Temporarily pin version because 0.11.1 breaks compilation 21 | # alloy-rpc-types-engine = "=0.11.0" 22 | 23 | [build-dependencies] 24 | risc0-build = { version = "1.2.2", optional = true } 25 | sp1-build = { version = "4.1.7", optional = true } 26 | sp1-sdk = { version = "4.1.7", optional = true } 27 | pico-cli = { git = "https://github.com/brevis-network/pico", optional = true } 28 | 29 | [package.metadata.risc0] 30 | methods = ["risc0"] 31 | 32 | [features] 33 | default = [] 34 | risc0 = ["dep:risc0-build"] 35 | sp1 = ["dep:sp1-build", "dep:sp1-sdk"] 36 | pico = ["dep:pico-cli"] 37 | l2 = ["ethrex-vm/l2"] 38 | 39 | [lib] 40 | path = "./src/lib.rs" 41 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/Makefile: -------------------------------------------------------------------------------- 1 | # This Makefile is intended to be invoked only by build.rs 2 | 3 | pico_elf := ${OUT_DIR}/riscv32im-pico-zkvm-elf 4 | 5 | $(pico_elf): 6 | cd pico; \ 7 | cargo pico build --output-directory ${OUT_DIR} 8 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/pico/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zkvm-pico-program" 3 | version = "0.1.0" 4 | edition = "2024" 5 | 6 | [workspace] 7 | 8 | [dependencies] 9 | pico-sdk = { git = "https://github.com/brevis-network/pico" } 10 | zkvm_interface = { path = "../" } 11 | 12 | ethrex-common = { path = "../../../../../common", default-features = false } 13 | ethrex-storage = { path = "../../../../../storage", default-features = false } 14 | ethrex-rlp = { path = "../../../../../common/rlp" } 15 | ethrex-vm = { path = "../../../../../vm", default-features = false } 16 | ethrex-blockchain = { path = "../../../../../blockchain", default-features = false } 17 | 18 | # Temporarily pin version because 0.11.1 breaks compilation 19 | #alloy-rpc-types-engine = "=0.11.0" 20 | 21 | [patch.crates-io] 22 | # Pico has its own patch for secp256k1 but it doesn't compile: it has a git dependency for a branch that 23 | # doesn't exists, and another git dependency to an seemingly private repo. The SP1 patch worked well. 24 | secp256k1 = { git = "https://github.com/sp1-patches/rust-secp256k1", branch = "patch-secp256k1-v0.29.1" } 25 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/pico/rust-toolchain: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2024-11-27" 3 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/pico/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use pico_sdk::io::{commit, read_as}; 4 | 5 | use zkvm_interface::io::ProgramInput; 6 | 7 | pico_sdk::entrypoint!(main); 8 | 9 | pub fn main() { 10 | let input: ProgramInput = read_as(); 11 | let output = zkvm_interface::execution::execution_program(input).unwrap(); 12 | 13 | commit(&output); 14 | } 15 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/risc0/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zkvm-risc0-program" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [workspace] 7 | 8 | [dependencies] 9 | risc0-zkvm = { version = "1.2.2", default-features = false, features = ["std"] } 10 | zkvm_interface = { path = "../" } 11 | 12 | ethrex-common = { path = "../../../../../common", default-features = false } 13 | ethrex-storage = { path = "../../../../../storage", default-features = false } 14 | ethrex-rlp = { path = "../../../../../common/rlp" } 15 | ethrex-vm = { path = "../../../../../vm", default-features = false } 16 | ethrex-blockchain = { path = "../../../../../blockchain", default-features = false } 17 | 18 | # Temporarily pin version because 0.11.1 breaks compilation 19 | alloy-rpc-types-engine = "=0.11.0" 20 | 21 | [patch.crates-io] 22 | crypto-bigint = { git = "https://github.com/risc0/RustCrypto-crypto-bigint", tag = "v0.5.5-risczero.0" } 23 | k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.0" } 24 | sha2 = { git = "https://github.com/risc0/RustCrypto-hashes", tag = "sha2-v0.10.6-risczero.0" } 25 | secp256k1 = { git = "https://github.com/sp1-patches/rust-secp256k1", branch = "patch-secp256k1-v0.29.1" } 26 | ecdsa-core = { git = "https://github.com/sp1-patches/signatures", package = "ecdsa", branch = "patch-ecdsa-v0.16.9" } 27 | 28 | [features] 29 | l2 = ["ethrex-vm/l2", "zkvm_interface/l2", "ethrex-blockchain/l2"] 30 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/risc0/NOTICE: -------------------------------------------------------------------------------- 1 | Original work Copyright [RISC Zero, Inc.] 2 | Copyright [2024] [LambdaClass] 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/risc0/src/main.rs: -------------------------------------------------------------------------------- 1 | use risc0_zkvm::guest::env; 2 | 3 | use zkvm_interface::io::ProgramInput; 4 | 5 | fn main() { 6 | let input: ProgramInput = env::read(); 7 | let output = zkvm_interface::execution::execution_program(input).unwrap(); 8 | 9 | env::commit(&output); 10 | } 11 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/sp1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | version = "0.1.0" 3 | name = "zkvm-sp1-program" 4 | edition = "2021" 5 | 6 | [workspace] 7 | 8 | [dependencies] 9 | sp1-zkvm = "4.1.7" 10 | zkvm_interface = { path = "../" } 11 | 12 | ethrex-common = { path = "../../../../../common", default-features = false } 13 | ethrex-storage = { path = "../../../../../storage", default-features = false } 14 | ethrex-rlp = { path = "../../../../../common/rlp" } 15 | ethrex-vm = { path = "../../../../../vm", default-features = false } 16 | ethrex-blockchain = { path = "../../../../../blockchain", default-features = false } 17 | 18 | [patch.crates-io] 19 | sha3-v0-10-8 = { git = "https://github.com/sp1-patches/RustCrypto-hashes", package = "sha3", tag = "patch-sha3-0.10.8-sp1-4.0.0" } 20 | crypto-bigint = { git = "https://github.com/sp1-patches/RustCrypto-bigint", tag = "patch-0.5.5-sp1-4.0.0" } 21 | secp256k1 = { git = "https://github.com/sp1-patches/rust-secp256k1", tag = "patch-0.29.1-sp1-4.0.0" } 22 | sha2-v0-10-8 = { git = "https://github.com/sp1-patches/RustCrypto-hashes", package = "sha2", tag = "patch-sha2-0.10.8-sp1-4.0.0" } 23 | ecdsa-core = { git = "https://github.com/sp1-patches/signatures", package = "ecdsa", tag = "patch-0.16.9-sp1-4.0.0" } 24 | tiny-keccak = { git = "https://github.com/sp1-patches/tiny-keccak", tag = "patch-2.0.2-sp1-4.0.0" } 25 | 26 | [features] 27 | l2 = ["ethrex-vm/l2", "zkvm_interface/l2", "ethrex-blockchain/l2"] 28 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/sp1/NOTICE: -------------------------------------------------------------------------------- 1 | # APACHE NOTICE 2 | Original work Copyright [Succinct Labs] 3 | Copyright [2024] [LambdaClass] 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | 17 | 18 | # MIT NOTICE 19 | The MIT License (MIT) 20 | 21 | Original work Copyright (c) 2023 Succinct Labs 22 | Copyright [2024] [LambdaClass] 23 | 24 | Permission is hereby granted, free of charge, to any person obtaining a copy 25 | of this software and associated documentation files (the "Software"), to deal 26 | in the Software without restriction, including without limitation the rights 27 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 28 | copies of the Software, and to permit persons to whom the Software is 29 | furnished to do so, subject to the following conditions: 30 | 31 | The above copyright notice and this permission notice shall be included in 32 | all copies or substantial portions of the Software. 33 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/sp1/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | use zkvm_interface::io::ProgramInput; 4 | 5 | sp1_zkvm::entrypoint!(main); 6 | 7 | pub fn main() { 8 | let input = sp1_zkvm::io::read::(); 9 | let output = zkvm_interface::execution::execution_program(input).unwrap(); 10 | 11 | sp1_zkvm::io::commit(&output.encode()); 12 | } 13 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/src/deposits.rs: -------------------------------------------------------------------------------- 1 | // This module was based on the L1 committer. 2 | // TODO: We should move this to some kind of "common" library for the L2, but the zkvm programs 3 | // can't depend on ethrex-l2 because of incompatible dependencies. 4 | 5 | use ethrex_common::{ 6 | types::{PrivilegedL2Transaction, Transaction}, 7 | Address, U256, 8 | }; 9 | use keccak_hash::{keccak, H256}; 10 | 11 | #[derive(Debug, thiserror::Error)] 12 | pub enum DepositError { 13 | #[error("Failed to decode deposit hash")] 14 | FailedToDecodeHash, 15 | #[error("Length does not fit in u16")] 16 | LengthTooLarge(#[from] std::num::TryFromIntError), 17 | } 18 | 19 | #[derive(Clone)] 20 | pub struct DepositLog { 21 | pub address: Address, 22 | pub amount: U256, 23 | pub nonce: u64, 24 | } 25 | 26 | pub fn get_block_deposits(txs: &[Transaction]) -> Vec { 27 | txs.iter() 28 | .filter_map(|tx| match tx { 29 | Transaction::PrivilegedL2Transaction(tx) => Some(tx.clone()), 30 | _ => None, 31 | }) 32 | .collect() 33 | } 34 | 35 | pub fn get_deposit_hash(deposit_hashes: Vec) -> Result { 36 | if !deposit_hashes.is_empty() { 37 | let deposit_hashes_len: u16 = deposit_hashes 38 | .len() 39 | .try_into() 40 | .map_err(DepositError::from)?; 41 | Ok(H256::from_slice( 42 | [ 43 | &deposit_hashes_len.to_be_bytes(), 44 | keccak( 45 | deposit_hashes 46 | .iter() 47 | .map(H256::as_bytes) 48 | .collect::>() 49 | .concat(), 50 | ) 51 | .as_bytes() 52 | .get(2..32) 53 | .ok_or(DepositError::FailedToDecodeHash)?, 54 | ] 55 | .concat() 56 | .as_slice(), 57 | )) 58 | } else { 59 | Ok(H256::zero()) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod execution; 2 | pub mod io; 3 | pub mod methods; 4 | pub mod trie; 5 | pub mod withdrawals; 6 | 7 | #[cfg(feature = "l2")] 8 | pub mod deposits; 9 | -------------------------------------------------------------------------------- /crates/l2/prover/zkvm/interface/src/methods.rs: -------------------------------------------------------------------------------- 1 | #[cfg(any(clippy, not(feature = "risc0")))] 2 | pub const ZKVM_RISC0_PROGRAM_ELF: &[u8] = &[0]; 3 | #[cfg(any(clippy, not(feature = "risc0")))] 4 | pub const ZKVM_RISC0_PROGRAM_ID: [u32; 8] = [0_u32; 8]; 5 | #[cfg(all(not(clippy), feature = "risc0"))] 6 | include!(concat!(env!("OUT_DIR"), "/methods.rs")); 7 | 8 | #[cfg(all(not(clippy), feature = "pico"))] 9 | pub const ZKVM_PICO_PROGRAM_ELF: &[u8] = 10 | include_bytes!(concat!(env!("OUT_DIR"), "/riscv32im-pico-zkvm-elf")); 11 | #[cfg(any(clippy, not(feature = "pico")))] 12 | pub const ZKVM_PICO_PROGRAM_ELF: &[u8] = &[0]; 13 | -------------------------------------------------------------------------------- /crates/l2/sdk/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-sdk" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | ethrex-common.workspace = true 8 | ethrex-rpc.workspace = true 9 | 10 | ethereum-types.workspace = true 11 | tokio.workspace = true 12 | hex.workspace = true 13 | keccak-hash.workspace = true 14 | secp256k1.workspace = true 15 | itertools = "0.13.0" 16 | thiserror.workspace = true 17 | serde_json.workspace = true 18 | serde.workspace = true 19 | tracing.workspace = true 20 | ethrex-rlp.workspace = true 21 | bytes.workspace = true 22 | reqwest.workspace = true 23 | eyre.workspace = true 24 | lazy_static.workspace = true 25 | 26 | [lib] 27 | name = "ethrex_l2_sdk" 28 | path = "src/sdk.rs" 29 | 30 | [lints.clippy] 31 | unwrap_used = "deny" 32 | expect_used = "deny" 33 | indexing_slicing = "deny" 34 | as_conversions = "deny" 35 | unnecessary_cast = "warn" 36 | panic = "deny" 37 | -------------------------------------------------------------------------------- /crates/l2/sequencer/configs.rs: -------------------------------------------------------------------------------- 1 | use aligned_sdk::common::types::Network; 2 | use ethrex_common::{Address, U256}; 3 | use secp256k1::SecretKey; 4 | use std::net::IpAddr; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct SequencerConfig { 8 | pub block_producer: BlockProducerConfig, 9 | pub l1_committer: CommitterConfig, 10 | pub eth: EthConfig, 11 | pub l1_watcher: L1WatcherConfig, 12 | pub proof_coordinator: ProofCoordinatorConfig, 13 | pub aligned: AlignedConfig, 14 | } 15 | 16 | // TODO: Move to blockchain/dev 17 | #[derive(Clone, Debug)] 18 | pub struct BlockProducerConfig { 19 | pub block_time_ms: u64, 20 | pub coinbase_address: Address, 21 | pub elasticity_multiplier: u64, 22 | } 23 | 24 | #[derive(Clone, Debug)] 25 | pub struct CommitterConfig { 26 | pub on_chain_proposer_address: Address, 27 | pub l1_address: Address, 28 | pub l1_private_key: SecretKey, 29 | pub commit_time_ms: u64, 30 | pub arbitrary_base_blob_gas_price: u64, 31 | pub validium: bool, 32 | } 33 | 34 | #[derive(Clone, Debug)] 35 | pub struct EthConfig { 36 | pub rpc_url: Vec, 37 | pub maximum_allowed_max_fee_per_gas: u64, 38 | pub maximum_allowed_max_fee_per_blob_gas: u64, 39 | pub max_number_of_retries: u64, 40 | pub backoff_factor: u64, 41 | pub min_retry_delay: u64, 42 | pub max_retry_delay: u64, 43 | } 44 | 45 | #[derive(Clone, Debug)] 46 | pub struct L1WatcherConfig { 47 | pub bridge_address: Address, 48 | pub check_interval_ms: u64, 49 | pub max_block_step: U256, 50 | pub watcher_block_delay: u64, 51 | } 52 | 53 | #[derive(Clone, Debug)] 54 | pub struct ProofCoordinatorConfig { 55 | pub l1_address: Address, 56 | pub l1_private_key: SecretKey, 57 | pub listen_ip: IpAddr, 58 | pub listen_port: u16, 59 | pub proof_send_interval_ms: u64, 60 | pub dev_mode: bool, 61 | } 62 | 63 | #[derive(Clone, Debug)] 64 | pub struct AlignedConfig { 65 | pub aligned_mode: bool, 66 | pub aligned_verifier_interval_ms: u64, 67 | pub beacon_url: String, 68 | pub network: Network, 69 | pub fee_estimate: String, 70 | pub aligned_sp1_elf_path: String, 71 | } 72 | -------------------------------------------------------------------------------- /crates/l2/sequencer/execution_cache.rs: -------------------------------------------------------------------------------- 1 | use std::{env::temp_dir, fs::File, path::PathBuf}; 2 | 3 | use ethrex_common::types::{AccountUpdate, BlockHash}; 4 | use tracing::warn; 5 | 6 | use super::errors::ExecutionCacheError; 7 | 8 | /// For now the result will only be account updates, in the future we can add other parameters as 9 | /// they're needed. 10 | pub type ExecutionResult = Vec; 11 | 12 | /// Proposer will push execution results into the cache so other components can retrieve them, 13 | /// without having to re-execute. The cache is implemented with temporary files. 14 | pub struct ExecutionCache { 15 | tempdir: PathBuf, 16 | } 17 | 18 | impl Default for ExecutionCache { 19 | fn default() -> Self { 20 | Self { 21 | tempdir: temp_dir(), 22 | } 23 | } 24 | } 25 | 26 | impl ExecutionCache { 27 | pub fn push( 28 | &self, 29 | block_hash: BlockHash, 30 | execution_result: ExecutionResult, 31 | ) -> Result<(), ExecutionCacheError> { 32 | let filename = format!("result_{block_hash:x}.ethrex"); 33 | let file = File::create(self.tempdir.join(filename))?; 34 | bincode::serialize_into(file, &execution_result).map_err(ExecutionCacheError::from) 35 | } 36 | 37 | pub fn get( 38 | &self, 39 | block_hash: BlockHash, 40 | ) -> Result, ExecutionCacheError> { 41 | let filename = format!("result_{block_hash:x}.ethrex"); 42 | File::open(self.tempdir.join(filename)) 43 | .inspect_err(|err| warn!("{err}")) 44 | .ok() 45 | .map(|file| bincode::deserialize_from(file).map_err(ExecutionCacheError::from)) 46 | .transpose() 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /crates/l2/storage/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | -------------------------------------------------------------------------------- /crates/l2/storage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-storage-rollup" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | 7 | [dependencies] 8 | ethrex-common.workspace = true 9 | ethrex-storage.workspace = true 10 | ethrex-rlp.workspace = true 11 | ethereum-types.workspace = true 12 | 13 | async-trait.workspace = true 14 | anyhow = "1.0.86" 15 | tracing.workspace = true 16 | libmdbx = { workspace = true, optional = true } 17 | redb = { workspace = true, optional = true } 18 | # NOTE: intentionally avoiding the workspace dep as it brings "full" features, breaking the provers 19 | # We only need the runtime for the blocking databases to spawn blocking tasks 20 | tokio = { version = "1.41.1", optional = true, default-features = false, features = ["rt"] } 21 | 22 | [features] 23 | default = [] 24 | libmdbx = ["dep:libmdbx", "dep:tokio"] 25 | redb = ["dep:redb", "dep:tokio"] 26 | l2 = [] 27 | 28 | [lib] 29 | name = "ethrex_storage_rollup" 30 | path = "src/lib.rs" 31 | 32 | [lints.clippy] 33 | unwrap_used = "deny" 34 | expect_used = "deny" 35 | indexing_slicing = "deny" 36 | as_conversions = "deny" 37 | unnecessary_cast = "warn" 38 | panic = "deny" 39 | -------------------------------------------------------------------------------- /crates/l2/storage/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod api; 2 | mod rlp; 3 | mod store; 4 | mod store_db; 5 | 6 | pub use store::{EngineType as EngineTypeRollup, Store as StoreRollup}; 7 | -------------------------------------------------------------------------------- /crates/l2/storage/src/store_db/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod in_memory; 2 | #[cfg(feature = "libmdbx")] 3 | pub mod libmdbx; 4 | #[cfg(feature = "redb")] 5 | pub mod redb; 6 | -------------------------------------------------------------------------------- /crates/l2/tee/contracts/.gitignore: -------------------------------------------------------------------------------- 1 | # Libraries 2 | lib/ 3 | 4 | # Dotenv file 5 | .env 6 | .env.out 7 | 8 | # Deploy dependencies 9 | deploydeps/ 10 | automata-dcap-qpl/ 11 | -------------------------------------------------------------------------------- /crates/l2/tee/contracts/README.md: -------------------------------------------------------------------------------- 1 | # Deployment 2 | 3 | You can use `make deploy-deps` to deploy the dependencies and `make deploy` to deploy the main contract. 4 | 5 | # Dependencies 6 | 7 | A compiled version ([for reproducibility](https://github.com/daimo-eth/p256-verifier/issues/46)) of [p256-verifier](https://github.com/daimo-eth/p256-verifier) is included as assets/p256.hex 8 | -------------------------------------------------------------------------------- /crates/l2/tee/contracts/assets/platform_ca.hex: -------------------------------------------------------------------------------- 1 | 308202963082023da003020102021500956f5dcdbd1be1e94049c9d4f433ce01570bde54300a06082a8648ce3d0403023068311a301806035504030c11496e74656c2053475820526f6f74204341311a3018060355040a0c11496e74656c20436f72706f726174696f6e3114301206035504070c0b53616e746120436c617261310b300906035504080c024341310b3009060355040613025553301e170d3138303532313130353031305a170d3333303532313130353031305a30703122302006035504030c19496e74656c205347582050434b20506c6174666f726d204341311a3018060355040a0c11496e74656c20436f72706f726174696f6e3114301206035504070c0b53616e746120436c617261310b300906035504080c024341310b30090603550406130255533059301306072a8648ce3d020106082a8648ce3d0301070342000435207feeddb595748ed82bb3a71c3be1e241ef61320c6816e6b5c2b71dad5532eaea12a4eb3f948916429ea47ba6c3af82a15e4b19664e52657939a2d96633dea381bb3081b8301f0603551d2304183016801422650cd65a9d3489f383b49552bf501b392706ac30520603551d1f044b30493047a045a043864168747470733a2f2f6365727469666963617465732e7472757374656473657276696365732e696e74656c2e636f6d2f496e74656c534758526f6f7443412e646572301d0603551d0e04160414956f5dcdbd1be1e94049c9d4f433ce01570bde54300e0603551d0f0101ff04040302010630120603551d130101ff040830060101ff020100300a06082a8648ce3d040302034700304402205ec5648b4c3e8ba558196dd417fdb6b9a5ded182438f551e9c0f938c3d5a8b970220261bd520260f9c647d3569be8e14a32892631ac358b994478088f4d2b27cf37e 2 | -------------------------------------------------------------------------------- /crates/l2/tee/quote-gen/.gitignore: -------------------------------------------------------------------------------- 1 | image.raw 2 | -------------------------------------------------------------------------------- /crates/l2/tee/quote-gen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quote-gen" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | configfs-tsm = "0.0.1" 8 | tokio = { version = "1.41.1", features = ["full"] } 9 | keccak-hash = "0.11" 10 | hex = "0.4" 11 | zerocopy = "0.8" 12 | serde = { version = "1.0", features = ["derive"] } 13 | serde_json = "1.0" 14 | 15 | ethrex-common = { path = "../../../common", default-features = false } 16 | ethrex-storage = { path = "../../../storage", default-features = false } 17 | ethrex-rlp = { path = "../../../common/rlp" } 18 | ethrex-vm = { path = "../../../vm", default-features = false } 19 | ethrex-blockchain = { path = "../../../blockchain", default-features = false } 20 | ethrex-rpc = { path = "../../../networking/rpc", default-features = false } 21 | zkvm_interface = { path = "../../prover/zkvm/interface", default-features = false } 22 | ethrex-sdk = { path = "../../sdk", default-features = false } 23 | ethrex-l2 = { path = "../..", default-features = false } 24 | 25 | secp256k1 = { version = "0.29.1", default-features = false, features = [ 26 | "global-context", 27 | "recovery", 28 | "rand", 29 | "std" 30 | ]} 31 | 32 | [workspace] 33 | 34 | [features] 35 | default = ["l2"] 36 | l2 = [ 37 | "ethrex-vm/l2", 38 | "zkvm_interface/l2", 39 | "ethrex-blockchain/l2", 40 | "ethrex-l2/l2", 41 | ] 42 | -------------------------------------------------------------------------------- /crates/l2/tee/quote-gen/Makefile: -------------------------------------------------------------------------------- 1 | # image.raw is set to phony because the list of files that require rebuilding it is huge 2 | .PHONY: run clean image.raw 3 | 4 | IMAGE_NAME = ethrex-image_0.1.raw 5 | NIXPKGS_URL = https://github.com/NixOS/nixpkgs/archive/3fcbdcfc707e0aa42c541b7743e05820472bdaec.tar.gz 6 | NIX_BUILD_ARGS = --no-out-link -I nixpkgs=$(NIXPKGS_URL) 7 | 8 | image.raw: 9 | $(eval IMAGE := $(shell nix-build image.nix ${NIX_BUILD_ARGS})/${IMAGE_NAME}) 10 | cp $(IMAGE) image.raw 11 | chmod u+rw image.raw 12 | 13 | run: image.raw 14 | $(eval RUN_QEMU := $(shell nix-build hypervisor.nix ${NIX_BUILD_ARGS})/bin/run-qemu) 15 | $(RUN_QEMU) image.raw 16 | 17 | clean: 18 | rm image.raw 19 | -------------------------------------------------------------------------------- /crates/l2/tee/quote-gen/image.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import { }; 3 | in 4 | (pkgs.nixos [ 5 | ( 6 | { 7 | config, 8 | lib, 9 | pkgs, 10 | modulesPath, 11 | ... 12 | }: 13 | let 14 | inherit (config.image.repart.verityStore) partitionIds; 15 | in 16 | { 17 | imports = [ 18 | "${modulesPath}/image/repart.nix" 19 | "${modulesPath}/profiles/minimal.nix" 20 | ./service.nix 21 | ]; 22 | 23 | system.stateVersion = "25.11"; 24 | environment.systemPackages = lib.mkOverride 99 []; 25 | 26 | boot.kernelModules = [ "tdx_guest" "tsm" ]; 27 | boot.initrd.availableKernelModules = [ "dm_mod" "dm_verity" "erofs" "sd_mod" "ahci" ]; 28 | boot.initrd.includeDefaultModules = false; 29 | nix.enable = false; 30 | boot = { 31 | loader.grub.enable = false; 32 | initrd.systemd.enable = true; 33 | kernelParams = [ "console=ttyS0" ]; 34 | }; 35 | system.image = { 36 | id = "ethrex"; 37 | version = "0.1"; 38 | }; 39 | fileSystems = { 40 | "/" = { 41 | fsType = "tmpfs"; 42 | options = [ "mode=0755" ]; 43 | }; 44 | "/nix/store" = { 45 | device = "/usr/nix/store"; 46 | options = [ "bind" "ro" ]; 47 | }; 48 | }; 49 | image.repart = { 50 | name = "ethrex-image"; 51 | verityStore = { 52 | enable = true; 53 | ukiPath = "/EFI/BOOT/BOOTX64.EFI"; 54 | }; 55 | partitions = { 56 | ${partitionIds.esp} = { 57 | repartConfig = { 58 | Type = "esp"; 59 | Format = "vfat"; 60 | SizeMinBytes = "96M"; 61 | }; 62 | }; 63 | ${partitionIds.store-verity}.repartConfig = { 64 | Minimize = "best"; 65 | }; 66 | ${partitionIds.store}.repartConfig = { 67 | Minimize = "best"; 68 | }; 69 | }; 70 | }; 71 | } 72 | ) 73 | ]).finalImage 74 | -------------------------------------------------------------------------------- /crates/l2/tee/quote-gen/service.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import { }; 3 | gitignoreSrc = pkgs.fetchFromGitHub { 4 | owner = "hercules-ci"; 5 | repo = "gitignore.nix"; 6 | rev = "637db329424fd7e46cf4185293b9cc8c88c95394"; 7 | sha256 = "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs"; 8 | }; 9 | inherit (import gitignoreSrc { inherit (pkgs) lib; }) gitignoreSource; 10 | in 11 | let 12 | quoteGen = pkgs.rustPlatform.buildRustPackage rec { 13 | pname = "quote-gen"; 14 | version = "0.1"; 15 | 16 | src = gitignoreSource ./../../../../.; 17 | sourceRoot = "${src.name}/crates/l2/tee/quote-gen"; 18 | 19 | cargoDeps = pkgs.rustPlatform.importCargoLock { 20 | lockFile = ./Cargo.lock; 21 | outputHashes = { 22 | "bls12_381-0.8.0" = "sha256-8/pXRA7hVAPeMKCZ+PRPfQfxqstw5Ob4MJNp85pv5WQ="; 23 | "spawned-concurrency-0.1.0" = "sha256-/RO23J4c1fNVpF6ZgHdVPp3C2mgpg+dCwLjg0JcZ0YI="; 24 | }; 25 | }; 26 | 27 | buildInputs = [ pkgs.openssl ]; 28 | nativeBuildInputs = [ 29 | pkgs.pkg-config 30 | pkgs.rustPlatform.cargoSetupHook 31 | ]; 32 | env.OPENSSL_NO_VENDOR = 1; 33 | }; 34 | in 35 | { 36 | systemd.services.quote-gen = { 37 | description = "Ethrex TDX Quote Generator"; 38 | wantedBy = [ "multi-user.target" ]; 39 | 40 | serviceConfig = { 41 | ExecStart = "${quoteGen}/bin/quote-gen"; 42 | StandardOutput = "journal+console"; 43 | }; 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /crates/l2/utils/error.rs: -------------------------------------------------------------------------------- 1 | use ethrex_blockchain::error::ChainError; 2 | use ethrex_storage::error::StoreError; 3 | use ethrex_vm::ProverDBError; 4 | use keccak_hash::H256; 5 | 6 | #[derive(Debug, thiserror::Error)] 7 | pub enum ProverInputError { 8 | #[error("Invalid block number: {0}")] 9 | InvalidBlockNumber(usize), 10 | #[error("Invalid parent block: {0}")] 11 | InvalidParentBlock(H256), 12 | #[error("Store error: {0}")] 13 | StoreError(#[from] StoreError), 14 | #[error("Chain error: {0}")] 15 | ChainError(#[from] ChainError), 16 | #[error("ProverDB error: {0}")] 17 | ProverDBError(#[from] ProverDBError), 18 | } 19 | 20 | #[derive(Debug, thiserror::Error)] 21 | pub enum UtilsError { 22 | #[error("Unable to parse withdrawal_event_selector: {0}")] 23 | WithdrawalSelectorError(String), 24 | } 25 | -------------------------------------------------------------------------------- /crates/l2/utils/helpers.rs: -------------------------------------------------------------------------------- 1 | use ethrex_common::{ 2 | types::{Receipt, Transaction, TxKind}, 3 | H256, 4 | }; 5 | use ethrex_l2_sdk::COMMON_BRIDGE_L2_ADDRESS; 6 | 7 | // this selector corresponds to this function signature: 8 | // WithdrawalInitiated(address,address,uint256) 9 | const WITHDRAWAL_EVENT_SELECTOR: H256 = H256([ 10 | 0xbb, 0x26, 0x89, 0xff, 0x87, 0x6f, 0x7e, 0xf4, 0x53, 0xcf, 0x88, 0x65, 0xdd, 0xe5, 0xab, 0x10, 11 | 0x34, 0x9d, 0x22, 0x2e, 0x2e, 0x13, 0x83, 0xc5, 0x15, 0x2f, 0xbd, 0xb0, 0x83, 0xf0, 0x2d, 0xa2, 12 | ]); 13 | 14 | pub fn is_withdrawal_l2(tx: &Transaction, receipt: &Receipt) -> bool { 15 | if let TxKind::Call(to) = tx.to() { 16 | if to == COMMON_BRIDGE_L2_ADDRESS { 17 | receipt.logs.iter().any(|log| { 18 | log.topics 19 | .iter() 20 | .any(|topic| *topic == WITHDRAWAL_EVENT_SELECTOR) 21 | }) 22 | } else { 23 | false 24 | } 25 | } else { 26 | false 27 | } 28 | } 29 | 30 | pub fn is_deposit_l2(tx: &Transaction) -> bool { 31 | matches!(tx, Transaction::PrivilegedL2Transaction(_tx)) 32 | } 33 | -------------------------------------------------------------------------------- /crates/l2/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod helpers; 3 | pub mod parse; 4 | pub mod prover; 5 | pub mod test_data_io; 6 | -------------------------------------------------------------------------------- /crates/l2/utils/parse.rs: -------------------------------------------------------------------------------- 1 | use ethereum_types::{Address, H256}; 2 | 3 | pub fn hash_to_address(hash: H256) -> Address { 4 | Address::from_slice(&hash.as_fixed_bytes()[12..]) 5 | } 6 | -------------------------------------------------------------------------------- /crates/l2/utils/prover/errors.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, thiserror::Error)] 2 | pub enum SaveStateError { 3 | #[error("Failed to create data dir")] 4 | FailedToCrateDataDir, 5 | #[error("Failed to interact with IO: {0}")] 6 | IOError(#[from] std::io::Error), 7 | #[error("Failed to de/serialize: {0}")] 8 | SerdeJson(#[from] serde_json::Error), 9 | #[error("Failed to parse block_number_from_path: {0}")] 10 | ParseIntError(#[from] std::num::ParseIntError), 11 | #[error("{0}")] 12 | Custom(String), 13 | } 14 | -------------------------------------------------------------------------------- /crates/l2/utils/prover/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod db; 2 | pub mod errors; 3 | pub mod proving_systems; 4 | pub mod save_state; 5 | -------------------------------------------------------------------------------- /crates/networking/docs/diagrams/SnapSync.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/crates/networking/docs/diagrams/SnapSync.drawio.png -------------------------------------------------------------------------------- /crates/networking/docs/diagrams/StateSyncAndHealing.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/crates/networking/docs/diagrams/StateSyncAndHealing.drawio.png -------------------------------------------------------------------------------- /crates/networking/docs/diagrams/bytecode_fetcher.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/crates/networking/docs/diagrams/bytecode_fetcher.jpg -------------------------------------------------------------------------------- /crates/networking/p2p/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-p2p" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ethrex-common.workspace = true 10 | ethrex-blockchain.workspace = true 11 | ethrex-rlp.workspace = true 12 | ethrex-storage.workspace = true 13 | ethrex-trie.workspace = true 14 | ethereum-types.workspace = true 15 | tracing.workspace = true 16 | tokio.workspace = true 17 | tokio-util.workspace = true 18 | bytes.workspace = true 19 | hex.workspace = true 20 | thiserror.workspace = true 21 | lazy_static.workspace = true 22 | snap.workspace = true 23 | serde.workspace = true 24 | 25 | tokio-stream = "0.1.17" 26 | futures = "0.3.31" 27 | k256 = { version = "0.13.3", features = ["ecdh"] } 28 | sha3 = "0.10.8" 29 | 30 | serde_json = "1.0.117" 31 | 32 | # RLPx 33 | concat-kdf = "0.1.0" 34 | hmac = "0.12.1" 35 | aes = "0.8.4" 36 | ctr = "0.9.2" 37 | rand = "0.8.5" 38 | 39 | [dev-dependencies] 40 | hex-literal = "0.4.1" 41 | 42 | [lib] 43 | path = "./p2p.rs" 44 | 45 | [features] 46 | default = ["c-kzg"] 47 | c-kzg = ["ethrex-blockchain/c-kzg", "ethrex-common/c-kzg"] 48 | sync-test = [] 49 | -------------------------------------------------------------------------------- /crates/networking/p2p/discv4/helpers.rs: -------------------------------------------------------------------------------- 1 | use std::time::{Duration, SystemTime, UNIX_EPOCH}; 2 | 3 | pub fn get_msg_expiration_from_seconds(seconds: u64) -> u64 { 4 | (SystemTime::now() + Duration::from_secs(seconds)) 5 | .duration_since(UNIX_EPOCH) 6 | .unwrap_or_default() 7 | .as_secs() 8 | } 9 | 10 | pub fn is_msg_expired(expiration: u64) -> bool { 11 | // this cast to a signed integer is needed as the rlp decoder doesn't take into account the sign 12 | // otherwise if a msg contains a negative expiration, it would pass since as it would wrap around the u64. 13 | (expiration as i64) < (current_unix_time() as i64) 14 | } 15 | 16 | pub fn elapsed_time_since(unix_timestamp: u64) -> u64 { 17 | let time = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(unix_timestamp); 18 | SystemTime::now() 19 | .duration_since(time) 20 | .unwrap_or_default() 21 | .as_secs() 22 | } 23 | 24 | pub fn current_unix_time() -> u64 { 25 | SystemTime::now() 26 | .duration_since(UNIX_EPOCH) 27 | .unwrap_or_default() 28 | .as_secs() 29 | } 30 | -------------------------------------------------------------------------------- /crates/networking/p2p/discv4/mod.rs: -------------------------------------------------------------------------------- 1 | pub(super) mod helpers; 2 | mod lookup; 3 | pub(super) mod messages; 4 | pub mod server; 5 | -------------------------------------------------------------------------------- /crates/networking/p2p/p2p.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod discv4; 2 | pub mod kademlia; 3 | pub mod network; 4 | pub mod peer_handler; 5 | pub mod rlpx; 6 | pub(crate) mod snap; 7 | pub mod sync; 8 | pub mod sync_manager; 9 | pub mod types; 10 | 11 | pub use network::periodically_show_peer_stats; 12 | pub use network::start_network; 13 | -------------------------------------------------------------------------------- /crates/networking/p2p/rlpx.rs: -------------------------------------------------------------------------------- 1 | pub mod connection; 2 | pub mod error; 3 | pub mod eth; 4 | pub mod frame; 5 | pub mod handshake; 6 | pub mod message; 7 | pub mod p2p; 8 | pub mod snap; 9 | pub mod utils; 10 | -------------------------------------------------------------------------------- /crates/networking/p2p/rlpx/eth/eth68/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod receipts; 2 | pub mod status; 3 | -------------------------------------------------------------------------------- /crates/networking/p2p/rlpx/eth/eth69/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod receipts; 2 | pub mod status; 3 | -------------------------------------------------------------------------------- /crates/networking/p2p/rlpx/eth/eth69/receipts.rs: -------------------------------------------------------------------------------- 1 | use crate::rlpx::{ 2 | message::RLPxMessage, 3 | utils::{snappy_compress, snappy_decompress}, 4 | }; 5 | use bytes::BufMut; 6 | use ethrex_common::types::Receipt; 7 | use ethrex_rlp::{ 8 | error::{RLPDecodeError, RLPEncodeError}, 9 | structs::{Decoder, Encoder}, 10 | }; 11 | 12 | #[derive(Debug)] 13 | pub(crate) struct Receipts69 { 14 | // id is a u64 chosen by the requesting peer, the responding peer must mirror the value for the response 15 | // https://github.com/ethereum/devp2p/blob/master/caps/eth.md#protocol-messages 16 | pub id: u64, 17 | pub receipts: Vec>, 18 | } 19 | 20 | impl Receipts69 { 21 | pub fn new(id: u64, receipts: Vec>) -> Self { 22 | Self { receipts, id } 23 | } 24 | } 25 | 26 | impl RLPxMessage for Receipts69 { 27 | const CODE: u8 = 0x0F; 28 | 29 | fn encode(&self, buf: &mut dyn BufMut) -> Result<(), RLPEncodeError> { 30 | let mut encoded_data = vec![]; 31 | Encoder::new(&mut encoded_data) 32 | .encode_field(&self.id) 33 | .encode_field(&self.receipts) 34 | .finish(); 35 | 36 | let msg_data = snappy_compress(encoded_data)?; 37 | buf.put_slice(&msg_data); 38 | Ok(()) 39 | } 40 | 41 | fn decode(msg_data: &[u8]) -> Result { 42 | let decompressed_data = snappy_decompress(msg_data)?; 43 | let decoder = Decoder::new(&decompressed_data)?; 44 | let (id, decoder): (u64, _) = decoder.decode_field("request-id")?; 45 | let (receipts, _): (Vec>, _) = decoder.decode_field("receipts")?; 46 | 47 | Ok(Self::new(id, receipts)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /crates/networking/p2p/rlpx/eth/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod backend; 2 | pub(crate) mod blocks; 3 | mod eth68; 4 | mod eth69; 5 | pub(crate) mod receipts; 6 | pub(crate) mod status; 7 | pub(crate) mod transactions; 8 | pub(crate) mod update; 9 | -------------------------------------------------------------------------------- /crates/networking/rpc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-rpc" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | axum.workspace = true 10 | tower-http = { version = "0.6.2", features = ["cors"] } 11 | serde = { version = "1.0.203", features = ["derive"] } 12 | serde_json = "1.0.117" 13 | tokio = { workspace = true, features = ["full"] } 14 | bytes.workspace = true 15 | tracing.workspace = true 16 | tracing-subscriber.workspace = true 17 | ethrex-common.workspace = true 18 | ethrex-storage.workspace = true 19 | ethrex-vm.workspace = true 20 | ethrex-blockchain.workspace = true 21 | ethrex-p2p.workspace = true 22 | ethrex-rlp.workspace = true 23 | ethrex-storage-rollup = { workspace = true, optional = true } 24 | ethereum-types.workspace = true 25 | hex.workspace = true 26 | axum-extra = { version = "0.10.0", features = ["typed-header"] } 27 | jsonwebtoken.workspace = true 28 | rand.workspace = true 29 | tokio-util = { workspace = true, features = ["codec"] } 30 | reqwest.workspace = true 31 | k256 = { version = "0.13.3", features = ["ecdh"] } 32 | sha3 = "0.10.8" 33 | sha2.workspace = true 34 | 35 | # Clients 36 | envy = "0.4.2" 37 | thiserror.workspace = true 38 | secp256k1.workspace = true 39 | keccak-hash.workspace = true 40 | 41 | cfg-if.workspace = true 42 | 43 | [dev-dependencies] 44 | hex-literal = "0.4.1" 45 | 46 | [lib] 47 | path = "./lib.rs" 48 | 49 | [features] 50 | l2 = ["ethrex-storage-rollup"] 51 | -------------------------------------------------------------------------------- /crates/networking/rpc/admin/mod.rs: -------------------------------------------------------------------------------- 1 | use ethrex_common::types::ChainConfig; 2 | use ethrex_storage::Store; 3 | use serde::Serialize; 4 | use serde_json::Value; 5 | use std::collections::HashMap; 6 | 7 | use crate::{rpc::NodeData, utils::RpcErr}; 8 | mod peers; 9 | pub use peers::peers; 10 | 11 | #[derive(Serialize, Debug)] 12 | struct NodeInfo { 13 | enode: String, 14 | enr: String, 15 | id: String, 16 | ip: String, 17 | name: String, 18 | ports: Ports, 19 | protocols: HashMap, 20 | } 21 | 22 | #[derive(Serialize, Debug)] 23 | struct Ports { 24 | discovery: u16, 25 | listener: u16, 26 | } 27 | 28 | #[derive(Serialize, Debug)] 29 | #[serde(untagged)] 30 | enum Protocol { 31 | Eth(ChainConfig), 32 | } 33 | 34 | pub fn node_info(storage: Store, node_data: &NodeData) -> Result { 35 | let enode_url = node_data.local_p2p_node.enode_url(); 36 | let enr_url = match node_data.local_node_record.enr_url() { 37 | Ok(enr) => enr, 38 | Err(_) => "".into(), 39 | }; 40 | let mut protocols = HashMap::new(); 41 | 42 | let chain_config = storage 43 | .get_chain_config() 44 | .map_err(|error| RpcErr::Internal(error.to_string()))?; 45 | protocols.insert("eth".to_string(), Protocol::Eth(chain_config)); 46 | 47 | let node_info = NodeInfo { 48 | enode: enode_url, 49 | enr: enr_url, 50 | id: hex::encode(node_data.local_p2p_node.node_id()), 51 | name: node_data.client_version.clone(), 52 | ip: node_data.local_p2p_node.ip.to_string(), 53 | ports: Ports { 54 | discovery: node_data.local_p2p_node.udp_port, 55 | listener: node_data.local_p2p_node.tcp_port, 56 | }, 57 | protocols, 58 | }; 59 | serde_json::to_value(node_info).map_err(|error| RpcErr::Internal(error.to_string())) 60 | } 61 | -------------------------------------------------------------------------------- /crates/networking/rpc/clients/beacon/errors.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, thiserror::Error)] 2 | pub enum BeaconClientError { 3 | #[error("reqwest error: {0}")] 4 | ReqwestError(#[from] reqwest::Error), 5 | #[error("Beacon RPC error (code: {0}): {1}")] 6 | RpcError(u64, String), 7 | #[error("Response deserialization error: {0}")] 8 | DeserializeError(#[from] serde_json::Error), 9 | #[error("Error: {0}")] 10 | Custom(String), 11 | } 12 | -------------------------------------------------------------------------------- /crates/networking/rpc/clients/beacon/types.rs: -------------------------------------------------------------------------------- 1 | use ethrex_common::{H256, U256}; 2 | use serde::Deserialize; 3 | use sha2::Digest; 4 | 5 | /// `data` structure of `/eth/v2/beacon/blocks/{block_id}` endpoint's response 6 | #[derive(Deserialize, Debug)] 7 | pub struct GetBlockResponseData { 8 | pub message: GetBlockResponseMessage, 9 | // 96 bytes hex string 10 | #[serde(rename = "signature", with = "ethrex_common::serde_utils::bytes")] 11 | _signature: bytes::Bytes, 12 | } 13 | 14 | /// `data.message` structure of `/eth/v2/beacon/blocks/{block_id}` endpoint's response 15 | // Actual response has many more fields, but we only care about `slot` for now 16 | #[derive(Deserialize, Debug)] 17 | pub struct GetBlockResponseMessage { 18 | #[serde(deserialize_with = "ethrex_common::serde_utils::u256::deser_dec_str")] 19 | pub slot: U256, 20 | } 21 | 22 | /// Each element of `data` array of `/eth/v1/beacon/blob_sidecars/{block_id}` endpoint's response 23 | // Actual response has many more fields, but we only care about these for now 24 | #[derive(Deserialize, Debug)] 25 | pub struct BlobSidecar { 26 | #[serde(deserialize_with = "ethrex_common::serde_utils::u64::deser_dec_str")] 27 | pub index: u64, 28 | #[serde(with = "ethrex_common::serde_utils::bytes")] 29 | pub blob: bytes::Bytes, 30 | #[serde(with = "ethrex_common::serde_utils::bytes")] 31 | pub kzg_commitment: bytes::Bytes, 32 | } 33 | 34 | impl BlobSidecar { 35 | pub fn versioned_hash(&self) -> H256 { 36 | let mut hasher = sha2::Sha256::new(); 37 | hasher.update(&self.kzg_commitment); 38 | 39 | let hash = &mut hasher.finalize(); 40 | hash[0] = 0x01; 41 | 42 | H256::from_slice(hash) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /crates/networking/rpc/clients/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod auth; 2 | pub mod beacon; 3 | pub mod eth; 4 | 5 | pub use auth::{errors::EngineClientError, EngineClient}; 6 | pub use eth::{errors::EthClientError, eth_sender::Overrides, EthClient}; 7 | -------------------------------------------------------------------------------- /crates/networking/rpc/engine/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod exchange_transition_config; 2 | pub mod fork_choice; 3 | pub mod payload; 4 | 5 | use crate::{ 6 | rpc::{RpcApiContext, RpcHandler}, 7 | utils::RpcErr, 8 | utils::RpcRequest, 9 | }; 10 | use serde_json::{json, Value}; 11 | 12 | pub type ExchangeCapabilitiesRequest = Vec; 13 | 14 | /// List of capabilities that the execution layer client supports. Add new capabilities here. 15 | /// More info: https://github.com/ethereum/execution-apis/blob/main/src/engine/common.md#engine_exchangecapabilities 16 | pub const CAPABILITIES: [&str; 14] = [ 17 | "engine_forkchoiceUpdatedV1", 18 | "engine_forkchoiceUpdatedV2", 19 | "engine_forkchoiceUpdatedV3", 20 | "engine_newPayloadV1", 21 | "engine_newPayloadV2", 22 | "engine_newPayloadV3", 23 | "engine_newPayloadV4", 24 | "engine_getPayloadV1", 25 | "engine_getPayloadV2", 26 | "engine_getPayloadV3", 27 | "engine_getPayloadV4", 28 | "engine_exchangeTransitionConfigurationV1", 29 | "engine_getPayloadBodiesByHashV1", 30 | "engine_getPayloadBodiesByRangeV1", 31 | ]; 32 | 33 | impl From for RpcRequest { 34 | fn from(val: ExchangeCapabilitiesRequest) -> Self { 35 | RpcRequest { 36 | method: "engine_exchangeCapabilities".to_string(), 37 | params: Some(vec![serde_json::json!(val)]), 38 | ..Default::default() 39 | } 40 | } 41 | } 42 | 43 | impl RpcHandler for ExchangeCapabilitiesRequest { 44 | fn parse(params: &Option>) -> Result { 45 | params 46 | .as_ref() 47 | .ok_or(RpcErr::BadParams("No params provided".to_owned()))? 48 | .first() 49 | .ok_or(RpcErr::BadParams("Expected 1 param".to_owned())) 50 | .and_then(|v| { 51 | serde_json::from_value(v.clone()) 52 | .map_err(|error| RpcErr::BadParams(error.to_string())) 53 | }) 54 | } 55 | 56 | async fn handle(&self, _context: RpcApiContext) -> Result { 57 | Ok(json!(CAPABILITIES)) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /crates/networking/rpc/eth/client.rs: -------------------------------------------------------------------------------- 1 | use serde_json::Value; 2 | use tracing::info; 3 | 4 | use crate::{ 5 | rpc::{RpcApiContext, RpcHandler}, 6 | utils::RpcErr, 7 | }; 8 | 9 | pub struct ChainId; 10 | impl RpcHandler for ChainId { 11 | fn parse(_params: &Option>) -> Result { 12 | Ok(Self {}) 13 | } 14 | 15 | async fn handle(&self, context: RpcApiContext) -> Result { 16 | info!("Requested chain id"); 17 | let chain_spec = context 18 | .storage 19 | .get_chain_config() 20 | .map_err(|error| RpcErr::Internal(error.to_string()))?; 21 | serde_json::to_value(format!("{:#x}", chain_spec.chain_id)) 22 | .map_err(|error| RpcErr::Internal(error.to_string())) 23 | } 24 | } 25 | 26 | pub struct Syncing; 27 | impl RpcHandler for Syncing { 28 | fn parse(_params: &Option>) -> Result { 29 | Ok(Self {}) 30 | } 31 | 32 | async fn handle(&self, context: RpcApiContext) -> Result { 33 | Ok(Value::Bool(!context.blockchain.is_synced())) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /crates/networking/rpc/l2/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod transaction; 2 | pub mod withdrawal; 3 | -------------------------------------------------------------------------------- /crates/networking/rpc/lib.rs: -------------------------------------------------------------------------------- 1 | mod admin; 2 | mod authentication; 3 | mod engine; 4 | mod eth; 5 | #[cfg(feature = "l2")] 6 | pub mod l2; 7 | mod mempool; 8 | mod net; 9 | mod rpc; 10 | mod tracing; 11 | 12 | pub mod clients; 13 | pub mod types; 14 | pub mod utils; 15 | pub use clients::{EngineClient, EthClient}; 16 | 17 | pub use rpc::start_api; 18 | -------------------------------------------------------------------------------- /crates/networking/rpc/mempool.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use ethrex_common::{Address, H256}; 4 | use serde::Serialize; 5 | use serde_json::Value; 6 | 7 | use crate::{rpc::RpcApiContext, types::transaction::RpcTransaction, utils::RpcErr}; 8 | 9 | /// Maps account sender to its transactions indexed by nonce 10 | type MempoolContentEntry = HashMap>; 11 | 12 | /// Full content of the mempool 13 | /// Transactions are grouped by sender and indexed by nonce 14 | #[derive(Serialize)] 15 | struct MempoolContent { 16 | pending: MempoolContentEntry, 17 | queued: MempoolContentEntry, 18 | } 19 | 20 | /// Handling of rpc endpoint `mempool_content` 21 | pub async fn content(context: RpcApiContext) -> Result { 22 | let transactions = context.blockchain.mempool.content()?; 23 | // Group transactions by sender and nonce and map them to rpc transactions 24 | let mut mempool_content = MempoolContentEntry::new(); 25 | for tx in transactions { 26 | let sender_entry = mempool_content.entry(tx.sender()).or_default(); 27 | sender_entry.insert( 28 | tx.nonce(), 29 | RpcTransaction::build(tx, None, H256::zero(), None), 30 | ); 31 | } 32 | let response = MempoolContent { 33 | pending: mempool_content, 34 | // We have no concept of "queued" transactions yet so we will leave this empty 35 | queued: MempoolContentEntry::new(), 36 | }; 37 | Ok(serde_json::to_value(response)?) 38 | } 39 | -------------------------------------------------------------------------------- /crates/networking/rpc/net/mod.rs: -------------------------------------------------------------------------------- 1 | use serde_json::Value; 2 | 3 | use crate::{ 4 | rpc::RpcApiContext, 5 | utils::{RpcErr, RpcRequest}, 6 | }; 7 | 8 | pub fn version(_req: &RpcRequest, context: RpcApiContext) -> Result { 9 | let chain_spec = context.storage.get_chain_config()?; 10 | 11 | let value = serde_json::to_value(format!("{}", chain_spec.chain_id))?; 12 | Ok(value) 13 | } 14 | 15 | // dummy function 16 | pub fn peer_count(_req: &RpcRequest, _context: RpcApiContext) -> Result { 17 | let value = serde_json::to_value("0")?; 18 | Ok(value) 19 | } 20 | -------------------------------------------------------------------------------- /crates/networking/rpc/types/account_proof.rs: -------------------------------------------------------------------------------- 1 | use ethrex_common::{serde_utils, Address, H256, U256}; 2 | use serde::{ser::SerializeSeq, Serialize, Serializer}; 3 | 4 | #[derive(Debug, Serialize)] 5 | #[serde(rename_all = "camelCase")] 6 | pub struct AccountProof { 7 | #[serde(serialize_with = "serialize_proofs")] 8 | pub account_proof: Vec>, 9 | pub address: Address, 10 | pub balance: U256, 11 | pub code_hash: H256, 12 | #[serde(with = "serde_utils::u64::hex_str")] 13 | pub nonce: u64, 14 | pub storage_hash: H256, 15 | pub storage_proof: Vec, 16 | } 17 | 18 | #[derive(Debug, Serialize)] 19 | #[serde(rename_all = "camelCase")] 20 | pub struct StorageProof { 21 | pub key: U256, 22 | #[serde(serialize_with = "serialize_proofs")] 23 | pub proof: Vec>, 24 | pub value: U256, 25 | } 26 | 27 | pub fn serialize_proofs(value: &Vec>, serializer: S) -> Result 28 | where 29 | S: Serializer, 30 | { 31 | let mut seq_serializer = serializer.serialize_seq(Some(value.len()))?; 32 | for encoded_node in value { 33 | seq_serializer.serialize_element(&format!("0x{}", hex::encode(encoded_node)))?; 34 | } 35 | seq_serializer.end() 36 | } 37 | -------------------------------------------------------------------------------- /crates/networking/rpc/types/fork_choice.rs: -------------------------------------------------------------------------------- 1 | use super::payload::PayloadStatus; 2 | use ethrex_common::{serde_utils, types::Withdrawal, Address, H256}; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Debug, Deserialize, Serialize)] 6 | #[serde(rename_all = "camelCase")] 7 | pub struct ForkChoiceState { 8 | #[allow(unused)] 9 | pub head_block_hash: H256, 10 | pub safe_block_hash: H256, 11 | pub finalized_block_hash: H256, 12 | } 13 | 14 | #[derive(Debug, Deserialize, Default, Serialize)] 15 | #[serde(rename_all = "camelCase")] 16 | #[allow(unused)] 17 | pub struct PayloadAttributesV3 { 18 | #[serde(with = "serde_utils::u64::hex_str")] 19 | pub timestamp: u64, 20 | pub prev_randao: H256, 21 | pub suggested_fee_recipient: Address, 22 | pub withdrawals: Option>, 23 | pub parent_beacon_block_root: Option, 24 | } 25 | 26 | #[derive(Debug, Serialize, Deserialize)] 27 | #[serde(rename_all = "camelCase")] 28 | pub struct ForkChoiceResponse { 29 | pub payload_status: PayloadStatus, 30 | #[serde(with = "serde_utils::u64::hex_str_opt_padded")] 31 | pub payload_id: Option, 32 | } 33 | 34 | impl ForkChoiceResponse { 35 | pub fn set_id(&mut self, id: u64) { 36 | self.payload_id = Some(id) 37 | } 38 | } 39 | 40 | impl From for ForkChoiceResponse { 41 | fn from(value: PayloadStatus) -> Self { 42 | Self { 43 | payload_status: value, 44 | payload_id: None, 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /crates/networking/rpc/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod account_proof; 2 | pub mod block; 3 | pub mod block_identifier; 4 | pub mod fork_choice; 5 | pub mod payload; 6 | pub mod receipt; 7 | pub mod transaction; 8 | -------------------------------------------------------------------------------- /crates/storage/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-storage" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | ethrex-rlp.workspace = true 10 | ethrex-common.workspace = true 11 | ethrex-trie.workspace = true 12 | 13 | async-trait.workspace = true 14 | ethereum-types.workspace = true 15 | anyhow = "1.0.86" 16 | bytes.workspace = true 17 | tracing.workspace = true 18 | thiserror.workspace = true 19 | sha3.workspace = true 20 | hex.workspace = true 21 | serde = { version = "1.0.203", features = ["derive"] } 22 | serde_json = "1.0.117" 23 | libmdbx = { workspace = true, optional = true } 24 | redb = { workspace = true, optional = true } 25 | # NOTE: intentionally avoiding the workspace dep as it brings "full" features, breaking the provers 26 | # We only need the runtime for the blocking databases to spawn blocking tasks 27 | tokio = { version = "1.41.1", optional = true, default-features = false, features = ["rt"] } 28 | 29 | [features] 30 | default = [] 31 | libmdbx = ["dep:libmdbx", "ethrex-trie/libmdbx", "dep:tokio"] 32 | redb = ["dep:redb", "dep:tokio"] 33 | 34 | [dev-dependencies] 35 | hex.workspace = true 36 | hex-literal.workspace = true 37 | tempdir = "0.3.7" 38 | tokio.workspace = true 39 | 40 | [lib] 41 | path = "./lib.rs" 42 | 43 | [lints.clippy] 44 | unwrap_used = "deny" 45 | -------------------------------------------------------------------------------- /crates/storage/clippy.toml: -------------------------------------------------------------------------------- 1 | allow-unwrap-in-tests = true 2 | -------------------------------------------------------------------------------- /crates/storage/error.rs: -------------------------------------------------------------------------------- 1 | use ethrex_rlp::error::RLPDecodeError; 2 | use ethrex_trie::TrieError; 3 | #[cfg(feature = "redb")] 4 | use redb::{CommitError, DatabaseError, StorageError, TableError, TransactionError}; 5 | use thiserror::Error; 6 | 7 | // TODO improve errors 8 | #[derive(Debug, Error)] 9 | pub enum StoreError { 10 | #[error("DecodeError")] 11 | DecodeError, 12 | #[cfg(feature = "libmdbx")] 13 | #[error("Libmdbx error: {0}")] 14 | LibmdbxError(anyhow::Error), 15 | #[cfg(feature = "redb")] 16 | #[error("Redb Storage error: {0}")] 17 | RedbStorageError(#[from] StorageError), 18 | #[cfg(feature = "redb")] 19 | #[error("Redb Table error: {0}")] 20 | RedbTableError(#[from] TableError), 21 | #[cfg(feature = "redb")] 22 | #[error("Redb Commit error: {0}")] 23 | RedbCommitError(#[from] CommitError), 24 | #[cfg(feature = "redb")] 25 | #[error("Redb Transaction error: {0}")] 26 | RedbTransactionError(#[from] TransactionError), 27 | #[error("Redb Database error: {0}")] 28 | #[cfg(feature = "redb")] 29 | RedbDatabaseError(#[from] DatabaseError), 30 | #[error("Redb Cast error")] 31 | #[cfg(feature = "redb")] 32 | RedbCastError, 33 | #[error("{0}")] 34 | Custom(String), 35 | #[error(transparent)] 36 | RLPDecode(#[from] RLPDecodeError), 37 | #[error(transparent)] 38 | Trie(#[from] TrieError), 39 | #[error("missing store: is an execution DB being used instead?")] 40 | MissingStore, 41 | #[error("Could not open DB for reading")] 42 | ReadError, 43 | #[error("Could not instantiate cursor for table {0}")] 44 | CursorError(String), 45 | #[error("Missing latest block number")] 46 | MissingLatestBlockNumber, 47 | #[error("Missing earliest block number")] 48 | MissingEarliestBlockNumber, 49 | #[error("Failed to lock mempool for writing")] 50 | MempoolWriteLock(String), 51 | #[error("Failed to lock mempool for reading")] 52 | MempoolReadLock(String), 53 | } 54 | -------------------------------------------------------------------------------- /crates/storage/lib.rs: -------------------------------------------------------------------------------- 1 | mod api; 2 | 3 | #[cfg(any(feature = "libmdbx", feature = "redb"))] 4 | mod rlp; 5 | mod store; 6 | mod store_db; 7 | mod trie_db; 8 | mod utils; 9 | 10 | pub mod error; 11 | pub use store::{ 12 | hash_address, hash_key, EngineType, Store, MAX_SNAPSHOT_READS, STATE_TRIE_SEGMENTS, 13 | }; 14 | -------------------------------------------------------------------------------- /crates/storage/store_db/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod in_memory; 2 | #[cfg(feature = "libmdbx")] 3 | pub mod libmdbx; 4 | #[cfg(feature = "redb")] 5 | pub mod redb; 6 | -------------------------------------------------------------------------------- /crates/storage/trie_db/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "libmdbx")] 2 | pub mod libmdbx; 3 | #[cfg(feature = "libmdbx")] 4 | pub mod libmdbx_dupsort; 5 | #[cfg(feature = "redb")] 6 | pub mod redb; 7 | #[cfg(feature = "redb")] 8 | pub mod redb_multitable; 9 | #[cfg(test)] 10 | mod test_utils; 11 | mod utils; 12 | -------------------------------------------------------------------------------- /crates/storage/trie_db/redb.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use ethrex_trie::{NodeHash, TrieDB, TrieError}; 4 | use redb::{Database, TableDefinition}; 5 | 6 | const TABLE: TableDefinition<&[u8], &[u8]> = TableDefinition::new("Trie"); 7 | 8 | pub struct RedBTrie { 9 | db: Arc, 10 | } 11 | 12 | impl RedBTrie { 13 | pub fn new(db: Arc) -> Self { 14 | Self { db } 15 | } 16 | } 17 | 18 | impl TrieDB for RedBTrie { 19 | fn get(&self, key: NodeHash) -> Result>, TrieError> { 20 | let read_txn = self 21 | .db 22 | .begin_read() 23 | .map_err(|e| TrieError::DbError(e.into()))?; 24 | let table = read_txn 25 | .open_table(TABLE) 26 | .map_err(|e| TrieError::DbError(e.into()))?; 27 | Ok(table 28 | .get(key.as_ref()) 29 | .map_err(|e| TrieError::DbError(e.into()))? 30 | .map(|value| value.value().to_vec())) 31 | } 32 | 33 | fn put_batch(&self, key_values: Vec<(NodeHash, Vec)>) -> Result<(), TrieError> { 34 | let write_txn = self 35 | .db 36 | .begin_write() 37 | .map_err(|e| TrieError::DbError(e.into()))?; 38 | { 39 | let mut table = write_txn 40 | .open_table(TABLE) 41 | .map_err(|e| TrieError::DbError(e.into()))?; 42 | for (key, value) in key_values { 43 | table 44 | .insert(key.as_ref(), &*value) 45 | .map_err(|e| TrieError::DbError(e.into()))?; 46 | } 47 | } 48 | write_txn 49 | .commit() 50 | .map_err(|e| TrieError::DbError(e.into()))?; 51 | 52 | Ok(()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /crates/storage/trie_db/test_utils.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "libmdbx")] 2 | pub mod libmdbx { 3 | use std::{path::PathBuf, sync::Arc}; 4 | 5 | use ethrex_trie::NodeHash; 6 | use libmdbx::{ 7 | orm::{table_info, Database, Table}, 8 | table, 9 | }; 10 | 11 | table!( 12 | /// Test table. 13 | (TestNodes) NodeHash => Vec 14 | ); 15 | 16 | /// Creates a new DB on a given path 17 | pub fn new_db_with_path(path: PathBuf) -> Arc { 18 | let tables = [table_info!(T)].into_iter().collect(); 19 | Arc::new(Database::create(Some(path), &tables).expect("Failed creating db with path")) 20 | } 21 | 22 | /// Creates a new temporary DB 23 | pub fn new_db() -> Arc { 24 | let tables = [table_info!(T)].into_iter().collect(); 25 | Arc::new(Database::create(None, &tables).expect("Failed to create temp DB")) 26 | } 27 | 28 | /// Opens a DB from a given path 29 | pub fn open_db(path: &str) -> Arc { 30 | let tables = [table_info!(T)].into_iter().collect(); 31 | Arc::new(Database::open(path, &tables).expect("Failed to open DB")) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /crates/storage/trie_db/utils.rs: -------------------------------------------------------------------------------- 1 | #[cfg(any(feature = "libmdbx", feature = "redb"))] 2 | // In order to use NodeHash as key in a dupsort table we must encode it into a fixed size type 3 | pub fn node_hash_to_fixed_size(node_hash: ethrex_trie::NodeHash) -> [u8; 33] { 4 | let node_hash_ref = node_hash.as_ref(); 5 | let original_len = node_hash.len(); 6 | // original len will always be lower or equal to 32 bytes 7 | debug_assert!(original_len <= 32); 8 | 9 | let mut buffer = [0u8; 33]; 10 | 11 | // Encode the node as [original_len, node_hash...] 12 | buffer[0] = original_len as u8; 13 | buffer[1..(original_len + 1)].copy_from_slice(node_hash_ref); 14 | buffer 15 | } 16 | -------------------------------------------------------------------------------- /crates/vm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-vm" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | ethrex-common.workspace = true 8 | ethrex-levm.workspace = true 9 | ethrex-trie.workspace = true 10 | ethrex-rlp.workspace = true 11 | 12 | revm = { version = "19.0.0", features = [ 13 | "serde", 14 | "std", 15 | "serde-json", 16 | "optional_no_base_fee", 17 | "optional_block_gas_limit", 18 | ], default-features = false } 19 | 20 | # These dependencies must be kept up to date with the corresponding revm version, otherwise errors may pop up because of trait implementation mismatches 21 | revm-inspectors = { version = "0.15.0" } 22 | derive_more = { version = "1.0.0", features = ["full"] } 23 | revm-primitives = { version = "15.2.0", features = [ 24 | "std", 25 | ], default-features = false } 26 | bytes.workspace = true 27 | thiserror.workspace = true 28 | hex.workspace = true 29 | lazy_static.workspace = true 30 | cfg-if.workspace = true 31 | tracing.workspace = true 32 | serde.workspace = true 33 | 34 | bincode = "1" 35 | dyn-clone = "1.0" 36 | 37 | ethereum-types.workspace = true 38 | 39 | [lib] 40 | path = "./lib.rs" 41 | 42 | [features] 43 | default = [] 44 | l2 = ["ethrex-levm/l2"] 45 | c-kzg = ["revm/c-kzg", "ethrex-levm/c-kzg", "ethrex-common/c-kzg"] 46 | blst = ["revm/blst"] 47 | 48 | [lints.clippy] 49 | unwrap_used = "deny" 50 | -------------------------------------------------------------------------------- /crates/vm/constants.rs: -------------------------------------------------------------------------------- 1 | use ethrex_common::Address; 2 | use std::{str::FromStr, sync::LazyLock}; 3 | 4 | pub static SYSTEM_ADDRESS: LazyLock
= LazyLock::new(|| { 5 | Address::from_str("fffffffffffffffffffffffffffffffffffffffe") 6 | .expect("Failed to get address from string") 7 | }); 8 | pub static BEACON_ROOTS_ADDRESS: LazyLock
= LazyLock::new(|| { 9 | Address::from_str("000F3df6D732807Ef1319fB7B8bB8522d0Beac02") 10 | .expect("Failed to get address from string") 11 | }); 12 | pub static HISTORY_STORAGE_ADDRESS: LazyLock
= LazyLock::new(|| { 13 | Address::from_str("0000F90827F1C53a10cb7A02335B175320002935") 14 | .expect("Failed to get address from string") 15 | }); 16 | pub static WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS: LazyLock
= LazyLock::new(|| { 17 | Address::from_str("00000961Ef480Eb55e80D19ad83579A64c007002") 18 | .expect("Failed to get address from string") 19 | }); 20 | pub static CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS: LazyLock
= LazyLock::new(|| { 21 | Address::from_str("0000BBdDc7CE488642fb579F8B00f3a590007251") 22 | .expect("Failed to get address from string") 23 | }); 24 | -------------------------------------------------------------------------------- /crates/vm/db.rs: -------------------------------------------------------------------------------- 1 | use crate::EvmError; 2 | use bytes::Bytes; 3 | use dyn_clone::DynClone; 4 | use ethrex_common::{ 5 | types::{AccountInfo, ChainConfig}, 6 | Address, H256, U256, 7 | }; 8 | 9 | pub trait VmDatabase: Send + Sync + DynClone { 10 | fn get_account_info(&self, address: Address) -> Result, EvmError>; 11 | fn get_storage_slot(&self, address: Address, key: H256) -> Result, EvmError>; 12 | fn get_block_hash(&self, block_number: u64) -> Result; 13 | fn get_chain_config(&self) -> Result; 14 | fn get_account_code(&self, code_hash: H256) -> Result; 15 | } 16 | 17 | dyn_clone::clone_trait_object!(VmDatabase); 18 | 19 | pub type DynVmDatabase = Box; 20 | -------------------------------------------------------------------------------- /crates/vm/helpers.rs: -------------------------------------------------------------------------------- 1 | use ethrex_common::types::{ChainConfig, Fork}; 2 | pub use revm::primitives::SpecId; 3 | 4 | /// Returns the spec id according to the block timestamp and the stored chain config 5 | /// WARNING: Assumes at least Merge fork is active 6 | pub fn spec_id(chain_config: &ChainConfig, block_timestamp: u64) -> SpecId { 7 | fork_to_spec_id(chain_config.get_fork(block_timestamp)) 8 | } 9 | 10 | pub fn fork_to_spec_id(fork: Fork) -> SpecId { 11 | match fork { 12 | Fork::Frontier => SpecId::FRONTIER, 13 | Fork::FrontierThawing => SpecId::FRONTIER_THAWING, 14 | Fork::Homestead => SpecId::HOMESTEAD, 15 | Fork::DaoFork => SpecId::DAO_FORK, 16 | Fork::Tangerine => SpecId::TANGERINE, 17 | Fork::SpuriousDragon => SpecId::SPURIOUS_DRAGON, 18 | Fork::Byzantium => SpecId::BYZANTIUM, 19 | Fork::Constantinople => SpecId::CONSTANTINOPLE, 20 | Fork::Petersburg => SpecId::PETERSBURG, 21 | Fork::Istanbul => SpecId::ISTANBUL, 22 | Fork::MuirGlacier => SpecId::MUIR_GLACIER, 23 | Fork::Berlin => SpecId::BERLIN, 24 | Fork::London => SpecId::LONDON, 25 | Fork::ArrowGlacier => SpecId::ARROW_GLACIER, 26 | Fork::GrayGlacier => SpecId::GRAY_GLACIER, 27 | Fork::Paris => SpecId::MERGE, 28 | Fork::Shanghai => SpecId::SHANGHAI, 29 | Fork::Cancun => SpecId::CANCUN, 30 | Fork::Prague => SpecId::PRAGUE, 31 | Fork::Osaka => SpecId::OSAKA, 32 | } 33 | } 34 | 35 | use ethrex_common::Address; 36 | 37 | pub fn create_contract_address(from: Address, nonce: u64) -> Address { 38 | Address::from_slice( 39 | revm::primitives::Address(from.0.into()) 40 | .create(nonce) 41 | .0 42 | .as_ref(), 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /crates/vm/levm/.gitignore: -------------------------------------------------------------------------------- 1 | *.tar.gz 2 | *.svg 3 | 4 | tests/ef/tests 5 | tmp/ 6 | target/ 7 | -------------------------------------------------------------------------------- /crates/vm/levm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethrex-levm" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | [dependencies] 7 | lazy_static.workspace = true 8 | ethrex-common.workspace = true 9 | ethrex-rlp.workspace = true 10 | derive_more = { version = "1.0.0", features = ["full"] } 11 | 12 | bytes.workspace = true 13 | keccak-hash.workspace = true 14 | thiserror.workspace = true 15 | serde = { workspace = true, features = ["derive", "rc"] } 16 | serde_json.workspace = true 17 | 18 | sha3 = "0.10.8" 19 | datatest-stable = "0.2.9" 20 | walkdir = "2.5.0" 21 | secp256k1.workspace = true 22 | p256 = { version = "0.13.2", features = ["ecdsa", "arithmetic", "expose-field"] } 23 | sha2 = "0.10.8" 24 | ripemd = "0.1.3" 25 | num-bigint = "0.4.5" 26 | lambdaworks-math = "0.11.0" 27 | k256 = { version = "0.13.3", features = ["ecdh"] } 28 | kzg-rs = "0.2.3" 29 | bls12_381 = { git = "https://github.com/lambdaclass/bls12_381", branch = "expose-fp-struct", features = [ 30 | "groups", 31 | "bits", 32 | "pairings", 33 | "alloc", 34 | "experimental", 35 | ] } 36 | 37 | [dev-dependencies] 38 | hex.workspace = true 39 | colored = "2.1.0" 40 | spinoff = "0.8.0" 41 | 42 | [features] 43 | default = [] 44 | c-kzg = ["ethrex-common/c-kzg"] 45 | ethereum_foundation_tests = [] 46 | l2 = [] 47 | 48 | [lints.rust] 49 | unsafe_code = "forbid" 50 | warnings = "warn" 51 | rust_2018_idioms = "warn" 52 | 53 | [lints.clippy] 54 | panic = "deny" 55 | unnecessary_cast = "warn" 56 | deref_by_slicing = "warn" 57 | indexing_slicing = "warn" 58 | manual_unwrap_or = "warn" 59 | manual_unwrap_or_default = "warn" 60 | as_conversions = "deny" 61 | unwrap_used = "deny" 62 | expect_used = "deny" 63 | 64 | arithmetic_side_effects = "deny" 65 | panicking_overflow_checks = "warn" 66 | manual_saturating_arithmetic = "warn" 67 | 68 | [lib] 69 | path = "./src/lib.rs" 70 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/.gitignore: -------------------------------------------------------------------------------- 1 | *.bin* 2 | **/*.bin* 3 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "revm_comparison" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [lib] 7 | name = "revm_comparison" 8 | path = "src/lib.rs" 9 | 10 | [dependencies] 11 | ethrex-levm = { path = "../../" } 12 | ethrex-vm.workspace = true 13 | ethrex-common.workspace = true 14 | ethrex-storage.workspace = true 15 | ethrex-blockchain.workspace = true 16 | hex.workspace = true 17 | bytes.workspace = true 18 | 19 | revm = "9.0.0" 20 | sha3 = "0.10.8" 21 | 22 | [[bin]] 23 | name = "compile" 24 | path = "src/compile.rs" 25 | 26 | [[bin]] 27 | name = "benchmark" 28 | path = "src/benchmark.rs" 29 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/README.md: -------------------------------------------------------------------------------- 1 | # Benchmarks 2 | This README explains how to run benchmarks to compare the performance of `levm` and `revm` when running different contracts. The benchmarking tool used to gather performance metrics is [hyperfine](https://github.com/sharkdp/hyperfine), and the obtained results will be included for reference. 3 | 4 | To run the benchmarks (from `levm`'s root): 5 | 6 | ```bash 7 | make revm-comparison 8 | ``` 9 | 10 | Note that first you will need to install the solidity compiler 11 | On mac you can use homebrew: 12 | 13 | ```bash 14 | brew install solidity 15 | ``` 16 | 17 | For other installation methods check out the [official solidity installation guide](https://docs.soliditylang.org/en/latest/installing-solidity.html) 18 | 19 | ## Factorial 20 | This program computes the nth factorial number, with n passed via calldata. We chose 1000 as n and ran the program on a loop 100,000 times. 21 | 22 | These are the obtained results: 23 | 24 | ### MacBook Air M1 (16 GB RAM) 25 | | | Mean [s] | Min [s] | Max [s] | Relative | 26 | |--------|---------------|---------|---------|-------------| 27 | | `revm` | 6.719 ± 0.047 | 6.677 | 6.843 | 1.00 | 28 | | `levm` | 8.283 ± 0.031 | 8.244 | 8.349 | 1.23 ± 0.01 | 29 | 30 | ## Fibonacci 31 | This program computed the nth Fibonacci number, with n passed via calldata. Again, we chose 1000 as n and ran the program on a loop 100,000 times. 32 | 33 | These are the obtained results: 34 | 35 | ### MacBook Air M1 (16 GB RAM) 36 | | | Mean [s] | Min [s] | Max [s] | Relative | 37 | |--------|---------------|---------|---------|-------------| 38 | | `revm` | 6.213 ± 0.029 | 6.169 | 6.253 | 1.00 | 39 | | `levm` | 8.303 ± 0.094 | 8.204 | 8.498 | 1.33 ± 0.02 | 40 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/contracts/BubbleSort.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | contract BubbleSort { 5 | uint256[] public numbers; 6 | 7 | function Benchmark(uint256 amount) public returns (uint8 result) { 8 | // Fill array with random numbers 9 | for (uint256 i = 0; i < amount; i++) { 10 | numbers.push(uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao, i))) % 100); 11 | } 12 | uint256 n = numbers.length; 13 | for (uint256 i = 0; i < n - 1; i++) { 14 | for (uint256 j = 0; j < n - i - 1; j++) { 15 | if (numbers[j] > numbers[j + 1]) { 16 | (numbers[j], numbers[j + 1]) = (numbers[j + 1], numbers[j]); 17 | } 18 | } 19 | } 20 | // Ensure the array is sorted 21 | for (uint256 i = 0; i < n - 1; i++) { 22 | require(numbers[i] <= numbers[i + 1]); 23 | } 24 | return 0; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/contracts/Factorial.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | contract Factorial { 5 | function Benchmark(uint256 n) public pure returns (uint256 result) { 6 | if (n == 0 || n == 1) { 7 | return 1; 8 | } 9 | 10 | result = 1; 11 | for (uint256 i = 2; i <= n; i++) { 12 | // Check for overflow 13 | if (result > (type(uint256).max / i)) { 14 | return type(uint256).max; 15 | } else { 16 | result *= i; 17 | } 18 | } 19 | 20 | return result; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/contracts/FactorialRecursive.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract FactorialRecursive { 5 | function Benchmark(uint256 n) public view returns (uint256) { 6 | // Base cases 7 | if (n == 0 || n == 1) { 8 | return 1; 9 | } 10 | 11 | // Recursive call via external function 12 | uint256 rec = this.Benchmark(n - 1); 13 | 14 | // Check for overflow 15 | if (rec > (type(uint256).max / n)) { 16 | return type(uint256).max; 17 | } 18 | 19 | return n * rec; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/contracts/Fibonacci.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | contract Fibonacci { 5 | function Benchmark(uint256 n) public pure returns (uint256 result) { 6 | if (n <= 1) return n; 7 | 8 | uint256 a = 0; 9 | uint256 b = 1; 10 | 11 | for (uint256 i = 2; i <= n; i++) { 12 | // Check for overflow 13 | if (b > (type(uint256).max - a)) { 14 | return type(uint256).max; 15 | } 16 | (a, b) = (b, a + b); 17 | } 18 | 19 | result = b; 20 | return result; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/contracts/ManyHashes.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | contract ManyHashes { 5 | function Benchmark(uint256 n) external pure returns (bytes32 result) { 6 | result = bytes32(0); 7 | for (uint256 i = 0; i < n; i++) { 8 | result = keccak256(abi.encodePacked(i)); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/contracts/erc20/ERC20ApprovalTransfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "./lib/ERC20.sol"; 5 | 6 | contract ERC20Approval is ERC20 { 7 | constructor() ERC20("ERC20Approval", "E20_APP") {} 8 | 9 | function Benchmark(uint256 n) external returns (uint256 result) { 10 | _mint(msg.sender, 1000000000 * 10**decimals()); 11 | for (uint256 i = 1; i < n; i++) { 12 | require( 13 | allowance(msg.sender, msg.sender) == 0, 14 | "Allowance at start is nonzero" 15 | ); 16 | approve(msg.sender, i); 17 | require( 18 | allowance(msg.sender, msg.sender) == i, 19 | "Sender has no allowance" 20 | ); 21 | transferFrom(msg.sender, msg.sender, i); 22 | require( 23 | allowance(msg.sender, msg.sender) == 0, 24 | "Allowance at end is nonzero" 25 | ); 26 | } 27 | return balanceOf(msg.sender); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/contracts/erc20/ERC20Mint.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "./lib/ERC20.sol"; 5 | 6 | contract ERC20Mint is ERC20 { 7 | constructor() ERC20("ERC20_Mint", "E20_MINT") {} 8 | 9 | function Benchmark(uint256 n) external returns (uint256 result) { 10 | address testAddress = 0x1234567890123456789012345678901234567890; 11 | for (uint256 i = 0; i < n; i++) { 12 | _mint(testAddress, i); 13 | } 14 | return balanceOf(testAddress); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/contracts/erc20/ERC20Transfer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "./lib/ERC20.sol"; 5 | 6 | contract ERC20Transfer is ERC20 { 7 | constructor() ERC20("ERC20_Transfer", "E20_TRANSFER") {} 8 | 9 | function Benchmark(uint256 n) external returns (uint256 result) { 10 | address testAddress = 0x1234567890123456789012345678901234567890; 11 | _mint(_msgSender(), 10000 * 10**decimals()); 12 | for (uint256 i = 0; i < n; i++) { 13 | transfer(testAddress, i); 14 | } 15 | return balanceOf(testAddress); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/contracts/erc20/lib/Context.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) 3 | 4 | pragma solidity ^0.8.0; 5 | 6 | /** 7 | * @dev Provides information about the current execution context, including the 8 | * sender of the transaction and its data. While these are generally available 9 | * via msg.sender and msg.data, they should not be accessed in such a direct 10 | * manner, since when dealing with meta-transactions the account sending and 11 | * paying for execution may not be the actual sender (as far as an application 12 | * is concerned). 13 | * 14 | * This contract is only required for intermediate, library-like contracts. 15 | */ 16 | abstract contract Context { 17 | function _msgSender() internal view virtual returns (address) { 18 | return msg.sender; 19 | } 20 | 21 | function _msgData() internal view virtual returns (bytes calldata) { 22 | return msg.data; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/contracts/erc20/lib/IERC20Metadata.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) 3 | 4 | pragma solidity ^0.8.0; 5 | 6 | import "./IERC20.sol"; 7 | 8 | /** 9 | * @dev Interface for the optional metadata functions from the ERC20 standard. 10 | * 11 | * _Available since v4.1._ 12 | */ 13 | interface IERC20Metadata is IERC20 { 14 | /** 15 | * @dev Returns the name of the token. 16 | */ 17 | function name() external view returns (string memory); 18 | 19 | /** 20 | * @dev Returns the symbol of the token. 21 | */ 22 | function symbol() external view returns (string memory); 23 | 24 | /** 25 | * @dev Returns the decimals places of the token. 26 | */ 27 | function decimals() external view returns (uint8); 28 | } 29 | -------------------------------------------------------------------------------- /crates/vm/levm/bench/revm_comparison/src/benchmark.rs: -------------------------------------------------------------------------------- 1 | use revm_comparison::{generate_calldata, load_contract_bytecode, run_with_levm, run_with_revm}; 2 | 3 | enum VM { 4 | Revm, 5 | Levm, 6 | } 7 | 8 | fn main() { 9 | let usage = "usage: benchmark [revm/levm] [bench_name] (#repetitions) (#iterations)"; 10 | let vm = std::env::args().nth(1).expect(usage); 11 | 12 | let vm = match vm.as_str() { 13 | "levm" => VM::Levm, 14 | "revm" => VM::Revm, 15 | _ => { 16 | eprintln!("{}", usage); 17 | std::process::exit(1); 18 | } 19 | }; 20 | 21 | let benchmark = std::env::args().nth(2).expect(usage); 22 | 23 | let runs: u64 = std::env::args() 24 | .nth(3) 25 | .unwrap_or_else(|| "10".to_string()) // Default to 10 runs 26 | .parse() 27 | .expect(usage); 28 | 29 | let number_of_iterations: u64 = std::env::args() 30 | .nth(4) 31 | .unwrap_or_else(|| "100".to_string()) // Default to 10 iterations 32 | .parse() 33 | .expect(usage); 34 | 35 | let bytecode = load_contract_bytecode(&benchmark); 36 | let calldata = generate_calldata("Benchmark", number_of_iterations); 37 | 38 | match vm { 39 | VM::Levm => run_with_levm(&bytecode, runs, &calldata), 40 | VM::Revm => run_with_revm(&bytecode, runs, &calldata), 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /crates/vm/levm/docs/callframe.md: -------------------------------------------------------------------------------- 1 | ### CallFrame 2 | 3 | The CallFrame has attributes `output` and `sub_return_data` to store both the return data of the current context and of the sub-context. 4 | 5 | Opcodes like `RETURNDATACOPY` and `RETURNDATASIZE` access the return data of the subcontext (`sub_return_data`). 6 | Meanwhile, opcodes like `RETURN` or `REVERT` modify the return data of the current context (`output`). 7 | -------------------------------------------------------------------------------- /crates/vm/levm/docs/forks.md: -------------------------------------------------------------------------------- 1 | ## Forks 2 | 3 | In LEVM we won't implement forks previous to Paris. 4 | The main reasons are: 5 | - Ethrex client will only support post-Merge forks 6 | - There are some aspects of previous forks that add complexity to the EVM, something we try to avoid whenever possible. 7 | 8 | For example, thanks to [EIP-158](https://eips.ethereum.org/EIPS/eip-158) from Tangerine Whistle onwards we don't need to differentiate existing empty accounts from non-existing empty accounts because the definition of empty account changed. Now empty accounts don't exist in the [trie](https://ethereum.org/en/developers/docs/data-structures-and-encoding/patricia-merkle-trie/). So the EVM can easily check if an account exists or not just by checking that it doesn't have nonce, balance nor code! Implementing forks previous to this change would definitely add more complexity to the EVM. 9 | We also avoid filling LEVM with `if` statements of old forks, this way the code is cleaner and easier to understand. 10 | 11 | 12 | Note: EFTests from older forks are also tested for more recent forks, so we aren't missing any tests. The tests remain the same but we just check the results for the forks we are interested in. 13 | -------------------------------------------------------------------------------- /crates/vm/levm/docs/validations.md: -------------------------------------------------------------------------------- 1 | ## Transaction Validation 2 | 3 | 1. **GASLIMIT_PRICE_PRODUCT_OVERFLOW**: The product of gas limit and gas price is too high. 4 | 2. **INSUFFICIENT_ACCOUNT_FUNDS**: Sender does not have enough funds to pay for the gas. 5 | 3. **INSUFFICIENT_MAX_FEE_PER_GAS**: The max fee per gas is lower than the base fee per gas. 6 | 4. **INITCODE_SIZE_EXCEEDED**: The size of the initcode is too big. 7 | 5. **INTRINSIC_GAS_TOO_LOW**: The gas limit is lower than the intrinsic gas. 8 | 6. **NONCE_IS_MAX**: The nonce of the sender is at its maximum value. 9 | 7. **PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS**: The priority fee is greater than the max fee per gas. 10 | 8. **SENDER_NOT_EOA**: The sender is not an EOA (it has code). 11 | 9. **GAS_ALLOWANCE_EXCEEDED**: The gas limit is higher than the block gas limit. 12 | 10. **INSUFFICIENT_MAX_FEE_PER_BLOB_GAS**: The max fee per blob gas is lower than the base fee per gas. 13 | 11. **TYPE_3_TX_ZERO_BLOBS**: The transaction has zero blobs. 14 | 12. **TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH**: The blob versioned hash is invalid. 15 | 13. **TYPE_3_TX_PRE_FORK**: The transaction is a pre-cancun transaction. 16 | 14. **TYPE_3_TX_BLOB_COUNT_EXCEEDED**: The blob count is higher than the max allowed. 17 | 15. **TYPE_3_TX_CONTRACT_CREATION**: The type 3 transaction is a contract creation. 18 | -------------------------------------------------------------------------------- /crates/vm/levm/src/db/cache.rs: -------------------------------------------------------------------------------- 1 | use ethrex_common::{types::Account, Address}; 2 | use std::collections::HashMap; 3 | 4 | pub type CacheDB = HashMap; 5 | 6 | pub fn account_is_cached(cached_accounts: &CacheDB, address: &Address) -> bool { 7 | cached_accounts.contains_key(address) 8 | } 9 | 10 | pub fn get_account<'cache>( 11 | cached_accounts: &'cache CacheDB, 12 | address: &Address, 13 | ) -> Option<&'cache Account> { 14 | cached_accounts.get(address) 15 | } 16 | 17 | pub fn get_account_mut<'cache>( 18 | cached_accounts: &'cache mut CacheDB, 19 | address: &Address, 20 | ) -> Option<&'cache mut Account> { 21 | cached_accounts.get_mut(address) 22 | } 23 | 24 | pub fn insert_account( 25 | cached_accounts: &mut CacheDB, 26 | address: Address, 27 | account: Account, 28 | ) -> Option { 29 | cached_accounts.insert(address, account) 30 | } 31 | 32 | pub fn remove_account(cached_accounts: &mut CacheDB, address: &Address) -> Option { 33 | cached_accounts.remove(address) 34 | } 35 | 36 | pub fn is_account_cached(cached_accounts: &CacheDB, address: &Address) -> bool { 37 | cached_accounts.contains_key(address) 38 | } 39 | -------------------------------------------------------------------------------- /crates/vm/levm/src/db/error.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error, Serialize, Deserialize)] 4 | pub enum DatabaseError { 5 | #[error("{0}")] 6 | Custom(String), 7 | } 8 | -------------------------------------------------------------------------------- /crates/vm/levm/src/db/mod.rs: -------------------------------------------------------------------------------- 1 | use bytes::Bytes; 2 | use error::DatabaseError; 3 | use ethrex_common::{ 4 | types::{Account, ChainConfig}, 5 | Address, H256, U256, 6 | }; 7 | 8 | pub mod cache; 9 | pub use cache::CacheDB; 10 | pub mod error; 11 | pub mod gen_db; 12 | 13 | pub trait Database: Send + Sync { 14 | fn get_account(&self, address: Address) -> Result; 15 | fn get_storage_value(&self, address: Address, key: H256) -> Result; 16 | fn get_block_hash(&self, block_number: u64) -> Result; 17 | fn account_exists(&self, address: Address) -> Result; 18 | fn get_chain_config(&self) -> Result; 19 | fn get_account_code(&self, code_hash: H256) -> Result; 20 | } 21 | -------------------------------------------------------------------------------- /crates/vm/levm/src/hooks/hook.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | errors::{ExecutionReport, VMError}, 3 | vm::VM, 4 | }; 5 | 6 | pub trait Hook { 7 | fn prepare_execution(&self, vm: &mut VM<'_>) -> Result<(), VMError>; 8 | 9 | fn finalize_execution( 10 | &self, 11 | vm: &mut VM<'_>, 12 | report: &mut ExecutionReport, 13 | ) -> Result<(), VMError>; 14 | } 15 | -------------------------------------------------------------------------------- /crates/vm/levm/src/hooks/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod default_hook; 2 | pub mod hook; 3 | #[cfg(feature = "l2")] 4 | pub mod l2_hook; 5 | 6 | pub use default_hook::DefaultHook; 7 | #[cfg(feature = "l2")] 8 | pub use l2_hook::L2Hook; 9 | -------------------------------------------------------------------------------- /crates/vm/levm/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod call_frame; 2 | pub mod constants; 3 | pub mod db; 4 | pub mod environment; 5 | pub mod errors; 6 | pub mod execution_handlers; 7 | pub mod gas_cost; 8 | pub mod hooks; 9 | pub mod memory; 10 | pub mod opcode_handlers; 11 | pub mod opcodes; 12 | pub mod precompiles; 13 | pub mod tracing; 14 | pub mod utils; 15 | pub mod vm; 16 | pub use environment::*; 17 | -------------------------------------------------------------------------------- /crates/vm/levm/src/opcode_handlers/dup.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | errors::{OpcodeResult, VMError}, 3 | gas_cost, 4 | vm::VM, 5 | }; 6 | 7 | // Duplication Operation (16) 8 | // Opcodes: DUP1 ... DUP16 9 | 10 | impl<'a> VM<'a> { 11 | // DUP operation 12 | pub fn op_dup(&mut self, depth: usize) -> Result { 13 | let current_call_frame = self.current_call_frame_mut()?; 14 | // Increase the consumed gas 15 | current_call_frame.increase_consumed_gas(gas_cost::DUPN)?; 16 | 17 | // Ensure the stack has enough elements to duplicate 18 | if current_call_frame.stack.len() < depth { 19 | return Err(VMError::StackUnderflow); 20 | } 21 | 22 | // Get the value at the specified depth 23 | let value_at_depth = current_call_frame.stack.get( 24 | current_call_frame 25 | .stack 26 | .len() 27 | .checked_sub(depth) 28 | .ok_or(VMError::StackUnderflow)?, 29 | )?; 30 | 31 | // Push the duplicated value onto the stack 32 | current_call_frame.stack.push(*value_at_depth)?; 33 | 34 | Ok(OpcodeResult::Continue { pc_increment: 1 }) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /crates/vm/levm/src/opcode_handlers/exchange.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | errors::{OpcodeResult, VMError}, 3 | gas_cost, 4 | vm::VM, 5 | }; 6 | 7 | // Exchange Operations (16) 8 | // Opcodes: SWAP1 ... SWAP16 9 | 10 | impl<'a> VM<'a> { 11 | // SWAP operation 12 | pub fn op_swap(&mut self, depth: usize) -> Result { 13 | let current_call_frame = self.current_call_frame_mut()?; 14 | current_call_frame.increase_consumed_gas(gas_cost::SWAPN)?; 15 | 16 | let stack_top_index = current_call_frame 17 | .stack 18 | .len() 19 | .checked_sub(1) 20 | .ok_or(VMError::StackUnderflow)?; 21 | 22 | if current_call_frame.stack.len() < depth { 23 | return Err(VMError::StackUnderflow); 24 | } 25 | let to_swap_index = stack_top_index 26 | .checked_sub(depth) 27 | .ok_or(VMError::StackUnderflow)?; 28 | current_call_frame 29 | .stack 30 | .swap(stack_top_index, to_swap_index)?; 31 | 32 | Ok(OpcodeResult::Continue { pc_increment: 1 }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /crates/vm/levm/src/opcode_handlers/keccak.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | errors::{OpcodeResult, VMError}, 3 | gas_cost, 4 | memory::{self, calculate_memory_size}, 5 | vm::VM, 6 | }; 7 | use ethrex_common::U256; 8 | use sha3::{Digest, Keccak256}; 9 | 10 | // KECCAK256 (1) 11 | // Opcodes: KECCAK256 12 | 13 | impl<'a> VM<'a> { 14 | pub fn op_keccak256(&mut self) -> Result { 15 | let current_call_frame = self.current_call_frame_mut()?; 16 | let offset = current_call_frame.stack.pop()?; 17 | let size: usize = current_call_frame 18 | .stack 19 | .pop()? 20 | .try_into() 21 | .map_err(|_| VMError::VeryLargeNumber)?; 22 | 23 | let new_memory_size = calculate_memory_size(offset, size)?; 24 | 25 | current_call_frame.increase_consumed_gas(gas_cost::keccak256( 26 | new_memory_size, 27 | current_call_frame.memory.len(), 28 | size, 29 | )?)?; 30 | 31 | let mut hasher = Keccak256::new(); 32 | hasher.update(memory::load_range( 33 | &mut current_call_frame.memory, 34 | offset, 35 | size, 36 | )?); 37 | current_call_frame 38 | .stack 39 | .push(U256::from_big_endian(&hasher.finalize()))?; 40 | 41 | Ok(OpcodeResult::Continue { pc_increment: 1 }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /crates/vm/levm/src/opcode_handlers/logging.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | errors::{OpcodeResult, VMError}, 3 | gas_cost, 4 | memory::{self, calculate_memory_size}, 5 | vm::VM, 6 | }; 7 | use bytes::Bytes; 8 | use ethrex_common::{types::Log, H256}; 9 | 10 | // Logging Operations (5) 11 | // Opcodes: LOG0 ... LOG4 12 | 13 | impl<'a> VM<'a> { 14 | // LOG operation 15 | pub fn op_log(&mut self, number_of_topics: u8) -> Result { 16 | let current_call_frame = self.current_call_frame_mut()?; 17 | if current_call_frame.is_static { 18 | return Err(VMError::OpcodeNotAllowedInStaticContext); 19 | } 20 | 21 | let offset = current_call_frame.stack.pop()?; 22 | let size = current_call_frame 23 | .stack 24 | .pop()? 25 | .try_into() 26 | .map_err(|_| VMError::VeryLargeNumber)?; 27 | let mut topics = Vec::new(); 28 | for _ in 0..number_of_topics { 29 | let topic = current_call_frame.stack.pop()?; 30 | topics.push(H256::from_slice(&topic.to_big_endian())); 31 | } 32 | 33 | let new_memory_size = calculate_memory_size(offset, size)?; 34 | 35 | current_call_frame.increase_consumed_gas(gas_cost::log( 36 | new_memory_size, 37 | current_call_frame.memory.len(), 38 | size, 39 | number_of_topics, 40 | )?)?; 41 | 42 | let log = Log { 43 | address: current_call_frame.to, 44 | topics, 45 | data: Bytes::from( 46 | memory::load_range(&mut current_call_frame.memory, offset, size)?.to_vec(), 47 | ), 48 | }; 49 | 50 | self.tracer.log(&log)?; 51 | 52 | self.current_call_frame_mut()?.logs.push(log); 53 | 54 | Ok(OpcodeResult::Continue { pc_increment: 1 }) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /crates/vm/levm/src/opcode_handlers/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod arithmetic; 2 | pub mod bitwise_comparison; 3 | pub mod block; 4 | pub mod dup; 5 | pub mod environment; 6 | pub mod exchange; 7 | pub mod keccak; 8 | pub mod logging; 9 | pub mod push; 10 | pub mod stack_memory_storage_flow; 11 | pub mod system; 12 | -------------------------------------------------------------------------------- /crates/vm/levm/tests/lib.rs: -------------------------------------------------------------------------------- 1 | mod tests; 2 | -------------------------------------------------------------------------------- /crates/vm/lib.rs: -------------------------------------------------------------------------------- 1 | mod constants; 2 | mod db; 3 | mod errors; 4 | mod execution_result; 5 | mod helpers; 6 | mod prover_db; 7 | pub mod tracing; 8 | 9 | pub mod backends; 10 | 11 | pub use backends::{BlockExecutionResult, Evm, EvmEngine}; 12 | pub use db::{DynVmDatabase, VmDatabase}; 13 | pub use errors::{EvmError, ProverDBError}; 14 | pub use execution_result::ExecutionResult; 15 | pub use helpers::{create_contract_address, fork_to_spec_id, SpecId}; 16 | pub use prover_db::ProverDB; 17 | -------------------------------------------------------------------------------- /crates/vm/tracing.rs: -------------------------------------------------------------------------------- 1 | use ethrex_common::tracing::CallTrace; 2 | use ethrex_common::types::Block; 3 | 4 | use crate::backends::levm::LEVM; 5 | use crate::{backends::revm::REVM, Evm, EvmError}; 6 | 7 | impl Evm { 8 | /// Runs a single tx with the call tracer and outputs its trace 9 | /// Asumes that the received state already contains changes from previous blocks and other 10 | /// transactions within its block 11 | /// Wraps [REVM::trace_tx_calls], does not currenlty have levm support. 12 | pub fn trace_tx_calls( 13 | &mut self, 14 | block: &Block, 15 | tx_index: usize, 16 | only_top_call: bool, 17 | with_log: bool, 18 | ) -> Result { 19 | let tx = block 20 | .body 21 | .transactions 22 | .get(tx_index) 23 | .ok_or(EvmError::Custom( 24 | "Missing Transaction for Trace".to_string(), 25 | ))?; 26 | 27 | match self { 28 | Evm::REVM { state } => { 29 | REVM::trace_tx_calls(&block.header, tx, state, only_top_call, with_log) 30 | } 31 | Evm::LEVM { db } => { 32 | LEVM::trace_tx_calls(db, &block.header, tx, only_top_call, with_log) 33 | } 34 | } 35 | } 36 | 37 | /// Reruns the given block, saving the changes on the state, doesn't output any results or receipts 38 | /// If the optional argument `stop_index` is set, the run will stop just before executing the transaction at that index 39 | /// and won't process the withdrawals afterwards 40 | /// Wraps [REVM::rerun_block], does not currenlty have levm support. 41 | pub fn rerun_block( 42 | &mut self, 43 | block: &Block, 44 | stop_index: Option, 45 | ) -> Result<(), EvmError> { 46 | match self { 47 | Evm::REVM { state } => REVM::rerun_block(block, state, stop_index), 48 | Evm::LEVM { db } => LEVM::rerun_block(db, block, stop_index), 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.82.0" 3 | profile = "default" 4 | -------------------------------------------------------------------------------- /test_data/2000-blocks.rlp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:98dc6d21b2f2baa36b9be9999c5df8207307ea06d510dbabfa720abf0e4ad6dd 3 | size 67422766 4 | -------------------------------------------------------------------------------- /test_data/ERC20/ERC20.sol: -------------------------------------------------------------------------------- 1 | import "./deps.sol"; 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | contract TestToken is ERC20 { 6 | 7 | uint256 constant defaultMint = 1000000 * (10**18); 8 | 9 | constructor() ERC20("TestToken", "TEST") { 10 | _mint(msg.sender, defaultMint); 11 | } 12 | 13 | // Mint a free amount for whoever 14 | // calls the function 15 | function freeMint() public { 16 | _mint(msg.sender, defaultMint); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test_data/blobs/1-1.blob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/test_data/blobs/1-1.blob -------------------------------------------------------------------------------- /test_data/blobs/2-1.blob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/test_data/blobs/2-1.blob -------------------------------------------------------------------------------- /test_data/blobs/3-1.blob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/test_data/blobs/3-1.blob -------------------------------------------------------------------------------- /test_data/blobs/4-1.blob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/test_data/blobs/4-1.blob -------------------------------------------------------------------------------- /test_data/blobs/5-1.blob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/test_data/blobs/5-1.blob -------------------------------------------------------------------------------- /test_data/chain.rlp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/test_data/chain.rlp -------------------------------------------------------------------------------- /test_data/hive_clients.yml: -------------------------------------------------------------------------------- 1 | - client: ethrex 2 | build_args: 3 | baseimage: ethrex 4 | -------------------------------------------------------------------------------- /test_data/l2-1k-erc20.rlp: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b1856840d8e0e90007134a61e60d1788ac6e5bc607c418b3862938c4ebf08ba3 3 | size 288667496 4 | -------------------------------------------------------------------------------- /test_data/l2-loadtest.rlp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/test_data/l2-loadtest.rlp -------------------------------------------------------------------------------- /test_data/load_test/IOHeavyContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: UNLICENSED 2 | pragma solidity ^0.8.13; 3 | 4 | // Contract used in the `load-test-io` Makefile target. The test sends transactions calling 5 | // the `incrementNumbers()` function, which writes to 100 storage slots. 6 | 7 | contract Counter { 8 | uint256[100] public number; 9 | 10 | constructor() { 11 | for(uint i = 0; i < 100; i++) { 12 | number[i] = i; 13 | } 14 | } 15 | 16 | function incrementNumbers() public { 17 | for(uint i = 0; i < 100; i++) { 18 | number[i] = number[i] + 1; 19 | } 20 | } 21 | 22 | function getFirstNumber() public view returns(uint256) { 23 | return number[0]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test_data/network_params.yaml: -------------------------------------------------------------------------------- 1 | participants: 2 | # NOTE: Both erigon and geth work on this example, but they provide wrong nodes information on discovery protocol 3 | # - el_type: erigon 4 | # el_image: ethpandaops/erigon:main-764a2c50 5 | # cl_type: lighthouse 6 | # cl_image: sigp/lighthouse:v7.0.0-beta.0 7 | # validator_count: 32 8 | # - el_type: reth 9 | # el_image: ghcr.io/paradigmxyz/reth:v1.2.2 10 | # cl_type: lighthouse 11 | # cl_image: sigp/lighthouse:v7.0.0-beta.0 12 | # validator_count: 32 13 | - el_type: besu 14 | el_image: ethpandaops/besu:main-142a5e6 15 | cl_type: lighthouse 16 | cl_image: sigp/lighthouse:v7.0.0-beta.0 17 | validator_count: 32 18 | - el_type: geth 19 | el_image: ethereum/client-go:v1.15.2 20 | cl_type: lighthouse 21 | cl_image: sigp/lighthouse:v7.0.0-beta.0 22 | validator_count: 32 23 | count: 1 24 | - el_type: ethrex 25 | cl_type: lighthouse 26 | cl_image: sigp/lighthouse:v7.0.0-beta.0 27 | validator_count: 32 28 | 29 | network_params: 30 | electra_fork_epoch: 1 31 | 32 | # The address of the staking contract address on the Eth1 chain 33 | deposit_contract_address: "0x4242424242424242424242424242424242424242" 34 | 35 | ethereum_metrics_exporter_enabled: true 36 | 37 | additional_services: 38 | - dora 39 | - forkmon 40 | - tx_fuzz 41 | - prometheus_grafana 42 | 43 | tx_fuzz_params: 44 | # A list of optional extra params that will be passed to the TX Fuzz container for modifying its behaviour 45 | tx_fuzz_extra_args: ["--txcount=3", "--accounts=80"] 46 | # Some tested seeds: 0x5a8e7b08fef94497, 0x6619e189b8a8b911, 0x52a0d7198393262e, use it as an extra argument for the tx_fuzz, i.e: "--seed=0x5a8e7b08fef94497" 47 | 48 | blockscout_params: 49 | image: "blockscout/blockscout:latest" 50 | verif_image: "ghcr.io/blockscout/smart-contract-verifier:latest" 51 | frontend_image: "ghcr.io/blockscout/frontend:latest" 52 | 53 | prometheus_params: 54 | # TODO: switch to latest when it points to v3.x 55 | image: "prom/prometheus:v3.2.1" 56 | -------------------------------------------------------------------------------- /test_data/rsp/input/1/21272632.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdaclass/ethrex/5a1aa0f02d7e00664cd0d08e8f417fca619b8857/test_data/rsp/input/1/21272632.bin -------------------------------------------------------------------------------- /tooling/genesis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "genesis-tool" 3 | edition.workspace = true 4 | version.workspace = true 5 | 6 | [dependencies] 7 | ethrex-common.workspace = true 8 | serde_json = { workspace = true, features = [ "preserve_order" ] } 9 | 10 | [lib] 11 | name = "genesis_tool" 12 | path = "./src/lib.rs" 13 | 14 | [[bin]] 15 | name = "genesis-sorter" 16 | path = "./src/genesis.rs" -------------------------------------------------------------------------------- /tooling/genesis/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod genesis; 2 | -------------------------------------------------------------------------------- /tooling/hive_report/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hive_report" 3 | edition.workspace = true 4 | version.workspace = true 5 | 6 | [dependencies] 7 | serde.workspace = true 8 | serde_json.workspace = true -------------------------------------------------------------------------------- /tooling/load_test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "load_test" 3 | edition.workspace = true 4 | version.workspace = true 5 | 6 | [dependencies] 7 | clap.workspace = true 8 | hex.workspace = true 9 | secp256k1.workspace = true 10 | ethereum-types.workspace = true 11 | ethrex-rpc.workspace = true 12 | ethrex-sdk.workspace = true 13 | ethrex-blockchain.workspace = true 14 | ethrex-common.workspace = true 15 | eyre.workspace = true 16 | keccak-hash.workspace = true 17 | tokio = { workspace = true, features = ["full"] } 18 | futures = "0.3" 19 | -------------------------------------------------------------------------------- /tooling/loc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "loc" 3 | edition.workspace = true 4 | version.workspace = true 5 | 6 | [dependencies] 7 | tokei = "12.1.2" 8 | serde.workspace = true 9 | serde_json.workspace = true 10 | clap.workspace = true 11 | clap_complete.workspace = true 12 | colored = "2.1.0" 13 | spinoff = "0.8.0" 14 | prettytable = "0.10.0" -------------------------------------------------------------------------------- /tooling/loc/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: loc loc-stats loc-detailed loc-compare-detailed 2 | 3 | loc: 4 | cargo run 5 | 6 | loc-stats: 7 | if [ "$(QUIET)" = "true" ]; then \ 8 | cargo run --quiet -- --summary;\ 9 | else \ 10 | cargo run -- --summary;\ 11 | fi 12 | 13 | loc-detailed: 14 | cargo run -- --detailed 15 | 16 | loc-compare-detailed: 17 | cargo run -- --compare-detailed 18 | --------------------------------------------------------------------------------