From 7ff62e4606958f550f5c5577f4fe2d52dd744bab Mon Sep 17 00:00:00 2001 From: James Date: Wed, 17 Jul 2024 15:14:50 -0400 Subject: [PATCH] time is a tree(this life one leaf) --- .github/CODEOWNERS | 1 + .github/dependabot.yml | 6 + .github/workflows/rust-ci.yml | 14 + .gitignore | 1 + Cargo.lock | 3420 +++++++++++++++++++++++++++++++++ Cargo.toml | 91 + LICENSE-APACHE | 176 ++ LICENSE-MIT | 23 + README.md | 54 + assets/states.png | Bin 0 -> 151164 bytes clippy.toml | 1 + examples/basic_transact.rs | 71 + examples/fork_ref_transact.rs | 86 + rustfmt.toml | 3 + src/evm.rs | 1148 +++++++++++ src/fill/alloy.rs | 323 ++++ src/fill/mod.rs | 9 + src/fill/noop.rs | 19 + src/fill/traits.rs | 121 ++ src/fill/zenith.rs | 43 + src/lib.rs | 443 +++++ src/lifecycle/contexts.rs | 790 ++++++++ src/lifecycle/mod.rs | 11 + src/lifecycle/output.rs | 75 + src/lifecycle/postflight.rs | 84 + src/lifecycle/trait.rs | 110 ++ src/states.rs | 378 ++++ src/syscall/eip4788.rs | 37 + src/syscall/eip6110.rs | 83 + src/syscall/eip7002.rs | 37 + src/syscall/eip7251.rs | 37 + src/syscall/fill.rs | 83 + src/syscall/mod.rs | 102 + src/test_utils.rs | 113 ++ 34 files changed, 7993 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/rust-ci.yml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 LICENSE-APACHE create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 assets/states.png create mode 100644 clippy.toml create mode 100644 examples/basic_transact.rs create mode 100644 examples/fork_ref_transact.rs create mode 100644 rustfmt.toml create mode 100644 src/evm.rs create mode 100644 src/fill/alloy.rs create mode 100644 src/fill/mod.rs create mode 100644 src/fill/noop.rs create mode 100644 src/fill/traits.rs create mode 100644 src/fill/zenith.rs create mode 100644 src/lib.rs create mode 100644 src/lifecycle/contexts.rs create mode 100644 src/lifecycle/mod.rs create mode 100644 src/lifecycle/output.rs create mode 100644 src/lifecycle/postflight.rs create mode 100644 src/lifecycle/trait.rs create mode 100644 src/states.rs create mode 100644 src/syscall/eip4788.rs create mode 100644 src/syscall/eip6110.rs create mode 100644 src/syscall/eip7002.rs create mode 100644 src/syscall/eip7251.rs create mode 100644 src/syscall/fill.rs create mode 100644 src/syscall/mod.rs create mode 100644 src/test_utils.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..2d63675 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +@prestwich @evalir \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..e8faa4d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: cargo + directory: "/" + schedule: + interval: "weekly" \ No newline at end of file diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml new file mode 100644 index 0000000..06a8226 --- /dev/null +++ b/.github/workflows/rust-ci.yml @@ -0,0 +1,14 @@ +name: Rust CI + +on: + workflow_dispatch: + push: + branches: [main] + pull_request: + +env: + CARGO_TERM_COLOR: always + +jobs: + rust-base: + uses: init4tech/actions/.github/workflows/rust-base.yml@main \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..618a18d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3420 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "alloy-chains" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1752d7d62e2665da650a36d84abbf239f812534475d51f072a49a533513b7cdd" +dependencies = [ + "num_enum", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da374e868f54c7f4ad2ad56829827badca388efd645f8cf5fccc61c2b5343504" +dependencies = [ + "alloy-eips 0.1.4", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.1.4", + "c-kzg", + "serde", +] + +[[package]] +name = "alloy-consensus" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58047cc851e58c26224521d1ecda466e3d746ebca0274cd5427aa660a88c353" +dependencies = [ + "alloy-eips 0.2.0", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.2.0", + "c-kzg", + "serde", +] + +[[package]] +name = "alloy-contract" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dc6957ff706f9e5f6fd42f52a93e4bce476b726c92d077b348de28c4a76730c" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-network 0.1.4", + "alloy-primitives", + "alloy-provider 0.1.4", + "alloy-rpc-types-eth 0.1.4", + "alloy-sol-types", + "alloy-transport 0.1.4", + "futures", + "futures-util", + "thiserror", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413902aa18a97569e60f679c23f46a18db1656d87ab4d4e49d0e1e52042f66df" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow 0.6.15", +] + +[[package]] +name = "alloy-eips" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76ecab54890cdea1e4808fc0891c7e6cfcf71fe1a9fe26810c7280ef768f4ed" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.1.4", + "c-kzg", + "derive_more", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-eips" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32a3e14fa0d152d00bd8daf605eb74ad397efb0f54bd7155585823dddb4401e" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.2.0", + "c-kzg", + "k256", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-json-abi" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc05b04ac331a9f07e3a4036ef7926e49a8bf84a99a1ccfc7e2ab55a5fcbb372" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d6f34930b7e3e2744bcc79056c217f00cb2abb33bc5d4ff88da7623c5bb078b" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a9feec2352c78545d1a37415699817bae8dc41654bd1bfe57d6cdd5433bd" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25f6895fc31b48fa12306ef9b4f78b7764f8bd6d7d91cdb0a40e233704a0f23f" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-eips 0.1.4", + "alloy-json-rpc 0.1.4", + "alloy-primitives", + "alloy-rpc-types-eth 0.1.4", + "alloy-serde 0.1.4", + "alloy-signer 0.1.4", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + +[[package]] +name = "alloy-network" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3223d71dc78f464b2743418d0be8b5c894313e272105a6206ad5e867d67b3ce2" +dependencies = [ + "alloy-consensus 0.2.0", + "alloy-eips 0.2.0", + "alloy-json-rpc 0.2.0", + "alloy-primitives", + "alloy-rpc-types-eth 0.2.0", + "alloy-serde 0.2.0", + "alloy-signer 0.2.0", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + +[[package]] +name = "alloy-primitives" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb3ead547f4532bc8af961649942f0b9c16ee9226e26caa3f38420651cc0bf4" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "k256", + "keccak-asm", + "proptest", + "rand", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-provider" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c538bfa893d07e27cb4f3c1ab5f451592b7c526d511d62b576a2ce59e146e4a" +dependencies = [ + "alloy-chains", + "alloy-consensus 0.1.4", + "alloy-eips 0.1.4", + "alloy-json-rpc 0.1.4", + "alloy-network 0.1.4", + "alloy-primitives", + "alloy-rpc-client 0.1.4", + "alloy-rpc-types-eth 0.1.4", + "alloy-transport 0.1.4", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "serde", + "serde_json", + "tokio", + "tracing", +] + +[[package]] +name = "alloy-provider" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29da7457d853cb8199ec04b227d5d2ef598be3e59fc2bbad70c8be213292f32" +dependencies = [ + "alloy-chains", + "alloy-consensus 0.2.0", + "alloy-eips 0.2.0", + "alloy-json-rpc 0.2.0", + "alloy-network 0.2.0", + "alloy-primitives", + "alloy-rpc-client 0.2.0", + "alloy-rpc-types-eth 0.2.0", + "alloy-transport 0.2.0", + "alloy-transport-http 0.2.0", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a43b18702501396fa9bcdeecd533bc85fac75150d308fc0f6800a01e6234a003" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d83524c1f6162fcb5b0decf775498a125066c86dda6066ed609531b0e912f85a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba31bae67773fd5a60020bea900231f8396202b7feca4d0c70c6b59308ab4a8" +dependencies = [ + "alloy-json-rpc 0.1.4", + "alloy-transport 0.1.4", + "alloy-transport-http 0.1.4", + "futures", + "pin-project", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8a9e609524fa31c2c70eb24c0da60796809193ad4787a6dfe6d0db0d3ac112d" +dependencies = [ + "alloy-json-rpc 0.2.0", + "alloy-transport 0.2.0", + "alloy-transport-http 0.2.0", + "futures", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4123ee21f99ba4bd31bfa36ba89112a18a500f8b452f02b35708b1b951e2b9" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-eips 0.1.4", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.1.4", + "alloy-sol-types", + "itertools 0.13.0", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "605fa8462732bb8fd0645a9941e12961e079d45ae6a44634c826f8229c187bdf" +dependencies = [ + "alloy-consensus 0.2.0", + "alloy-eips 0.2.0", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.2.0", + "alloy-sol-types", + "itertools 0.13.0", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-serde" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9416c52959e66ead795a11f4a86c248410e9e368a0765710e57055b8a1774dd6" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c5b9057acc02aee1b8aac2b5a0729cb0f73d080082c111313e5d1f92a96630" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33753c09fa1ad85e5b092b8dc2372f1e337a42e84b9b4cff9fede75ba4adb32" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + +[[package]] +name = "alloy-signer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37f10592696f4ab8b687d5a8ab55e998a14ea0ca5f8eb20ad74a96ad671bb54a" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + +[[package]] +name = "alloy-signer-local" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfc9c26fe6c6f1bad818c9a976de9044dd12e1f75f1f156a801ee3e8148c1b6" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-network 0.1.4", + "alloy-primitives", + "alloy-signer 0.1.4", + "async-trait", + "k256", + "rand", + "thiserror", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b40397ddcdcc266f59f959770f601ce1280e699a91fc1862f29cef91707cd09" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "867a5469d61480fea08c7333ffeca52d5b621f5ca2e44f271b117ec1fc9a0525" +dependencies = [ + "alloy-json-abi", + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.72", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e482dc33a32b6fadbc0f599adea520bd3aaa585c141a80b404d0a3e3fa72528" +dependencies = [ + "alloy-json-abi", + "const-hex", + "dunce", + "heck", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.72", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" +dependencies = [ + "serde", + "winnow 0.6.15", +] + +[[package]] +name = "alloy-sol-types" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a91ca40fa20793ae9c3841b83e74569d1cc9af29a2f5237314fd3452d51e38c7" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b51a291f949f755e6165c3ed562883175c97423703703355f4faa4b7d0a57c" +dependencies = [ + "alloy-json-rpc 0.1.4", + "base64", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b44b0f6f4a2593b258fa7b6cae8968e6a4c404d9ef4f5bc74401f2d04fa23fa" +dependencies = [ + "alloy-json-rpc 0.2.0", + "base64", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d65871f9f1cafe1ed25cde2f1303be83e6473e995a2d56c275ae4fcce6119c" +dependencies = [ + "alloy-transport 0.1.4", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d8f1eefa8cb9e7550740ee330feba4fed303a77ad3085707546f9152a88c380" +dependencies = [ + "alloy-json-rpc 0.2.0", + "alloy-transport 0.2.0", + "reqwest", + "serde_json", + "tower", + "tracing", + "url", +] + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.0", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "async-trait" +version = "0.1.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "aurora-engine-modexp" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aef7712851e524f35fbbb74fa6599c5cd8692056a1c36f9ca0d2001b670e7e5" +dependencies = [ + "hex", + "num", +] + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "serde", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bls12_381" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc6d6292be3a19e6379786dac800f551e5865a5bb51ebbe3064ab80433f403" +dependencies = [ + "ff", + "group", + "pairing", + "rand_core", + "subtle", +] + +[[package]] +name = "blst" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62dc83a094a71d43eeadd254b1ec2d24cb6a0bb6cadce00df51f0db594711a32" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +dependencies = [ + "serde", +] + +[[package]] +name = "c-kzg" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf100c4cea8f207e883ff91ca886d621d8a166cb04971dfaa9bb8fd99ed95df" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "serde", +] + +[[package]] +name = "cc" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn 2.0.72", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enumn" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "bitvec", + "rand_core", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", + "serde", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + +[[package]] +name = "keccak-asm" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a3633291834c4fbebf8673acbc1b04ec9d151418ff9b8e26dcd79129928758" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "kzg-rs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9920cd4460ce3cbca19c62f3bb9a9611562478a4dc9d2c556f4a7d049c5b6b" +dependencies = [ + "bls12_381", + "glob", + "hex", + "once_cell", + "serde", + "serde_derive", + "serde_yaml", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "object" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group", +] + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd53dff83f26735fdc1ca837098ccf133605d794cdae66acfc2bfac3ec809d95" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "reqwest" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + +[[package]] +name = "revm" +version = "12.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6cfb48bce8ca2113e157bdbddbd5eeb09daac1c903d79ec17085897c38c7c91" +dependencies = [ + "alloy-eips 0.2.0", + "alloy-provider 0.2.0", + "alloy-transport 0.2.0", + "auto_impl", + "cfg-if", + "dyn-clone", + "revm-interpreter", + "revm-precompile", + "serde", + "serde_json", + "tokio", +] + +[[package]] +name = "revm-interpreter" +version = "8.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b0daddea06fc6da5346acc39b32a357bbe3579e9e3d94117d9ae125cd596fc" +dependencies = [ + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-precompile" +version = "9.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef55228211251d7b6c7707c3ee13bb70dea4d2fd81ec4034521e4fe31010b2ea" +dependencies = [ + "aurora-engine-modexp", + "blst", + "c-kzg", + "cfg-if", + "k256", + "once_cell", + "revm-primitives", + "ripemd", + "secp256k1", + "sha2", + "substrate-bn", +] + +[[package]] +name = "revm-primitives" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc4311037ee093ec50ec734e1424fcb3e12d535c6cef683b75d1c064639630c" +dependencies = [ + "alloy-eips 0.2.0", + "alloy-primitives", + "auto_impl", + "bitflags", + "bitvec", + "c-kzg", + "cfg-if", + "derive_more", + "dyn-clone", + "enumn", + "hashbrown", + "hex", + "kzg-rs", + "once_cell", + "serde", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.23", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "secp256k1" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0cc0f1cf93f4969faf3ea1c7d8a9faed25918d96affa959720823dfe86d4f3" +dependencies = [ + "rand", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1433bd67156263443f14d603720b082dd3121779323fce20cba2aa07b874bc1b" +dependencies = [ + "cc", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "serde_json" +version = "1.0.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3-asm" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9b57fd861253bff08bb1919e995f90ba8f4889de2726091c8876f3a4e823b40" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.72", +] + +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand", + "rustc-hex", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c837dc8852cb7074e46b444afb81783140dab12c58867b49fb3898fbafedf7ea" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "trevm" +version = "0.1.0" +dependencies = [ + "alloy-consensus 0.2.0", + "alloy-eips 0.2.0", + "alloy-primitives", + "alloy-provider 0.2.0", + "alloy-rlp", + "alloy-signer 0.1.4", + "alloy-signer-local", + "alloy-sol-types", + "alloy-transport 0.2.0", + "eyre", + "revm", + "serde_json", + "tokio", + "zenith-types", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.72", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557404e450152cd6795bb558bca69e43c585055f4606e3bcae5894fc6dac9ba0" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zenith-types" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631c66a913e55b2b73b08e7271d5c8bea52464040b8f9e38ae183ca2aad79676" +dependencies = [ + "alloy-consensus 0.1.4", + "alloy-contract", + "alloy-eips 0.1.4", + "alloy-primitives", + "alloy-rlp", + "alloy-signer 0.1.4", + "alloy-sol-types", + "serde", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..4bff12e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,91 @@ +[package] +name = "trevm" +version = "0.1.0" +rust-version = "1.79.0" +edition = "2021" +authors = ["init4"] +homepage = "https://github.com/init4tech/trevm" +repository = "https://github.com/init4tech/trevm" +license = "MIT OR Apache-2.0" +description = "A typestate API wrapper for the revm EVM implementation" + +[lints.rust] +missing-debug-implementations = "warn" +missing-docs = "warn" +unreachable-pub = "warn" +unused-must-use = "deny" +rust-2018-idioms = "deny" +unnameable-types = "warn" + +[lints.rustdoc] +all = "warn" + +[lints.clippy] +missing-const-for-fn = "warn" +use-self = "warn" +option-if-let-else = "warn" +redundant-clone = "warn" + +[dependencies] +alloy-consensus = { version = "0.2", features = ["k256"] } +alloy-eips = "0.2.0" +alloy-primitives = "0.7.6" +alloy-sol-types = "0.7.7" +revm = { version = "12.0.0", default-features = false, features = ["std"] } +zenith-types = "0.2.2" + +[dev-dependencies] +revm = { version = "12.0.0", features = ["test-utils", "serde-json", "std", "alloydb"] } +tokio = { version = "1.28", features = ["macros", "rt-multi-thread"] } + +# alloydb +alloy-provider = "0.2" +alloy-transport = "0.2" + +alloy-signer = { version = "0.1", default-features = false } +alloy-signer-local = { version = "0.1", default-features = false } + +alloy-rlp = { version = "0.3", default-features = false } + + +serde_json = { version = "1.0", default-features = false, features = ["alloc"] } + + +# misc +eyre = "0.6" + +[features] +default = [ + "revm/std", + "revm/c-kzg", + "revm/blst", + "revm/portable", + "revm/secp256k1" +] + +test-utils = ["revm/test-utils"] + + +secp256k1 = ["revm/secp256k1"] +c-kzg = ["revm/c-kzg"] +blst = ["revm/blst"] + +portable = ["revm/portable"] + +dev = [ + "memory_limit", + "optional_balance_check", + "optional_block_gas_limit", + "optional_eip3607", + "optional_gas_refund", + "optional_no_base_fee", + "optional_beneficiary_reward", +] + +memory_limit = ["revm/memory_limit"] +optional_balance_check = ["revm/optional_balance_check"] +optional_beneficiary_reward = ["revm/optional_beneficiary_reward"] +optional_block_gas_limit = ["revm/optional_block_gas_limit"] +optional_eip3607 = ["revm/optional_eip3607"] +optional_gas_refund = ["revm/optional_gas_refund"] +optional_no_base_fee = ["revm/optional_no_base_fee"] diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..1b5ec8b --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,23 @@ +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..810a643 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +## Trevm + +⚠️ Trevm is **experimental** software and not considered ready for production +use. ⚠️ + +Trevm is a [typestate] API wrapper for [revm]. It provides an ergonomic way to +interact with the revm API, shortcuts for common tasks, and straightforward API +extensibility. Trevm does NOT try to provide low-level EVM management, but +rather to shortcut and simplify common tasks like simulating transactions. + +Trevm is NOT a replacement for revm. It is a wrapper around the revm API. It is +NOT a drop-in upgrade, integrating trevm into your project will require changes +to your code. + +See the [documentation on docs.rs] for information on usage. + +## Why use Trevm? + +Trevm is for building complex high-level flows on top of the low-level revm +API. It provides a state machine that ensures you can only take actions that are +valid in the current state. This makes it easier to reason about the state of +your EVM instance and ensures you don't make mistakes. + +Trevm is useful for: + +- full node implementations +- block builders +- searchers +- any other transaction simulation usecase + +## Limitations + +Trevm is a work in progress and is not feature complete. In particular, trevm +does not currently have lifecycle support for blocks before Shanghai. This means +it cannot produce correct post-block states for blocks before Shanghai. + +### The Trevm State Machine + +Trevm provides a state machine that represents the internal state of a revm EVM +instance. It ensures that at each step you can only take actions that are valid +in the current state. This is done by using the [typestate] pattern. + +As you progress between states, the API will change to reflect the available +operations. For example, you can't call `open_block()` on a `EvmNeedsTx` state, +as the block is already open. You can't call `close_block()` on a `EvmReady` +state, without explicitly clearing or running the transaction that has been +ready. + +![typestates are cool](./assets/states.png) + +[typestate]: https://cliffle.com/blog/rust-typestate/ +[revm]: https://github.com/bluealloy/revm +[docs.rs]: https://docs.rs/trevm/latest/trevm/ +[documentation on docs.rs]: https://docs.rs/trevm/latest/trevm/ diff --git a/assets/states.png b/assets/states.png new file mode 100644 index 0000000000000000000000000000000000000000..a5278d762cdf3c4eb8fc80e605177baf0a044058 GIT binary patch literal 151164 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 literal 0 HcmV?d00001 diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..fda0d7c --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +too-large-for-stack = 128 diff --git a/examples/basic_transact.rs b/examples/basic_transact.rs new file mode 100644 index 0000000..554c966 --- /dev/null +++ b/examples/basic_transact.rs @@ -0,0 +1,71 @@ +//! Simple TREVM example that demonstrates how to execute a transaction on a contract. +//! It simply loads the contract bytecode and executes a transaction. + +use revm::{ + inspector_handle_register, + inspectors::TracerEip3155, + primitives::{hex, AccountInfo, Address, Bytecode, TransactTo, U256}, + EvmBuilder, InMemoryDB, +}; +use trevm::{trevm_aliases, NoopBlock, NoopCfg, Shanghai, TrevmBuilder, Tx}; + +/// Foundry's default Counter.sol contract bytecode. +const CONTRACT_BYTECODE: &str = "0x6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fb5c1cb1460415780638381f58a146053578063d09de08a14606d575b600080fd5b6051604c3660046083565b600055565b005b605b60005481565b60405190815260200160405180910390f35b6051600080549080607c83609b565b9190505550565b600060208284031215609457600080fd5b5035919050565b60006001820160ba57634e487b7160e01b600052601160045260246000fd5b506001019056fea2646970667358221220091e48831e9eee32d4571d6291233a4fdaaa34b7dced8770f36f5368be825c5264736f6c63430008190033"; + +/// The address of Counter.sol +const CONTRACT_ADDR: Address = Address::with_last_byte(32); + +/// The input data for the Counter.sol program. We're calling setNumber(10) +const PROGRAM_INPUT: &str = + "0x3fb5c1cb000000000000000000000000000000000000000000000000000000000000000a"; + +/// The caller address +const CALLER_ADDR: Address = Address::with_last_byte(1); + +struct SampleTx; + +impl Tx for SampleTx { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + tx_env.caller = CALLER_ADDR; + tx_env.transact_to = TransactTo::Call(CONTRACT_ADDR); + tx_env.data = hex::decode(PROGRAM_INPUT).unwrap().into(); + } +} + +// Produce aliases for the Trevm type +trevm_aliases!(TracerEip3155, InMemoryDB); + +fn main() { + let mut db = revm::InMemoryDB::default(); + + let bytecode = Bytecode::new_raw(hex::decode(CONTRACT_BYTECODE).unwrap().into()); + let acc_info = AccountInfo::new(U256::ZERO, 1, bytecode.hash_slow(), bytecode); + + // insert both the contract code to the contract cache, and the account info to the account cache + db.insert_contract(&mut acc_info.clone()); + db.insert_account_info(CONTRACT_ADDR, acc_info); + + let evm = EvmBuilder::default() + .with_db(db) + .with_external_context(TracerEip3155::new(Box::new(std::io::stdout()))) + .append_handler_register(inspector_handle_register) + .build_trevm() + .fill_cfg(&NoopCfg) + .open_block(&NoopBlock, Shanghai::default()) + .unwrap(); + + let account = evm.read_account_ref(CONTRACT_ADDR).unwrap(); + println!("account: {account:?}"); + + let evm = evm.fill_tx(&SampleTx).run(); + + match evm { + Ok(res) => { + let res = res.result_and_state(); + println!("Execution result: {res:#?}"); + } + Err(e) => { + println!("Execution error: {e:?}"); + } + }; +} diff --git a/examples/fork_ref_transact.rs b/examples/fork_ref_transact.rs new file mode 100644 index 0000000..fa83fc0 --- /dev/null +++ b/examples/fork_ref_transact.rs @@ -0,0 +1,86 @@ +//! This example demonstrates how to query storage slots of a contract, using AlloyDB. + +use alloy_eips::BlockId; +use alloy_primitives::Address; +use alloy_provider::ProviderBuilder; +use alloy_sol_types::sol; +use alloy_sol_types::SolCall; +use revm::{ + db::{AlloyDB, CacheDB}, + primitives::{address, TxKind, U256}, + Evm, +}; +use trevm::{NoopBlock, NoopCfg, Shanghai, TrevmBuilder, Tx}; + +sol! { + #[allow(missing_docs)] + function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); +} + +struct GetReservesFiller; + +impl Tx for GetReservesFiller { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + tx_env.caller = Address::with_last_byte(0); + // ETH/USDT pair on Uniswap V2 + tx_env.transact_to = TxKind::Call(POOL_ADDRESS); + // calldata formed via alloy's abi encoder + tx_env.data = getReservesCall::new(()).abi_encode().into(); + // transaction value in wei + tx_env.value = U256::from(0); + } +} + +const POOL_ADDRESS: Address = address!("0d4a11d5EEaaC28EC3F61d100daF4d40471f1852"); + +#[tokio::main] +async fn main() -> eyre::Result<()> { + // create ethers client and wrap it in Arc + let rpc_url = "https://mainnet.infura.io/v3/c60b0bb42f8a4c6481ecd229eddaca27"; + + let client = ProviderBuilder::new().on_http(rpc_url.parse()?); + + // ----------------------------------------------------------- // + // Storage slots of UniV2Pair contract // + // =========================================================== // + // storage[5] = factory: address // + // storage[6] = token0: address // + // storage[7] = token1: address // + // storage[8] = (res0, res1, ts): (uint112, uint112, uint32) // + // storage[9] = price0CumulativeLast: uint256 // + // storage[10] = price1CumulativeLast: uint256 // + // storage[11] = kLast: uint256 // + // =========================================================== // + + // initialize new AlloyDB + let alloydb = AlloyDB::new(client, BlockId::default()).unwrap(); + + // initialise empty in-memory-db + let cache_db = CacheDB::new(alloydb); + + // initialise an empty (default) EVM + let evm = Evm::builder() + .with_db(cache_db) + .build_trevm() + .fill_cfg(&NoopCfg) + .open_block(&NoopBlock, Shanghai::default()) + .unwrap() + .fill_tx(&GetReservesFiller) + .run() + .inspect_err(|e| panic!("Execution error {e:?}")) + .unwrap(); + + // Inspect the outcome of a transaction execution, and get the return value + println!("Execution result: {:#?}", evm.result()); + let output = evm.output().expect("Execution halted"); + + // decode bytes to reserves + ts via alloy's abi decode + let return_vals = getReservesCall::abi_decode_returns(output, true)?; + + // Print emulated getReserves() call output + println!("Reserve0: {:#?}", return_vals.reserve0); + println!("Reserve1: {:#?}", return_vals.reserve1); + println!("Timestamp: {:#?}", return_vals.blockTimestampLast); + + Ok(()) +} diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..1e491f9 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,3 @@ +reorder_imports = true +use_field_init_shorthand = true +use_small_heuristics = "Max" diff --git a/src/evm.rs b/src/evm.rs new file mode 100644 index 0000000..f4a1b23 --- /dev/null +++ b/src/evm.rs @@ -0,0 +1,1148 @@ +use crate::{ + states::EvmBlockComplete, BasicContext, Block, BlockComplete, BlockContext, Cfg, ErroredState, + EvmErrored, EvmNeedsCfg, EvmNeedsFirstBlock, EvmNeedsNextBlock, EvmNeedsTx, EvmReady, + EvmTransacted, HasCfg, HasOutputs, NeedsBlock, NeedsCfg, NeedsNextBlock, NeedsTx, Ready, + TransactedState, Tx, +}; +use alloy_consensus::constants::KECCAK_EMPTY; +use alloy_primitives::{Address, Bytes, U256}; +use revm::{ + db::{states::bundle_state::BundleRetention, BundleState}, + primitives::{ + Account, AccountInfo, AccountStatus, Bytecode, EVMError, EvmState, EvmStorageSlot, + ExecutionResult, InvalidTransaction, ResultAndState, SpecId, + }, + Database, DatabaseCommit, DatabaseRef, Evm, State, +}; +use std::{convert::Infallible, fmt}; + +/// Trevm provides a type-safe interface to the EVM, using the typestate pattern. +/// +/// See the [crate-level documentation](crate) for more information. +pub struct Trevm<'a, Ext, Db: Database + DatabaseCommit, TrevmState> { + inner: Box>, + state: TrevmState, +} + +impl fmt::Debug for Trevm<'_, Ext, Db, TrevmState> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Trevm").finish_non_exhaustive() + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> AsRef> + for Trevm<'a, Ext, Db, TrevmState> +{ + fn as_ref(&self) -> &Evm<'a, Ext, Db> { + &self.inner + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit> From> for EvmNeedsCfg<'a, Ext, Db> { + fn from(inner: Evm<'a, Ext, Db>) -> Self { + Self { inner: Box::new(inner), state: NeedsCfg::new() } + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, TrevmState> { + /// Get a reference to the current [`Evm`]. + pub fn inner(&self) -> &Evm<'a, Ext, Db> { + self.as_ref() + } + + /// Get a mutable reference to the current [`Evm`]. This should be used with + /// caution, as modifying the EVM may lead to inconsistent Trevmstate or invalid + /// execution. + pub fn inner_mut_unchecked(&mut self) -> &mut Evm<'a, Ext, Db> { + &mut self.inner + } + + /// Destructure the [`Trevm`] into its parts. + pub fn into_inner(self) -> Box> { + self.inner + } + + /// Get the id of the currently running hardfork spec. Convenience function + /// calling [`Evm::spec_id`]. + pub fn spec_id(&self) -> SpecId { + self.inner.spec_id() + } + + /// Get the current account info for a specific address. + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn try_read_account(&mut self, address: Address) -> Result, Db::Error> { + self.inner.db_mut().basic(address) + } + + /// Get the current nonce for a specific address + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn try_read_nonce(&mut self, address: Address) -> Result { + self.try_read_account(address).map(|a| a.map(|a| a.nonce).unwrap_or_default()) + } + + /// Get the current nonce for a specific address + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn try_read_balance(&mut self, address: Address) -> Result { + self.try_read_account(address).map(|a| a.map(|a| a.balance).unwrap_or_default()) + } + + /// Get the value of a storage slot. + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn try_read_storage(&mut self, address: Address, slot: U256) -> Result { + self.inner.db_mut().storage(address, slot) + } + + /// Get the code at the given account, if any. + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn try_read_code(&mut self, address: Address) -> Result, Db::Error> { + let acct_info = self.try_read_account(address)?; + match acct_info { + Some(acct) => Ok(Some(self.inner.db_mut().code_by_hash(acct.code_hash)?)), + None => Ok(None), + } + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit + DatabaseRef, TrevmState> + Trevm<'a, Ext, Db, TrevmState> +{ + /// Get the current account info for a specific address. + pub fn try_read_account_ref( + &self, + address: Address, + ) -> Result, ::Error> { + self.inner.db().basic_ref(address) + } + + /// Get the current nonce for a specific address + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn try_read_nonce_ref(&self, address: Address) -> Result::Error> { + self.try_read_account_ref(address).map(|a| a.map(|a| a.nonce).unwrap_or_default()) + } + + /// Get the current nonce for a specific address + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn try_read_balance_ref( + &self, + address: Address, + ) -> Result::Error> { + self.try_read_account_ref(address).map(|a| a.map(|a| a.balance).unwrap_or_default()) + } + + /// Get the value of a storage slot. + pub fn try_read_storage_ref( + &self, + address: Address, + slot: U256, + ) -> Result::Error> { + self.inner.db().storage_ref(address, slot) + } + + /// Get the code at the given account, if any. + pub fn try_read_code_ref( + &self, + address: Address, + ) -> Result, ::Error> { + let acct_info = self.try_read_account_ref(address)?; + match acct_info { + Some(acct) => Ok(Some(self.inner.db().code_by_hash_ref(acct.code_hash)?)), + None => Ok(None), + } + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> + Trevm<'a, Ext, Db, TrevmState> +{ + /// Get the current account info for a specific address. + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn read_account(&mut self, address: Address) -> Option { + self.inner.db_mut().basic(address).expect("infallible") + } + + /// Get the current nonce for a specific address + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn read_nonce(&mut self, address: Address) -> u64 { + self.read_account(address).map(|a: AccountInfo| a.nonce).unwrap_or_default() + } + + /// Get the current nonce for a specific address + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn read_balance(&mut self, address: Address) -> U256 { + self.read_account(address).map(|a: AccountInfo| a.balance).unwrap_or_default() + } + + /// Get the value of a storage slot. + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn read_storage(&mut self, address: Address, slot: U256) -> U256 { + self.inner.db_mut().storage(address, slot).expect("infallible") + } + + /// Get the code at the given account, if any. + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn read_code(&mut self, address: Address) -> Option { + let acct_info = self.read_account(address)?; + Some(self.inner.db_mut().code_by_hash(acct_info.code_hash).expect("infallible")) + } +} + +impl< + 'a, + Ext, + Db: Database + DatabaseRef + DatabaseCommit, + TrevmState, + > Trevm<'a, Ext, Db, TrevmState> +{ + /// Get the current account info for a specific address. + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn read_account_ref(&self, address: Address) -> Option { + self.inner.db().basic_ref(address).expect("infallible") + } + + /// Get the current nonce for a specific address + pub fn read_nonce_ref(&self, address: Address) -> u64 { + self.read_account_ref(address).map(|a: AccountInfo| a.nonce).unwrap_or_default() + } + + /// Get the current nonce for a specific address + pub fn read_balance_ref(&self, address: Address) -> U256 { + self.read_account_ref(address).map(|a: AccountInfo| a.balance).unwrap_or_default() + } + + /// Get the value of a storage slot. + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn read_storage_ref(&self, address: Address, slot: U256) -> U256 { + self.inner.db().storage_ref(address, slot).expect("infallible") + } + + /// Get the code at the given account, if any. + /// + /// Note: due to revm's DB model, this requires a mutable pointer. + pub fn read_code_ref(&self, address: Address) -> Option { + let acct_info = self.read_account_ref(address)?; + Some(self.inner.db().code_by_hash_ref(acct_info.code_hash).expect("infallible")) + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> Trevm<'a, Ext, Db, TrevmState> { + /// Commit a set of state changes to the database. This is a low-level API, + /// and is not intended for general use. Prefer executing a transaction. + pub fn commit_unchecked(&mut self, state: EvmState) { + self.inner.db_mut().commit(state); + } + + /// Modify an account with a closure and commit the modified account. This + /// is a low-level API, and is not intended for general use. + pub fn try_modify_account_unchecked( + &mut self, + address: Address, + f: F, + ) -> Result { + let db = self.inner_mut_unchecked().db_mut(); + + let mut info = db.basic(address)?.unwrap_or_default(); + let old = info.clone(); + f(&mut info); + + // Make a new account with the modified info + let mut acct = Account { info, status: AccountStatus::Touched, ..Default::default() }; + acct.mark_touch(); + + // Create a state object with the modified account. + let state = [(address, acct)].iter().cloned().collect(); + self.commit_unchecked(state); + + Ok(old) + } + + /// Set the nonce of an account, returning the previous nonce. This is a + /// low-level API, and is not intended for general use. + pub fn try_set_nonce_unchecked( + &mut self, + address: Address, + nonce: u64, + ) -> Result { + self.try_modify_account_unchecked(address, |info| info.nonce = nonce).map(|info| info.nonce) + } + + /// Increment the nonce of an account, returning the previous nonce. This is + /// a low-level API, and is not intended for general use. + /// + /// If the nonce is already at the maximum value, it will not be + /// incremented. + pub fn try_increment_nonce_unchecked(&mut self, address: Address) -> Result { + self.try_modify_account_unchecked(address, |info| info.nonce = info.nonce.saturating_add(1)) + .map(|info| info.nonce) + } + + /// Decrement the nonce of an account, returning the previous nonce. This is + /// a low-level API, and is not intended for general use. + /// + /// If the nonce is already 0, it will not be decremented. + pub fn try_decrement_nonce_unchecked(&mut self, address: Address) -> Result { + self.try_modify_account_unchecked(address, |info| info.nonce = info.nonce.saturating_sub(1)) + .map(|info| info.nonce) + } + + /// Set the EVM storage at a slot. This is a low-level API, and is not + /// intended for general use. + pub fn try_set_storage_unchecked( + &mut self, + address: Address, + slot: U256, + value: U256, + ) -> Result { + let db = self.inner_mut_unchecked().db_mut(); + let info = db.basic(address)?.unwrap_or_default(); + let old = db.storage(address, slot)?; + + let change = EvmStorageSlot::new_changed(old, value); + + // Make a new account with the modified storage + let storage = [(slot, change)].iter().cloned().collect(); + let mut acct = Account { storage, info, ..Default::default() }; + acct.mark_touch(); + + // Create a state object with the modified account. + let state = [(address, acct)].iter().cloned().collect(); + self.commit_unchecked(state); + + Ok(old) + } + + /// Set the bytecode at a specific address, returning the previous bytecode + /// at that address. This is a low-level API, and is not intended for + /// general use. + pub fn try_set_bytecode_unchecked( + &mut self, + address: Address, + bytecode: Bytecode, + ) -> Result, Db::Error> { + let db = self.inner_mut_unchecked().db_mut(); + let mut info = db.basic(address)?.unwrap_or_default(); + + let old = if info.code_hash != KECCAK_EMPTY { + Some(db.code_by_hash(info.code_hash)?) + } else { + None + }; + + info.code_hash = if bytecode.is_empty() { KECCAK_EMPTY } else { bytecode.hash_slow() }; + info.code = Some(bytecode); + + let mut acct = Account { info, ..Default::default() }; + acct.mark_touch(); + let state = [(address, acct)].iter().cloned().collect(); + self.commit_unchecked(state); + + Ok(old) + } + + /// Increase the balance of an account. Returns the previous balance. This + /// is a low-level API, and is not intended for general use. + /// + /// If this would cause an overflow, the balance will be increased to the + /// maximum value. + pub fn try_increase_balance_unchecked( + &mut self, + address: Address, + amount: U256, + ) -> Result { + self.try_modify_account_unchecked(address, |info| { + info.balance = info.balance.saturating_add(amount) + }) + .map(|info| info.balance) + } + + /// Decrease the balance of an account. Returns the previous balance. This + /// is a low-level API, and is not intended for general use. + /// + /// If this would cause an underflow, the balance will be decreased to 0. + pub fn try_decrease_balance_unchecked( + &mut self, + address: Address, + amount: U256, + ) -> Result { + self.try_modify_account_unchecked(address, |info| { + info.balance = info.balance.saturating_sub(amount) + }) + .map(|info| info.balance) + } + + /// Set the balance of an account. Returns the previous balance. This is a + /// low-level API, and is not intended for general use. + pub fn try_set_balance_unchecked( + &mut self, + address: Address, + amount: U256, + ) -> Result { + self.try_modify_account_unchecked(address, |info| info.balance = amount) + .map(|info| info.balance) + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState> + Trevm<'a, Ext, Db, TrevmState> +{ + /// Modify an account with a closure and commit the modified account. This + /// is a low-level API, and is not intended for general use. + pub fn modify_account_unchecked( + &mut self, + address: Address, + f: impl FnOnce(&mut AccountInfo), + ) -> AccountInfo { + self.try_modify_account_unchecked(address, f).expect("infallible") + } + + /// Set the nonce of an account, returning the previous nonce. This is a + /// low-level API, and is not intended for general use. + pub fn set_nonce_unchecked(&mut self, address: Address, nonce: u64) -> u64 { + self.try_set_nonce_unchecked(address, nonce).expect("infallible") + } + + /// Increment the nonce of an account, returning the previous nonce. This is + /// a low-level API, and is not intended for general use. + /// + /// If this would cause the nonce to overflow, the nonce will be set to the + /// maximum value. + pub fn increment_nonce_unchecked(&mut self, address: Address) -> u64 { + self.try_increment_nonce_unchecked(address).expect("infallible") + } + + /// Decrement the nonce of an account, returning the previous nonce. This is + /// a low-level API, and is not intended for general use. + /// + /// If this would cause the nonce to underflow, the nonce will be set to 0. + pub fn decrement_nonce_unchecked(&mut self, address: Address) -> u64 { + self.try_decrement_nonce_unchecked(address).expect("infallible") + } + + /// Set the EVM storage at a slot. This is a low-level API, and is not + /// intended for general use. + pub fn set_storage_unchecked(&mut self, address: Address, slot: U256, value: U256) -> U256 { + self.try_set_storage_unchecked(address, slot, value).expect("infallible") + } + + /// Set the bytecode at a specific address, returning the previous bytecode + /// at that address. This is a low-level API, and is not intended for + /// general use. + pub fn set_bytecode_unchecked( + &mut self, + address: Address, + bytecode: Bytecode, + ) -> Option { + self.try_set_bytecode_unchecked(address, bytecode).expect("infallible") + } + + /// Increase the balance of an account. Returns the previous balance. This + /// is a low-level API, and is not intended for general use. + pub fn increase_balance_unchecked(&mut self, address: Address, amount: U256) -> U256 { + self.try_increase_balance_unchecked(address, amount).expect("infallible") + } + + /// Decrease the balance of an account. Returns the previous balance. This + /// is a low-level API, and is not intended for general use. + pub fn decrease_balance_unchecked(&mut self, address: Address, amount: U256) -> U256 { + self.try_decrease_balance_unchecked(address, amount).expect("infallible") + } + + /// Set the balance of an account. Returns the previous balance. This is a + /// low-level API, and is not intended for general use. + pub fn set_balance_unchecked(&mut self, address: Address, amount: U256) -> U256 { + self.try_set_balance_unchecked(address, amount).expect("infallible") + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, EvmState> Trevm<'a, Ext, State, EvmState> { + /// Set the [EIP-161] state clear flag, activated in the Spurious Dragon + /// hardfork. + pub fn set_state_clear_flag(&mut self, flag: bool) { + self.inner.db_mut().set_state_clear_flag(flag) + } +} + +// --- NEEDS CFG + +impl<'a, Ext, Db: Database + DatabaseCommit> EvmNeedsCfg<'a, Ext, Db> { + /// Fill the configuration environment. + pub fn fill_cfg(mut self, filler: &T) -> EvmNeedsFirstBlock<'a, Ext, Db> { + filler.fill_cfg(&mut self.inner); + // SAFETY: Same size and repr. Only phantomdata type changes + unsafe { std::mem::transmute(self) } + } +} + +// --- HAS CFG + +impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: HasCfg> Trevm<'a, Ext, Db, TrevmState> { + /// Set the [EIP-170] contract code size limit. By default this is set to + /// 0x6000 bytes (~25KiB). Contracts whose bytecode is larger than this + /// limit cannot be deployed and will produce a [`CreateInitCodeSizeLimit`] + /// error. + /// + /// [`CreateInitCodeSizeLimit`]: InvalidTransaction::CreateInitCodeSizeLimit + /// [`Eip-170`]: https://eips.ethereum.org/EIPS/eip-170 + pub fn set_code_size_limit(&mut self, limit: usize) -> Option { + let cfg = self.inner.cfg_mut(); + cfg.limit_contract_code_size.replace(limit) + } + + /// Disable the [EIP-170] contract code size limit, returning the previous + /// setting. + /// + /// [`Eip-170`]: https://eips.ethereum.org/EIPS/eip-170 + pub fn disable_code_size_limit(&mut self) -> Option { + self.inner.cfg_mut().limit_contract_code_size.take() + } + + /// Run a closure with the code size limit disabled, then restore the + /// previous setting. + pub fn without_code_size_limit( + mut self, + f: F, + ) -> Trevm<'a, Ext, Db, NewState> + where + F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>, + { + let limit = self.disable_code_size_limit(); + let mut new = f(self); + if let Some(limit) = limit { + new.set_code_size_limit(limit); + } + new + } + + /// Set the [EIP-170] contract code size limit to the default value of + /// 0x6000 bytes (~25KiB), returning the previous setting. Contracts whose + /// bytecode is larger than this limit cannot be deployed and will produce + /// a [`CreateInitCodeSizeLimit`] error. + /// + /// [`CreateInitCodeSizeLimit`]: InvalidTransaction::CreateInitCodeSizeLimit + /// [`Eip-170`]: https://eips.ethereum.org/EIPS/eip-170 + pub fn set_default_code_size_limit(&mut self) -> Option { + self.set_code_size_limit(0x6000) + } + + /// Set the KZG settings used for point evaluation precompiles. By default + /// this is set to the settings used in the Ethereum mainnet. + /// + /// This is a low-level API, and is not intended for general use. + #[cfg(feature = "c-kzg")] + pub fn set_kzg_settings( + &mut self, + settings: revm::primitives::EnvKzgSettings, + ) -> revm::primitives::EnvKzgSettings { + let cfg = self.inner.cfg_mut(); + std::mem::replace(&mut cfg.kzg_settings, settings) + } + + /// Set a limit beyond which a callframe's memory cannot be resized. + /// Attempting to resize beyond this limit will result in a + /// [OutOfGasError::Memory] error. + /// + /// In cases where the gas limit may be extraordinarily high, it is + /// recommended to set this to a sane value to prevent memory allocation + /// panics. Defaults to `2^32 - 1` bytes per EIP-1985. + /// + /// [OutOfGasError::Memory]: revm::primitives::OutOfGasError::Memory + #[cfg(feature = "memory_limit")] + pub fn set_memory_limit(&mut self, new_limit: u64) -> u64 { + let cfg = self.inner.cfg_mut(); + std::mem::replace(&mut cfg.memory_limit, new_limit) + } + + /// Disable balance checks. If the sender does not have enough balance to + /// cover the transaction, the sender will be given enough ether to ensure + /// execution doesn't fail. + #[cfg(feature = "optional_balance_check")] + pub fn disable_balance_check(&mut self) { + self.inner.cfg_mut().disable_balance_check = true + } + + /// Enable balance checks. See [`Self::disable_balance_check`]. + #[cfg(feature = "optional_balance_check")] + pub fn enable_balance_check(&mut self) { + self.inner.cfg_mut().disable_balance_check = false + } + + /// Run a closure with balance checks disabled, then restore the previous + /// setting. + #[cfg(feature = "optional_balance_check")] + pub fn without_balance_check( + mut self, + f: F, + ) -> Trevm<'a, Ext, Db, NewState> + where + F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>, + { + let previous = self.inner.cfg().disable_balance_check; + self.disable_balance_check(); + let mut new = f(self); + new.inner.cfg_mut().disable_balance_check = previous; + new + } + + /// Disable block gas limits. This allows transactions to execute even if + /// they gas needs exceed the block gas limit. This is useful for + /// simulating large transactions like forge scripts. + #[cfg(feature = "optional_block_gas_limit")] + pub fn disable_block_gas_limit(&mut self) { + self.inner.cfg_mut().disable_beneficiary_reward = true + } + + /// Enable block gas limits. See [`Self::disable_block_gas_limit`]. + #[cfg(feature = "optional_block_gas_limit")] + pub fn enable_block_gas_limit(&mut self) { + self.inner.cfg_mut().disable_beneficiary_reward = false + } + + /// Run a closure with block gas limits disabled, then restore the previous + /// setting. + #[cfg(feature = "optional_block_gas_limit")] + pub fn without_block_gas_limit( + mut self, + f: F, + ) -> Trevm<'a, Ext, Db, NewState> + where + F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>, + { + let previous = self.inner.cfg().disable_block_gas_limit; + self.disable_block_gas_limit(); + let mut new = f(self); + new.inner.cfg_mut().disable_block_gas_limit = previous; + new + } + + /// Disable [EIP-3607]. This allows transactions to originate from accounts + /// that contain code. This is useful for simulating smart-contract calls. + /// + /// [EIP-3607]: https://eips.ethereum.org/EIPS/eip-3607 + #[cfg(feature = "optional_eip3607")] + pub fn disable_eip3607(&mut self) { + self.inner.cfg_mut().disable_eip3607 = true + } + + /// Enable [EIP-3607]. See [`Self::disable_eip3607`]. + /// + /// [EIP-3607]: https://eips.ethereum.org/EIPS/eip-3607 + #[cfg(feature = "optional_eip3607")] + pub fn enable_eip3607(&mut self) { + self.inner.cfg_mut().disable_eip3607 = false + } + + /// Run a closure with [EIP-3607] disabled, then restore the previous + /// setting. + #[cfg(feature = "optional_eip3607")] + pub fn without_eip3607(mut self, f: F) -> Trevm<'a, Ext, Db, NewState> + where + F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>, + { + let previous = self.inner.cfg().disable_eip3607; + self.disable_eip3607(); + let mut new = f(self); + new.inner.cfg_mut().disable_eip3607 = previous; + new + } + + /// Disables all gas refunds. This is useful when using chains that have + /// gas refunds disabled e.g. Avalanche. Reasoning behind removing gas + /// refunds can be found in [EIP-3298]. + /// By default, it is set to `false`. + /// + /// [EIP-3298]: https://eips.ethereum.org/EIPS/eip-3298 + #[cfg(feature = "optional_gas_refund")] + pub fn disable_gas_refund(&mut self) { + self.inner.cfg_mut().disable_gas_refund = true + } + + /// Enable gas refunds. See [`Self::disable_gas_refund`]. + #[cfg(feature = "optional_gas_refund")] + pub fn enable_gas_refund(&mut self) { + self.inner.cfg_mut().disable_gas_refund = false + } + + /// Run a closure with gas refunds disabled, then restore the previous + /// setting. + #[cfg(feature = "optional_gas_refund")] + pub fn without_gas_refund(mut self, f: F) -> Trevm<'a, Ext, Db, NewState> + where + F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>, + { + let previous = self.inner.cfg().disable_gas_refund; + self.disable_gas_refund(); + let mut new = f(self); + new.inner.cfg_mut().disable_gas_refund = previous; + new + } + + /// Disables [EIP-1559] base fee checks. This is useful for testing method + /// calls with zero gas price. + /// + /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 + #[cfg(feature = "optional_no_base_fee")] + pub fn disable_base_fee(&mut self) { + self.inner.cfg_mut().disable_base_fee = true; + } + + /// Enable [EIP-1559] base fee checks. See [`Self::disable_base_fee`]. + /// + /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 + #[cfg(feature = "optional_no_base_fee")] + pub fn enable_base_fee(&mut self) { + self.inner.cfg_mut().disable_base_fee = false + } + + /// Run a closure with [EIP-1559] base fee checks disabled, then restore the + /// previous setting. + /// + /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559 + #[cfg(feature = "optional_no_base_fee")] + pub fn without_base_fee(mut self, f: F) -> Trevm<'a, Ext, Db, NewState> + where + F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>, + { + let previous = self.inner.cfg().disable_base_fee; + self.disable_base_fee(); + let mut new = f(self); + new.inner.cfg_mut().disable_base_fee = previous; + new + } + + /// Disable the payout of the block and gas fees to the block beneficiary. + #[cfg(feature = "optional_beneficiary_reward")] + pub fn disable_beneficiary_reward(&mut self) { + self.inner.cfg_mut().disable_beneficiary_reward = true; + } + + /// Enable the payout of the block and gas fees to the block beneficiary. + /// See [`Self::disable_beneficiary_reward`]. + #[cfg(feature = "optional_beneficiary_reward")] + pub fn enable_beneficiary_reward(&mut self) { + self.inner.cfg_mut().disable_beneficiary_reward = false + } + + /// Run a closure with the block and gas fees payout disabled, then restore + /// the previous setting. + #[cfg(feature = "optional_beneficiary_reward")] + pub fn without_beneficiary_reward( + mut self, + f: F, + ) -> Trevm<'a, Ext, Db, NewState> + where + F: FnOnce(Self) -> Trevm<'a, Ext, Db, NewState>, + { + let previous = self.inner.cfg().disable_beneficiary_reward; + self.disable_beneficiary_reward(); + let mut new = f(self); + new.inner.cfg_mut().disable_beneficiary_reward = previous; + new + } +} + +// --- NEEDS BLOCK + +impl<'a, Ext, Db: Database + DatabaseCommit, TrevmState: NeedsBlock> + Trevm<'a, Ext, Db, TrevmState> +{ + /// Open a block, apply some logic, and return the EVM ready for the next + /// transaction. + /// + /// This is intended to be used for pre-block logic, such as applying + /// pre-block hooks introduced in [EIP-4788] or [EIP-2935]. + /// + /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 + /// [EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935 + pub fn open_block( + mut self, + filler: &B, + mut context: C, + ) -> Result, EvmErrored<'a, Ext, Db, C>> + where + B: Block, + C: BlockContext, + Db: Database + DatabaseCommit, + { + let res = context.open_block(self.inner_mut_unchecked(), filler); + + match res { + Ok(_) => Ok(Trevm { inner: self.inner, state: NeedsTx(context) }), + Err(error) => Err(Trevm { inner: self.inner, state: ErroredState { context, error } }), + } + } + + /// Open a block and return the EVM ready for the next transaction. This is + /// a convenience API and uses [`BasicContext`] for the block context. + /// + /// This is a shortcut for `open_block(filler, BasicContext::default())`. + /// It will not perform any pre-block or post-block logic, and will not + /// produce transaction receipts. As such, this cannot be used to produce + /// real blocks on any network, and may produce inconsistent results when + /// applied on networks that require pre-block or post-block logic. + pub fn fill_block(self, filler: &B) -> EvmNeedsTx<'a, Ext, Db, BasicContext> { + self.open_block(filler, BasicContext) + .unwrap_or_else(|_| unreachable!("basic filler is infallible")) + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, Missing: HasOutputs> + Trevm<'a, Ext, State, Missing> +{ + /// Finish execution and return the outputs. + /// + /// ## Panics + /// + /// If the State has not been built with StateBuilder::with_bundle_update. + /// + /// See [`State::merge_transitions`] and [`State::take_bundle`]. + pub fn finish(self) -> BundleState + where + Db: Database + DatabaseCommit, + { + let Self { inner: mut evm, .. } = self; + + evm.db_mut().merge_transitions(BundleRetention::Reverts); + let bundle = evm.db_mut().take_bundle(); + + bundle + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, C> EvmBlockComplete<'a, Ext, Db, C> { + /// Destructure the EVM and return the block context and the EVM ready for + /// the next block. + pub fn take_context(self) -> (C, EvmNeedsNextBlock<'a, Ext, Db>) { + (self.state.0, EvmNeedsNextBlock { inner: self.inner, state: NeedsNextBlock::new() }) + } + + /// Discard the block context and return the EVM ready for the next block. + pub fn discard_context(self) -> EvmNeedsNextBlock<'a, Ext, Db> { + self.take_context().1 + } +} + +// --- NEEDS FIRST TX + +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> EvmNeedsTx<'a, Ext, Db, C> { + /// Close the current block, applying some logic, and returning the EVM + /// ready for the next block. + /// + /// This is intended to be used for post-block logic, such as applying + /// post-block hooks introduced in [EIP-7002] or [EIP-7251]. + /// + /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 + /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 + pub fn close_block( + self, + ) -> Result, EvmErrored<'a, Ext, Db, C>> { + let Trevm { mut inner, state: NeedsTx(mut context) } = self; + + let res = context.close_block(&mut inner); + + inner.block_mut().clear(); + + match res { + Ok(_) => Ok(Trevm { inner, state: BlockComplete(context) }), + Err(error) => Err(Trevm { inner, state: ErroredState { context, error } }), + } + } + + /// Fill the transaction environment. + pub fn fill_tx(mut self, filler: &T) -> EvmReady<'a, Ext, Db, C> { + filler.fill_tx(&mut self.inner); + EvmReady { inner: self.inner, state: Ready(self.state.0) } + } + + /// Execute a transaction. Shortcut for `fill_tx(tx).run_tx()`. + pub fn run_tx( + self, + filler: &T, + ) -> Result, EvmErrored<'a, Ext, Db, C>> { + self.fill_tx(filler).run() + } + + /// Execute a transaction, accept the output, and ignore errors. + pub fn run_tx_ignore_err(self, filler: &T) -> Self { + match self.run_tx(filler) { + Ok(evm) => evm.accept(), + Err(evm) => evm.discard_error(), + } + } +} + +// --- READY + +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> EvmReady<'a, Ext, Db, C> { + /// Clear the current transaction environment. + pub fn clear_tx(self) -> EvmNeedsTx<'a, Ext, Db, C> { + // NB: we do not clear the tx env here, as we may read it during `BlockContext::post_tx` + EvmNeedsTx { inner: self.inner, state: NeedsTx(self.state.0) } + } + + /// Execute the loaded transaction. + pub fn run(mut self) -> Result, EvmErrored<'a, Ext, Db, C>> { + let result = self.inner.transact(); + + let Trevm { inner, state: Ready(context) } = self; + + match result { + Ok(result) => Ok(Trevm { inner, state: TransactedState { context, result } }), + Err(error) => { + Err(EvmErrored { inner, state: ErroredState { context, error: error.into() } }) + } + } + } + + /// Execute the loaded transaction, accept the output, and ignore errors. + pub fn run_ignore_err(self) -> EvmNeedsTx<'a, Ext, Db, C> { + match self.run() { + Ok(evm) => evm.accept(), + Err(evm) => evm.discard_error(), + } + } +} + +// --- ERRORED + +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext, E> + EvmErrored<'a, Ext, Db, C, E> +{ + /// Get a reference to the error. + pub const fn error(&self) -> &E { + &self.state.error + } + + /// Inspect the error with a closure. + pub fn inspect_error(&self, f: F) -> T + where + F: FnOnce(&E) -> T, + { + f(self.error()) + } + + /// Discard the error and return the EVM. + pub fn discard_error(self) -> EvmNeedsTx<'a, Ext, Db, C> { + Trevm { inner: self.inner, state: NeedsTx(self.state.context) } + } + + /// Convert the error into an [`EVMError`]. + pub fn into_error(self) -> E { + self.state.error + } + + /// Reset the EVM, returning the error and the EVM ready for the next + /// transaction. + pub fn take_error(self) -> (EvmNeedsTx<'a, Ext, Db, C>, E) { + let Trevm { inner, state: ErroredState { context, error } } = self; + (Trevm { inner, state: NeedsTx(context) }, error) + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> + EvmErrored<'a, Ext, Db, C, EVMError> +{ + /// Check if the error is a transaction error. This is provided as a + /// convenience function for common cases, as Transaction errors should + /// usually be discarded. + pub const fn is_transaction_error(&self) -> bool { + matches!(self.state.error, EVMError::Transaction(_)) + } + + /// Fallible cast to a [`InvalidTransaction`]. + pub const fn as_transaction_err(&self) -> Option<&InvalidTransaction> { + match &self.state.error { + EVMError::Transaction(err) => Some(err), + _ => None, + } + } + + /// Discard the error if it is a transaction error, returning the EVM. If + /// the error is not a transaction error, return self + pub fn discard_transaction_error(self) -> Result, Self> { + if self.is_transaction_error() { + Ok(self.discard_error()) + } else { + Err(self) + } + } +} + +// --- TRANSACTED + +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> AsRef + for EvmTransacted<'a, Ext, Db, C> +{ + fn as_ref(&self) -> &ResultAndState { + &self.state.result + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> AsRef + for EvmTransacted<'a, Ext, Db, C> +{ + fn as_ref(&self) -> &ExecutionResult { + &self.state.result.result + } +} + +impl<'a, Ext, Db: Database + DatabaseCommit, C: BlockContext> + EvmTransacted<'a, Ext, Db, C> +{ + /// Get a reference to the result. + pub fn result(&self) -> &ExecutionResult { + self.as_ref() + } + + /// Get a mutable reference to the result. Modification of the result is + /// discouraged, as it may lead to inconsistent state. + /// + /// This is primarily intended for use in [`SystemTx`] execution. + /// + /// [`SystemTx`]: crate::syscall::SystemTx + pub fn result_mut_unchecked(&mut self) -> &mut ExecutionResult { + &mut self.state.result.result + } + + /// Get a reference to the state. + pub const fn state(&self) -> &EvmState { + &self.state.result.state + } + + /// Get a mutable reference to the state. Modification of the state is + /// discouraged, as it may lead to inconsistent state. + pub fn state_mut_unchecked(&mut self) -> &mut EvmState { + &mut self.state.result.state + } + + /// Get a reference to the result and state. + pub fn result_and_state(&self) -> &ResultAndState { + self.as_ref() + } + + /// Get a mutable reference to the result and state. Modification of the + /// result and state is discouraged, as it may lead to inconsistent state. + /// + /// This is primarily intended for use in [`SystemTx`] execution. + /// + /// [`SystemTx`]: crate::syscall::SystemTx + pub fn result_and_state_mut_unchecked(&mut self) -> &mut ResultAndState { + &mut self.state.result + } + + /// Get the output of the transaction. This is the return value of the + /// outermost callframe. + pub fn output(&self) -> Option<&Bytes> { + self.result().output() + } + + /// Get the output of the transaction, and decode it as the return value of + /// a [`SolCall`]. If `validate` is true, the output will be type- and + /// range-checked. + /// + /// [`SolCall`]: alloy_sol_types::SolCall + pub fn output_sol( + &self, + validate: bool, + ) -> Option> + where + T::Return: alloy_sol_types::SolType, + { + self.output().map(|output| T::abi_decode_returns(output, validate)) + } + + /// Get the gas used by the transaction. + pub fn gas_used(&self) -> u64 { + self.state.result.result.gas_used() + } + + /// Discard the state changes and return the EVM. + pub fn reject(self) -> EvmNeedsTx<'a, Ext, Db, C> { + Trevm { inner: self.inner, state: NeedsTx(self.state.context) } + } + + /// Accept the state changes, invoking the [`BlockContext::after_tx`] + /// method, and return the EVM ready for the next transaction. + pub fn accept(self) -> EvmNeedsTx<'a, Ext, Db, C> { + let Trevm { mut inner, state: TransactedState { mut context, result } } = self; + + context.after_tx(&mut inner, result); + + Trevm { inner, state: NeedsTx(context) } + } + + /// Accept the state changes, skipping the [`BlockContext::after_tx`] + /// method, and committing them directly to the database. + /// + /// This is a low-level API, and is not intended for general use. It is + /// almost always better to use [`Self::accept`] instead. + pub fn accept_skip_context_unchecked(self) -> EvmNeedsTx<'a, Ext, Db, C> { + let Trevm { mut inner, state: TransactedState { context, result } } = self; + + inner.db_mut().commit(result.state); + + Trevm { inner, state: NeedsTx(context) } + } +} + +// Some code above and documentation is adapted from the revm crate, and is +// reproduced here under the terms of the MIT license. +// +// MIT License +// +// Copyright (c) 2021-2024 draganrakita +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// Some code above is reproduced from `reth`. It is reused here under the MIT +// license. +// +// The MIT License (MIT) +// +// Copyright (c) 2022-2024 Reth Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. diff --git a/src/fill/alloy.rs b/src/fill/alloy.rs new file mode 100644 index 0000000..12f9db0 --- /dev/null +++ b/src/fill/alloy.rs @@ -0,0 +1,323 @@ +use alloy_consensus::Signed; +use alloy_primitives::U256; +use revm::primitives::{BlockEnv, TxEnv}; + +use crate::{Block, Tx}; + +impl Tx for Signed { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + let TxEnv { + caller, + gas_limit, + gas_price, + transact_to, + value, + data, + nonce, + chain_id, + access_list, + gas_priority_fee, + blob_hashes, + max_fee_per_blob_gas, + authorization_list, + } = tx_env; + *caller = self.recover_signer().unwrap(); + *gas_limit = self.tx().gas_limit as u64; + *gas_price = U256::from(self.tx().gas_price); + *transact_to = self.tx().to; + *value = self.tx().value; + *data = self.tx().input.clone(); + *nonce = Some(self.tx().nonce); + *chain_id = self.tx().chain_id; + access_list.clear(); + gas_priority_fee.take(); + blob_hashes.clear(); + max_fee_per_blob_gas.take(); + authorization_list.take(); + } +} + +impl Tx for Signed { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + let TxEnv { + caller, + gas_limit, + gas_price, + transact_to, + value, + data, + nonce, + chain_id, + access_list, + gas_priority_fee, + blob_hashes, + max_fee_per_blob_gas, + authorization_list, + } = tx_env; + *caller = self.recover_signer().unwrap(); + *gas_limit = self.tx().gas_limit as u64; + *gas_price = U256::from(self.tx().gas_price); + *transact_to = self.tx().to; + *value = self.tx().value; + *data = self.tx().input.clone(); + *nonce = Some(self.tx().nonce); + *chain_id = Some(self.tx().chain_id); + access_list.clone_from(&self.tx().access_list.0); + gas_priority_fee.take(); + blob_hashes.clear(); + max_fee_per_blob_gas.take(); + authorization_list.take(); + } +} + +impl Tx for Signed { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + let TxEnv { + caller, + gas_limit, + gas_price, + transact_to, + value, + data, + nonce, + chain_id, + access_list, + gas_priority_fee, + blob_hashes, + max_fee_per_blob_gas, + authorization_list, + } = tx_env; + *caller = self.recover_signer().unwrap(); + *gas_limit = self.tx().gas_limit as u64; + *gas_price = U256::from(self.tx().max_fee_per_gas); + *transact_to = self.tx().to; + *value = self.tx().value; + *data = self.tx().input.clone(); + *nonce = Some(self.tx().nonce); + *chain_id = Some(self.tx().chain_id); + access_list.clone_from(&self.tx().access_list.0); + *gas_priority_fee = Some(U256::from(self.tx().max_priority_fee_per_gas)); + blob_hashes.clear(); + max_fee_per_blob_gas.take(); + authorization_list.take(); + } +} + +impl Tx for Signed { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + let TxEnv { + caller, + gas_limit, + gas_price, + transact_to, + value, + data, + nonce, + chain_id, + access_list, + gas_priority_fee, + blob_hashes, + max_fee_per_blob_gas, + authorization_list, + } = tx_env; + *caller = self.recover_signer().unwrap(); + *gas_limit = self.tx().gas_limit as u64; + *gas_price = U256::from(self.tx().max_fee_per_gas); + *transact_to = self.tx().to.into(); + *value = self.tx().value; + *data = self.tx().input.clone(); + *nonce = Some(self.tx().nonce); + *chain_id = Some(self.tx().chain_id); + access_list.clone_from(&self.tx().access_list.0); + *gas_priority_fee = Some(U256::from(self.tx().max_priority_fee_per_gas)); + blob_hashes.clone_from(&self.tx().blob_versioned_hashes); + *max_fee_per_blob_gas = Some(U256::from(self.tx().max_fee_per_blob_gas)); + authorization_list.take(); + } +} + +impl Tx for Signed { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + let TxEnv { + caller, + gas_limit, + gas_price, + transact_to, + value, + data, + nonce, + chain_id, + access_list, + gas_priority_fee, + blob_hashes, + max_fee_per_blob_gas, + authorization_list, + } = tx_env; + *caller = self.recover_signer().unwrap(); + *gas_limit = self.tx().tx.gas_limit as u64; + *gas_price = U256::from(self.tx().tx.max_fee_per_gas); + *transact_to = self.tx().tx.to.into(); + *value = self.tx().tx.value; + *data = self.tx().tx.input.clone(); + *nonce = Some(self.tx().tx.nonce); + *chain_id = Some(self.tx().tx.chain_id); + access_list.clone_from(&self.tx().tx.access_list.0); + *gas_priority_fee = Some(U256::from(self.tx().tx.max_priority_fee_per_gas)); + blob_hashes.clone_from(&self.tx().tx.blob_versioned_hashes); + *max_fee_per_blob_gas = Some(U256::from(self.tx().tx.max_fee_per_blob_gas)); + authorization_list.take(); + } +} + +impl Tx for Signed { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + let TxEnv { + caller, + gas_limit, + gas_price, + transact_to, + value, + data, + nonce, + chain_id, + access_list, + gas_priority_fee, + blob_hashes, + max_fee_per_blob_gas, + authorization_list, + } = tx_env; + let tx = match self.tx() { + alloy_consensus::TxEip4844Variant::TxEip4844(tx) => tx, + alloy_consensus::TxEip4844Variant::TxEip4844WithSidecar(tx) => &tx.tx, + }; + *caller = self.recover_signer().unwrap(); + *gas_limit = tx.gas_limit as u64; + *gas_price = U256::from(tx.max_fee_per_gas); + *transact_to = tx.to.into(); + *value = tx.value; + *data = tx.input.clone(); + *nonce = Some(tx.nonce); + *chain_id = Some(tx.chain_id); + access_list.clone_from(&tx.access_list.0); + *gas_priority_fee = Some(U256::from(tx.max_priority_fee_per_gas)); + blob_hashes.clone_from(&tx.blob_versioned_hashes); + *max_fee_per_blob_gas = Some(U256::from(tx.max_fee_per_blob_gas)); + authorization_list.take(); + } +} + +impl Tx for alloy_consensus::TxEnvelope { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + match self { + Self::Legacy(t) => t.fill_tx_env(tx_env), + Self::Eip2930(t) => t.fill_tx_env(tx_env), + Self::Eip1559(t) => t.fill_tx_env(tx_env), + Self::Eip4844(t) => t.fill_tx_env(tx_env), + _ => panic!("Unsupported transaction type"), + } + } +} + +impl Block for alloy_consensus::Header { + fn fill_block_env(&self, block_env: &mut revm::primitives::BlockEnv) { + let BlockEnv { + number, + coinbase, + timestamp, + gas_limit, + basefee, + difficulty, + prevrandao, + blob_excess_gas_and_price: _, + } = block_env; + *number = U256::from(self.number); + *coinbase = self.beneficiary; + *timestamp = U256::from(self.timestamp); + *gas_limit = U256::from(self.gas_limit); + *basefee = self.base_fee_per_gas.map_or_else(Default::default, U256::from); + + *difficulty = self.difficulty; + *prevrandao = if self.difficulty.is_zero() { Some(self.mix_hash) } else { None }; + + if let Some(excess_blob_gas) = self.excess_blob_gas { + block_env.set_blob_excess_gas_and_price(excess_blob_gas as u64); + } + } + + fn tx_count_hint(&self) -> Option { + None + } +} + +#[cfg(test)] +mod tests { + use crate::{NoopBlock, NoopCfg, TrevmBuilder}; + use alloy_consensus::{Header, TxEnvelope, EMPTY_ROOT_HASH}; + + use alloy_rlp::Decodable; + + use revm::{Evm, InMemoryDB}; + + #[test] + // Test vector from https://etherscan.io/tx/0xce4dc6d7a7549a98ee3b071b67e970879ff51b5b95d1c340bacd80fa1e1aab31 + fn test_live_tx_1559_fill() { + let raw_tx = alloy_primitives::hex::decode("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8").unwrap(); + let tx = TxEnvelope::decode(&mut raw_tx.as_slice()).unwrap(); + + let _ = Evm::builder() + .with_db(InMemoryDB::default()) + .build_trevm() + .fill_cfg(&NoopCfg) + .fill_block(&NoopBlock) + .fill_tx(&tx); + } + + #[test] + // Test vector from https://etherscan.io/tx/0x280cde7cdefe4b188750e76c888f13bd05ce9a4d7767730feefe8a0e50ca6fc4 + fn test_live_tx_legacy_fill() { + let raw_tx = alloy_primitives::hex::decode("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8").unwrap(); + let tx = TxEnvelope::decode(&mut raw_tx.as_slice()).unwrap(); + + let _ = Evm::builder() + .with_db(InMemoryDB::default()) + .build_trevm() + .fill_cfg(&NoopCfg) + .fill_block(&NoopBlock) + .fill_tx(&tx); + } + + #[test] + // Test vector from https://sepolia.etherscan.io/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 + // Blobscan: https://sepolia.blobscan.com/tx/0x9a22ccb0029bc8b0ddd073be1a1d923b7ae2b2ea52100bae0db4424f9107e9c0 + fn test_live_tx_4844_fill() { + let raw_tx = alloy_primitives::hex::decode("0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544").unwrap(); + let tx = TxEnvelope::decode(&mut raw_tx.as_slice()).unwrap(); + + let _ = Evm::builder() + .with_db(InMemoryDB::default()) + .build_trevm() + .fill_cfg(&NoopCfg) + .fill_block(&NoopBlock) + .fill_tx(&tx); + } + + #[test] + fn test_header_fill() { + let raw = r#"{"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","ommersHash":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","beneficiary":"0x0000000000000000000000000000000000000000","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","withdrawalsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","difficulty":"0x0","number":"0x0","gasLimit":"0x0","gasUsed":"0x0","timestamp":"0x0","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000000","baseFeePerGas":"0x1","extraData":"0x"}"#; + let header = Header { + base_fee_per_gas: Some(1), + withdrawals_root: Some(EMPTY_ROOT_HASH), + ..Default::default() + }; + + let json = serde_json::to_string(&header).unwrap(); + assert_eq!(json, raw); + + // Fill using the constructed header + let _ = Evm::builder() + .with_db(InMemoryDB::default()) + .build_trevm() + .fill_cfg(&NoopCfg) + .fill_block(&header); + } +} diff --git a/src/fill/mod.rs b/src/fill/mod.rs new file mode 100644 index 0000000..6c33032 --- /dev/null +++ b/src/fill/mod.rs @@ -0,0 +1,9 @@ +mod alloy; + +mod traits; +pub use traits::{Block, Cfg, Tx}; + +mod noop; +pub use noop::{NoopBlock, NoopCfg}; + +mod zenith; diff --git a/src/fill/noop.rs b/src/fill/noop.rs new file mode 100644 index 0000000..8494275 --- /dev/null +++ b/src/fill/noop.rs @@ -0,0 +1,19 @@ +use revm::primitives::{BlockEnv, CfgEnv}; + +use crate::{Block, Cfg}; + +/// A no-op block filler. +#[derive(Debug)] +pub struct NoopBlock; + +impl Block for NoopBlock { + fn fill_block_env(&self, _: &mut BlockEnv) {} +} + +/// A no-op configuration filler. +#[derive(Debug)] +pub struct NoopCfg; + +impl Cfg for NoopCfg { + fn fill_cfg_env(&self, _: &mut CfgEnv) {} +} diff --git a/src/fill/traits.rs b/src/fill/traits.rs new file mode 100644 index 0000000..eedcf9e --- /dev/null +++ b/src/fill/traits.rs @@ -0,0 +1,121 @@ +use revm::{ + primitives::{BlockEnv, CfgEnv, TxEnv}, + Database, Evm, +}; + +/// Types that can fill the EVM transaction environment [`TxEnv`]. +pub trait Tx { + /// Fill the transaction environment. + /// + /// ## Note: + /// + /// It is generally expected that the filler will fill (or at least + /// inspect) ALL fields of the transaction environment. This is because + /// the EVM will NOT clear the transaction environment between + /// transactions. If the filler does not fill a field, it will be left + /// unchanged from the previous transaction. + fn fill_tx_env(&self, tx_env: &mut TxEnv); + + /// Fill the transaction environment on the EVM. + fn fill_tx(&self, evm: &mut Evm<'_, Ext, Db>) { + let tx_env: &mut TxEnv = evm.tx_mut(); + self.fill_tx_env(tx_env); + } +} +/// Types that can fill the EVM block environment [`BlockEnv`]. +pub trait Block { + /// Fill the block environment. + /// + /// ## Note: + /// + /// It is generally expected that the filler will fill (or at least + /// inspect) ALL fields of the block environment. This is because the EVM + /// will NOT clear the block environment between blocks. If the filler does + /// not fill a field, it will be left unchanged from the previous block. + fn fill_block_env(&self, block_env: &mut BlockEnv); + + /// Fill the block environment on the EVM. + fn fill_block(&self, evm: &mut Evm<'_, Ext, Db>) { + let block_env: &mut BlockEnv = evm.block_mut(); + self.fill_block_env(block_env); + } + + /// Get the transaction count hint from the filler. This can be used for + /// memory pre-allocation during block execution. + fn tx_count_hint(&self) -> Option { + None + } +} + +/// Types that can fill the EVM configuration environment [`CfgEnv`]. +/// +/// Because the CFG env has quite a few conditionally compiled properties, it +/// is recommended to use the default implementation of `fill_cfg_env` to ensure +/// that fields are filled only when appropriate. +/// +/// Note that the following properties on [`CfgEnv`] are feature-gated: +/// - `kzg_settings` - gated by `c-kzg` +/// - `memory_limit` - gated by `memory_limit` +/// - `disable_balance_check` - gated by `optional_balance_check` +/// - `disable_block_gas_limit` - gated by `optional_block_gas_limit` +/// - `disable_eip3607` - gated by `optional_eip3607` +/// - `disable_gas_refund` - gated by `optional_gas_refund` +/// - `disable_base_fee` - gated by `optional_no_base_fee` +/// - `disable_beneficiary_reward` - gated by `optional_beneficiary_reward` +/// +/// Cfg fillers should consider these feature gates when implementing the trait. +pub trait Cfg { + /// Fill the configuration environment. + fn fill_cfg_env(&self, cfg_env: &mut CfgEnv); + + /// Fill the configuration environment on the EVM. + fn fill_cfg(&self, evm: &mut Evm<'_, Ext, Db>) { + let cfg_env: &mut CfgEnv = evm.cfg_mut(); + self.fill_cfg_env(cfg_env); + } +} + +#[cfg(test)] +mod test { + use alloy_consensus::constants::GWEI_TO_WEI; + use alloy_primitives::{B256, U256}; + use revm::primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv}; + + use super::*; + + impl Block for () { + fn fill_block_env(&self, block_env: &mut BlockEnv) { + let BlockEnv { + number, + coinbase, + timestamp, + gas_limit, + basefee, + difficulty, + prevrandao, + blob_excess_gas_and_price, + } = block_env; + *number = U256::from(1); + *coinbase = Default::default(); + *timestamp = U256::from(1720450148); // Time when I was writing the test code + *gas_limit = U256::from(30_000_000); + *basefee = U256::from(5 * GWEI_TO_WEI); + + let diff = B256::repeat_byte(0xab); + *prevrandao = Some(diff); + *difficulty = U256::from_be_bytes(diff.into()); + *blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(1_000_000)); + } + + fn tx_count_hint(&self) -> Option { + None + } + } + + impl Cfg for () { + fn fill_cfg_env(&self, cfg_env: &mut CfgEnv) { + let CfgEnv { chain_id, .. } = cfg_env; + *chain_id = 1; + } + } +} diff --git a/src/fill/zenith.rs b/src/fill/zenith.rs new file mode 100644 index 0000000..8c697cd --- /dev/null +++ b/src/fill/zenith.rs @@ -0,0 +1,43 @@ +use crate::Tx; +use alloy_primitives::U256; +use revm::primitives::{TransactTo, TxEnv}; +use zenith_types::Transactor; + +impl Tx for Transactor::Transact { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + // destructuring here means that any changes to the fields will result + // in breaking changes here, ensuring that they never silently add new + // fields + let TxEnv { + caller, + gas_limit, + gas_price, + transact_to, + value, + data, + nonce, + chain_id, + access_list, + gas_priority_fee, + blob_hashes, + max_fee_per_blob_gas, + authorization_list, + } = tx_env; + + *caller = self.sender; + *gas_limit = self.gas.as_limbs()[0]; + *gas_price = self.maxFeePerGas; + *gas_priority_fee = Some(U256::ZERO); + *transact_to = TransactTo::Call(self.to); + *value = self.value; + *data = self.data.clone(); + *chain_id = Some(self.rollup_chain_id()); + // This causes nonce validation to be skipped. i.e. the Transact event + // will always use the next available nonce + *nonce = None; + *access_list = vec![]; + blob_hashes.clear(); + max_fee_per_blob_gas.take(); + authorization_list.take(); + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..a8262fc --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,443 @@ +//! [`Trevm`] - a typestate interface to [`revm`]. +//! +//! Tevm provides a safe and low-overhead way to interact with the EVM. It is +//! based on the [typestate pattern], which allows the compiler to enforce +//! correct usage of the EVM. +//! +//! Tevm is NOT an EVM implementation. It is a thin wrapper around the EVM +//! provided by [`revm`]. +//! +//! [`Trevm`] models the EVM as a state machine with the following states: +//! +//! - [`EvmNeedsCfg`]: The EVM needs to be configured. +//! - [`EvmNeedsFirstBlock`]: The EVM is configured and needs a block +//! environment. +//! - [`EvmNeedsTx`]: The EVM is configured and has a block environment, and +//! now needs a transaction to execute. +//! - [`EvmReady`]: The EVM has a transaction loaded and is ready to execute it. +//! - [`EvmErrored`]: The EVM has executed a transaction and encountered an +//! error. +//! - [`EvmTransacted`]: The EVM has executed a transaction successfully. +//! - [`EvmBlockComplete`]: The EVM has executed and closed a block and contains +//! some a populated [`BlockContext`] object +//! - [`EvmNeedsNextBlock`]: The EVM has executed a transaction (or several +//! transactions) successfully and the block has been closed. The EVM is now +//! ready to open the next block, or to yield its outputs. +//! +//! ## Quickstart +//! +//! To get started, use a [`revm::EvmBuilder`] to configure the EVM, then call +//! [`TrevmBuilder::build_trevm`] to construct the [`Trevm`] instance. From +//! there, you should do the following: +//! +//! - Fill a Cfg by calling [`Trevm::fill_cfg`] with a [`Cfg`]. +//! - Open a block by calling [`Trevm::open_block`] with a [`Block`]. +//! - Fill a Tx by calling [`Trevm::fill_tx`] with a [`Tx`]. +//! - Run the transaction by calling [`Trevm::run_tx`]. +//! - Handle the result by calling [`EvmTransacted::accept`] or +//! [`EvmTransacted::reject`]. +//! - Call [`Trevm::close_block`] to close the block. +//! - Call[ [`Trevm::take_context`] to get the context object or +//! [`Trevm::discard_context`] to drop it. +//! - Then call [`Trevm::finish`] to get the outputs. +//! +//! +//! ### Running a transaction +//! +//! Running transactions is simple with Trevm. Here's a basic example: +//! +//! ``` +//! use revm::{EvmBuilder, db::InMemoryDB}; +//! use trevm::{TrevmBuilder, EvmErrored, Cfg, Block, Tx, }; +//! +//! # fn t(cfg: &C, block: &B, tx: &T) +//! # -> Result<(), Box> { +//! EvmBuilder::default() +//! .with_db(InMemoryDB::default()) +//! .build_trevm() +//! .fill_cfg(cfg) +//! .fill_block(block) +//! .run_tx(tx); +//! # Ok(()) +//! # } +//! ``` +//! If you get stuck, don't worry! You _cannot_ invoke the wrong function or +//! mess up the inner state unless you access a method marked `_unchecked`. +//! While the states and generics may seem intimidating at first, they fade +//! into the background when you start writing your application. +//! +//! ## Writing an application +//! +//! When writing your code, we strongly recommend using the `Evm____` type +//! aliases to simplify your code. +//! +//! We also recommend defining concrete types for `Ext` and `Db` whenever +//! possible, to simplify your code and remove bounds. Most users will want +//! `()` for `Ext`, unless specifically using an inspector or a customized EVM. +//! +//! To help you use concrete types, we provide the [`trevm_aliases`] macro. This +//! macro generates type aliases for the Trevm states with a concrete `Ext` and +//! +//! ``` +//! use trevm::trevm_aliases; +//! use revm::db::InMemoryDB; +//! +//! // produces types that look like this: +//! // type EvmNeedsCfg = trevm::EvmNeedsCfg<'static, (), InMemoryDB>; +//! trevm_aliases!(revm::db::InMemoryDB); +//! ``` +//! +//! ### [`BlockContext`] +//! +//! Trevm handles transaction application, receipts, and pre- and post-block +//! logic through the [`BlockContext`] trait. The [`BlockContext`] trait is +//! invoked by [`Trevm`] to manage the lifecycle of a single block. At the start +//! of a block, the context is opened with [`BlockContext::open_block`], and at +//! the end of the block, the context is closed with +//! [`BlockContext::close_block`]. After each transaction, the context's +//! [`BlockContext::after_tx`] logic controls how and whether the execution +//! result is applied to the EVM state, and handles per-transaction logic like +//! generating receipts and tracking senders. +//! +//! Trevm provides a few block context implementations: +//! +//! - [`Shanghai`]: Shanghai context applies the post-block system +//! action (withdrawals) introduced by [EIP-4895]. +//! - [`Cancun`]: Cancun context applies the [`Shanghai`] +//! as well as the post-block logic introduced by [EIP-4788]. +//! - [`Prague`]: Prague context applies [`Shanghai`] and +//! [`Cancun`] as well as the pre-block logic of [EIP-2935], the +//! post-block logic introduced by [EIP-7002] and [EIP-7251], and the +//! withdrawal request accumulation logic introduced in [EIP-6110]. +//! - [`BasicContext`]: Basic context applies no pre- or post-block logic, and +//! is useful for testing or for applications that do not require the +//! real system state. +//! +//! Contexts before Shanghai are not currently implemented. In particular, +//! block and ommer rewards for pre-merge blocks are not implemented. +//! +//! The [`BlockContext`] trait methods take a mutable reference to allow the +//! context to accumulate information about the execution. This is useful for +//! accumulating receipts, senders, or other information that is more readily +//! available during execution. It is also useful for pre-block logic, where +//! the lifecycle may need to accumulate information about the block before the +//! first transaction is executed, and re-use that information to close the +//! block. It may also be used to compute statistics or indices that are only +//! available after the block is closed. +//! +//! ``` +//! # use revm::{EvmBuilder, db::InMemoryDB}; +//! # use trevm::{TrevmBuilder, EvmErrored, Cfg, Block, Tx, +//! # Shanghai, Cancun}; +//! # use alloy_primitives::B256; +//! # fn t(cfg: &C, block: &B, tx: &T) +//! # -> Result<(), Box> { +//! // Contexts manage the lifecycle of a single block +//! let mut context = Cancun::<'static> { +//! parent_beacon_root: B256::repeat_byte(0x42), +//! shanghai: Shanghai::default(), +//! }; +//! +//! let (context, trevm) = EvmBuilder::default() +//! .with_db(InMemoryDB::default()) +//! .build_trevm() +//! .fill_cfg(cfg) +//! // The pre-block logic is applied here +//! .open_block(block, context) +//! // Note that the logic is fallible, so we have to handle errors +//! .map_err(EvmErrored::into_error)? +//! .run_tx(tx) +//! .map_err(EvmErrored::into_error)? +//! .accept() +//! // Closing the block applies the post-block logic, and is also fallible +//! .close_block() +//! .map_err(EvmErrored::into_error)? +//! // This splits the context, and puts trevm into `EvmNeedsNextBlock` +//! .take_context(); +//! # Ok(()) +//! # } +//! ``` +//! +//! ### Handling execution errors +//! +//! Trevm uses the [`EvmErrored`] state to handle errors during transaction +//! execution. This type is a wrapper around the error that occurred, and +//! provides a method to discard the error and continue execution. This is +//! useful when you want to continue executing transactions even if one fails. +//! +//! Usually, errors will be [`EVMError`], but [`BlockContext`] +//! implementations may return other errors. The [`EvmErrored`] type is +//! generic over the error type, so you can use it with any error type. +//! +//! ``` +//! # use revm::{EvmBuilder, db::InMemoryDB}; +//! # use trevm::{TrevmBuilder, EvmErrored, Cfg, Block, Tx, +//! # Shanghai, Cancun}; +//! # use alloy_primitives::B256; +//! # fn t(cfg: &C, block: &B, tx: &T) +//! # -> Result<(), Box> { +//! // Contexts manage the lifecycle of a single block +//! let mut context = Cancun::<'static> { +//! parent_beacon_root: B256::repeat_byte(0x42), +//! shanghai: Shanghai::default(), +//! }; +//! +//! match EvmBuilder::default() +//! .with_db(InMemoryDB::default()) +//! .build_trevm() +//! .fill_cfg(cfg) +//! // The pre-block logic is applied here +//! .open_block(block, context) { +//! Ok(trevm) => { +//! trevm +//! } +//! Err(transacted_error) => { +//! let (trevm, error) = transacted_error.take_error(); +//! // Handle the error here, and return the EVM if you want +//! // to keep going +//! trevm +//! } +//! }; +//! # Ok(()) +//! # } +//! ``` +//! +//! ### Extending Trevm +//! +//! Trevm has a few extension points: +//! +//! - Build the [`revm::Evm`] with a [`revm::Inspector`] and use it in trevm. +//! - Implement the [`PostTx`] trait to apply post-transaction logic/changes. +//! - Implement your own [`Cfg`], [`Block`], and +//! [`Tx`] to fill the EVM from your own data structures. +//! - Implement your own [`BlockContext`] to apply pre- and post-block logic, +//! use custom receipt types, or more. +//! +//! ``` +//! # use trevm::Tx; +//! # use alloy_primitives::Address; +//! // You can implement your own Tx to fill the EVM environment with your own +//! // data. +//! pub struct MyTx; +//! +//! impl Tx for MyTx { +//! fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { +//! // fill the tx_env with your data +//! // we recommend destructuring here to safeguard against future changes +//! // to the TxEnv struct +//! let revm::primitives::TxEnv { +//! caller, +//! .. +//! } = tx_env; +//! *caller = Address::repeat_byte(0xde); +//! } +//! } +//! ``` +//! +//! ### Trevm feature flags +//! +//! Trevm passes through most feature flags from revm, the following are on by +//! default: +//! +//! - `c-kzg` - Enable KZG precompiles as specced for [EIP-4844]. +//! - `blst` - Enable BLST precompiles as speced for [EIP-2537]. +//! - `portable` - Compiles BLST in portable mode. +//! - `secp256k1` - Use libsecp256k1 for ecrecover (default is k256). +//! +//! Cfg features: +//! - `memory_limit` - Allow users to limit callframe memory usage. +//! - `optional_balance_check` - Allow transacting with insufficient balance. +//! - `optional_block_gas_limit` - Allow transactions that exceed the block gas +//! limit. +//! - `optional_eip3607` - Allow transactions whose sender account has contract +//! code. +//! - `optional_gas_refund` - Allow disabling gas refunds, as in Avalanche. +//! - `optional_no_base_fee` - Allow disabling basefee checks. +//! - `optional_beneficiary_reward` - Allow disabling fees and rewards paid to +//! the block beneficiary. +//! +//! - `dev` - Enable all Cfg features. +//! +//! Trevm also provides the following: +//! +//! - `test-utils` - activates revm's `test-utils` feature, and provides +//! convenience functions for instantiating [`Trevm`] with an in-memory DB. +//! +//! ### Testing using Trevm +//! +//! Trevm provides a `test-utils` module for easy testing. The test-utils gives +//! you access to an in-memory clean-slate [`Trevm`] instance, as well as tools +//! for directly modifying the state of the EVM. +//! +//! During testing you can use +//! - set balance, nonce, codehash for any account +//! - a single-function setup for a blank EVM +//! - pre-funding for any number of accounts +//! ## Understanding the state machine +//! +//! Here's an example, with states written out: +//! +//! ``` +//! # use revm::{EvmBuilder, db::{InMemoryDB, BundleState}, State, +//! # StateBuilder}; +//! # use trevm::{TrevmBuilder, EvmErrored, Cfg, Block, Tx, BlockOutput, +//! # EvmNeedsCfg, EvmNeedsFirstBlock, EvmNeedsTx, EvmReady, EvmNeedsNextBlock, +//! # EvmBlockComplete, Shanghai, EvmTransacted}; +//! # fn t(cfg: &C, block: &B, tx: &T) +//! # -> Result<(), Box> { +//! let state = StateBuilder::new_with_database(InMemoryDB::default()).build(); +//! +//! // Trevm starts in `EvmNeedsCfg`. +//! let trevm: EvmNeedsCfg<'_, _, _> = EvmBuilder::default() +//! .with_db(state) +//! .build_trevm(); +//! +//! // Once the cfg is filled, we move to `EvmNeedsFirstBlock`. +//! let trevm: EvmNeedsFirstBlock<'_, _, _> = trevm.fill_cfg(cfg); +//! +//! // Filling the block gets us to `EvmNeedsTx`. `open_block` takes a +//! // context object that will apply pre- and post-block logic, accumulate +//! // receipts, and perform other lifecycle tasks. +//! let trevm: EvmNeedsTx<'_, _, _, _> = trevm.open_block( +//! block, +//! Shanghai::default() +//! ).map_err(EvmErrored::into_error)?; +//! // Filling the tx gets us to `EvmReady`. +//! let trevm: EvmReady<'_, _, _, _> = trevm.fill_tx(tx); +//! +//! let res: Result< +//! EvmTransacted<'_, _, _, _>, +//! EvmErrored<'_, _, _, _>, +//! > = trevm.run(); +//! +//! +//! // Applying the tx or ignoring the error gets us back to `EvmNeedsTx`. +//! // You could also make additional checks and discard the success result here +//! let trevm: EvmNeedsTx<'_, _, _, _> = match res { +//! Ok(trevm) => trevm.accept(), +//! Err(e) => e.discard_error(), +//! }; +//! +//! // Clearing or closing a block gets us to `EvmNeedsNextBlock`, ready for the +//! // next block. +//! let trevm: EvmBlockComplete<'_, _, _, _> = trevm +//! .close_block() +//! .map_err(EvmErrored::into_error)?;; +//! +//! // During block execution, a context object +//! let (context, trevm): (Shanghai<'_>, EvmNeedsNextBlock<'_, _, _>) = trevm +//! .take_context(); +//! +//! // Finishing the EVM gets us the final changes and a list of block outputs +//! // that includes the transaction receipts. +//! let bundle: BundleState = trevm.finish(); +//! # Ok(()) +//! # } +//! ``` +//! +//! ## Happy Path Loop +//! +//! The most simple, straightforward application of Tevm is applying a +//! set of transaction to the EVM. This is done by following : +//! +//! ```none +//! +-----+ +-----------+ +//! |Start| ---> |EvmNeedsCfg| +//! +-----+ +-----------+ +//! | +--------------------+ +//! +-- fill_cfg() -------> | EvmNeedsFirstBlock | +//! +--------------------+ +//! | +//! +----------------+ | +//! +-- |EvmBlockComplete|--take_context()-+ | +//! | +----------------+ or discard | | +//! | ^ V | +//! | | +-----------------+ | +//! finish() | |EvmNeedsNextBlock|------+ +//! | close_block() +-----------------+ | +//! V | | +//! +------+ +----------+ | +//! |Finish| |EvmNeedsTx| <------ open_block() -----------+ +//! +------+ +----------+ +//! ^ | +--------+ +//! | +------- fill_tx() -------> |EvmReady|--+ +//! | +--------+ | +//! | +-------------+ | +//! +- accept() --|EvmTransacted| <-- run_tx() -----+ +//! or reject() +-------------+ +//! ``` +//! +//! A basic block loop should look like this: +//! +//! ```no_compile +//! let mut evm = evm +//! .fill_cfg(cfg); +//! .open_block(block, pre_block_logic); +//! +//! for tx in txs { +//! // apply the transaction, discard the error if any +//! evm = match evm.run_tx(tx); +//! } +//! +//! // close out the EVM, getting the final state +//! let (bundle, outputs) = evm.close_block(block, post_block_logic).finish(); +//! ``` +//! +//! [`EVMError`]: revm::primitives::EVMError +//! [typestate pattern]: https://cliffle.com/blog/rust-typestate/ +//! [crate readme]: https://github.com/init4tt/trevm +//! [EIP-2537]: https://eips.ethereum.org/EIPS/eip-2537 +//! [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844 +//! [EIP-4895]: https://eips.ethereum.org/EIPS/eip-4895 +//! [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 +//! [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 + +#![doc( + html_logo_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/alloy.jpg", + html_favicon_url = "https://raw.githubusercontent.com/alloy-rs/core/main/assets/favicon.ico" +)] +#![cfg_attr(not(test), warn(unused_crate_dependencies))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] + +mod evm; +pub use evm::Trevm; + +mod fill; +pub use fill::{Block, Cfg, NoopBlock, NoopCfg, Tx}; + +mod lifecycle; +pub use lifecycle::{ + BasicContext, BlockContext, BlockOutput, Cancun, PostTx, PostflightResult, Prague, Shanghai, +}; + +mod states; +pub(crate) use states::sealed::*; +pub use states::{ + EvmBlockComplete, EvmErrored, EvmNeedsCfg, EvmNeedsFirstBlock, EvmNeedsNextBlock, EvmNeedsTx, + EvmReady, EvmTransacted, +}; + +pub mod syscall; + +pub use revm; + +/// Utilities for testing Trevm or testing with Trevm. +#[cfg(feature = "test-utils")] +pub mod test_utils; + +use revm::{Database, DatabaseCommit, EvmBuilder}; + +/// Ext trait for [`EvmBuilder`] that builds a [`Trevm`]. +pub trait TrevmBuilder<'a, Ext, Db: Database + DatabaseCommit> { + /// Builds the [`Trevm`]. + fn build_trevm(self) -> EvmNeedsCfg<'a, Ext, Db>; +} + +impl<'a, Stage, Ext, Db: Database + DatabaseCommit> TrevmBuilder<'a, Ext, Db> + for EvmBuilder<'a, Stage, Ext, Db> +{ + /// Builds the [`Trevm`]. + fn build_trevm(self) -> EvmNeedsCfg<'a, Ext, Db> { + Trevm::from(self.build()) + } +} diff --git a/src/lifecycle/contexts.rs b/src/lifecycle/contexts.rs new file mode 100644 index 0000000..b6514dc --- /dev/null +++ b/src/lifecycle/contexts.rs @@ -0,0 +1,790 @@ +use crate::{ + syscall::{ + eip6110, eip7002::WITHDRAWAL_REQUEST_BYTES, eip7251::CONSOLIDATION_REQUEST_BYTES, + execute_system_tx, SystemTx, + }, + Block, BlockContext, BlockOutput, +}; +use alloy_consensus::{Receipt, ReceiptEnvelope, Request, TxReceipt}; +use alloy_eips::{ + eip2935::{HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_CODE}, + eip4895::Withdrawal, + eip7002::WithdrawalRequest, + eip7251::ConsolidationRequest, +}; +use alloy_primitives::{Bloom, Log, B256, U256}; +use alloy_sol_types::SolEvent; +use revm::{ + primitives::{ + Account, AccountInfo, AccountStatus, Bytecode, EVMError, EvmStorageSlot, ExecutionResult, + ResultAndState, SpecId, BLOCKHASH_SERVE_WINDOW, + }, + Database, DatabaseCommit, Evm, +}; +use std::{collections::HashMap, sync::OnceLock}; + +/// A context that performs the fewest meaingful actions. Specifically, it +/// fills the block, and applies transactions to the EVM db. It does not apply +/// any pre- or post-block actions. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +pub struct BasicContext; + +impl BlockContext for BasicContext { + type Error = EVMError; + + fn open_block( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + b: &B, + ) -> Result<(), Self::Error> { + b.fill_block(evm); + Ok(()) + } + + fn after_tx(&mut self, evm: &mut Evm<'_, Ext, Db>, result: revm::primitives::ResultAndState) { + evm.db_mut().commit(result.state); + } + + fn close_block(&mut self, _evm: &mut Evm<'_, Ext, Db>) -> Result<(), Self::Error> { + Ok(()) + } +} + +/// Shanghai lifecycle. This applies the [EIP-4895] post-block system action. +/// +/// [EIP-4895]: https://eips.ethereum.org/EIPS/eip-4895 +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct Shanghai<'a> { + /// The withdrawals to be processed. + pub withdrawals: &'a [Withdrawal], + + /// The block outputs. + outputs: BlockOutput, +} + +impl<'a> From<&'a [Withdrawal]> for Shanghai<'a> { + fn from(withdrawals: &'a [Withdrawal]) -> Self { + Self { withdrawals, outputs: Default::default() } + } +} + +impl BlockContext for Shanghai<'_> { + type Error = EVMError; + + fn open_block( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + b: &B, + ) -> Result<(), Self::Error> { + if let Some(hint) = b.tx_count_hint() { + self.outputs.reserve(hint); + } + b.fill_block(evm); + Ok(()) + } + + fn after_tx(&mut self, evm: &mut Evm<'_, Ext, Db>, result: ResultAndState) { + let sender = evm.tx().caller; + + let receipt = self.make_receipt(result.result).into(); + + let tx_env = evm.tx(); + + // Determine the receipt envelope type based on the transaction type. + let receipt = if !tx_env.blob_hashes.is_empty() { + ReceiptEnvelope::Eip4844(receipt) + } else if tx_env.gas_priority_fee.is_some() { + ReceiptEnvelope::Eip1559(receipt) + } else if !tx_env.access_list.is_empty() { + ReceiptEnvelope::Eip2930(receipt) + } else { + ReceiptEnvelope::Legacy(receipt) + }; + + evm.db_mut().commit(result.state); + self.outputs.push_result(receipt, sender); + } + + fn close_block(&mut self, evm: &mut Evm<'_, Ext, Db>) -> Result<(), Self::Error> { + self.apply_withdrawals(evm)?; + Ok(()) + } +} + +impl<'a> Shanghai<'a> { + /// Instantiate a new Shanghai context. + pub fn new(withdrawals: &'a [Withdrawal]) -> Self { + Self { withdrawals, outputs: Default::default() } + } + + /// Destroy the Shanghai context and return the withdrawals and block + /// outputs. + pub fn into_parts(self) -> (&'a [Withdrawal], BlockOutput) { + (self.withdrawals, self.outputs) + } +} + +impl Shanghai<'_> { + /// Cumulative gas used in the block so far. + pub fn cumulative_gas_used(&self) -> u128 { + self.outputs.cumulative_gas_used() + } + + /// Make a receipt from the execution result. + pub fn make_receipt(&self, result: ExecutionResult) -> Receipt { + let cumulative_gas_used = + self.cumulative_gas_used().saturating_add(result.gas_used() as u128); + Receipt { + status: result.is_success().into(), + cumulative_gas_used, + logs: result.into_logs(), + } + } + + /// Apply the withdrawals to the EVM state. + pub fn apply_withdrawals( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + ) -> Result<(), >::Error> + where + Db: Database + DatabaseCommit, + { + // We need to apply the withdrawals by incrementing the balances of the + // respective accounts, then committing the changes to the database. + let mut changes = HashMap::new(); + + let increments = self + .withdrawals + .iter() + .map(|withdrawal| (withdrawal.address, withdrawal.amount as u128)) + .filter(|(_, amount)| *amount != 0); + + for (address, amount) in increments { + let mut info = match evm.db_mut().basic(address) { + Ok(account) => account.unwrap_or_default(), + Err(error) => return Err(EVMError::Database(error)), + }; + info.balance = info.balance.saturating_add(U256::from(amount)); + changes.insert( + address, + Account { info, status: AccountStatus::Touched, ..Default::default() }, + ); + } + evm.db_mut().commit(changes); + + Ok(()) + } + + /// Get the block outputs. This contains receipts and senders. + pub const fn outputs(&self) -> &BlockOutput { + &self.outputs + } + + /// Get the receipts produced in the block. + pub fn receipts(&self) -> &[ReceiptEnvelope] { + self.outputs.receipts() + } + + /// Get the logs produced in the block. + pub fn logs(&self) -> impl Iterator { + self.outputs.logs() + } + + /// Calculate the bloom filter for the block. + pub fn bloom(&self) -> &Bloom { + static BLOOM: OnceLock = OnceLock::new(); + + BLOOM.get_or_init(|| { + let mut bloom: Bloom = Bloom::default(); + self.outputs.receipts().iter().for_each(|r| bloom.accrue_bloom(&r.bloom())); + bloom + }) + } +} + +/// Cancun lifecycle. This applies the [EIP-4788] pre-block beacon root update +/// system transact action, as well withdrawals via the [`Shanghai`] context. +/// +/// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Cancun<'a> { + /// The parent beacon root, for the [EIP-4788] pre-block system action. + /// + /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 + pub parent_beacon_root: B256, + + /// The Cancun lifecycle is a superset of the Shanghai lifecycle. + pub shanghai: Shanghai<'a>, +} + +impl<'a> std::ops::Deref for Cancun<'a> { + type Target = Shanghai<'a>; + + fn deref(&self) -> &Self::Target { + &self.shanghai + } +} + +impl<'a> std::ops::DerefMut for Cancun<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.shanghai + } +} + +impl BlockContext for Cancun<'_> { + type Error = EVMError; + + fn open_block( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + b: &B, + ) -> Result<(), Self::Error> { + self.shanghai.open_block(evm, b)?; + self.apply_eip4788(evm) + } + + fn after_tx(&mut self, evm: &mut Evm<'_, Ext, Db>, result: ResultAndState) { + self.shanghai.after_tx(evm, result) + } + + fn close_block(&mut self, evm: &mut Evm<'_, Ext, Db>) -> Result<(), Self::Error> { + self.shanghai.close_block(evm) + } +} + +impl<'a> Cancun<'a> { + /// Create a new Cancun context. + pub const fn new(parent_beacon_root: B256, shanghai: Shanghai<'a>) -> Self { + Self { parent_beacon_root, shanghai } + } +} + +impl Cancun<'_> { + /// Apply a system transaction as specified in [EIP-4788]. The EIP-4788 + /// pre-block action was introduced in Cancun, and calls the beacon root + /// contract to update the historical beacon root. + /// + /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 + pub fn apply_eip4788( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + ) -> Result<(), >::Error> + where + Db: Database + DatabaseCommit, + { + if evm.spec_id() < SpecId::CANCUN { + return Ok(()); + } + execute_system_tx(evm, &SystemTx::eip4788(self.parent_beacon_root))?; + Ok(()) + } +} + +/// Prague block context. This applies withdrawals via the [Shanghai], +/// beacon root updates via the [`Cancun`] context, the [EIP-7002] post-block +/// withdrawal requests, and the [EIP-7251] post-block consolidation requests. +/// +/// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 +/// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Prague<'a> { + /// The Prague context is a superset of the [`Cancun`] context. + pub cancun: Cancun<'a>, + + /// Requests produced in the block. These include Deposit, Withdrawal, and + /// Consolidation requests. + requests: Vec, +} + +impl<'a> std::ops::Deref for Prague<'a> { + type Target = Cancun<'a>; + + fn deref(&self) -> &Self::Target { + &self.cancun + } +} + +impl<'a> std::ops::DerefMut for Prague<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.cancun + } +} + +impl<'a> From> for Prague<'a> { + fn from(cancun: Cancun<'a>) -> Self { + Self { cancun, requests: Vec::new() } + } +} + +impl BlockContext for Prague<'_> +where + Db: Database + DatabaseCommit, +{ + type Error = EVMError; + + fn open_block<'a, B: Block>( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + b: &B, + ) -> Result<(), Self::Error> { + self.cancun.open_block(evm, b)?; + Self::apply_eip2935(evm) + } + + fn after_tx<'a>(&mut self, evm: &mut Evm<'_, Ext, Db>, result: ResultAndState) { + // ordering is important here as the `find_deposit_logs` method relies + // on the receipt produced by the inner `after_tx` call. + self.cancun.after_tx(evm, result); + self.find_deposit_logs(); + } + + fn close_block<'a>(&mut self, evm: &mut Evm<'_, Ext, Db>) -> Result<(), Self::Error> { + self.apply_eip7002(evm)?; + self.apply_eip7251(evm)?; + self.cancun.close_block(evm) + } +} + +impl<'a> Prague<'a> { + /// Create a new Prague context. + pub fn new(cancun: Cancun<'a>) -> Self { + Self { cancun, requests: Vec::new() } + } + + /// Get the requests produced in the block. + pub fn requests(&self) -> &[Request] { + &self.requests + } + + /// Destructure the Prague context and return the Cancun context and the + /// requests produced during the block. + pub fn into_parts(self) -> (Cancun<'a>, Vec) { + (self.cancun, self.requests) + } +} + +impl Prague<'_> { + /// Finds deposit logs for the most recent receipt. + fn find_deposit_logs(&mut self) { + let receipt = + self.cancun.shanghai.outputs.receipts().last().expect("produced in shanghai lifecycle"); + + for log in receipt + .logs() + .iter() + .filter(|log| log.address == eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS) + { + // We assume that the log is valid because it was emitted by the + // deposit contract. + let decoded_log = eip6110::DepositEvent::decode_log(log, false).expect("invalid log"); + let deposit = eip6110::parse_deposit_from_log(&decoded_log); + self.requests.push(Request::DepositRequest(deposit)); + } + } + + /// Apply the pre-block logic for [EIP-2935]. This logic was introduced in + /// Prague and updates historical block hashes in a special system + /// contract. Unlike other system actions, this is NOT modeled as a + /// transaction. + /// + /// [EIP-2935]: https://eips.ethereum.org/EIPS/eip-2935 + pub fn apply_eip2935( + evm: &mut Evm<'_, Ext, Db>, + ) -> Result<(), >::Error> + where + Db: Database + DatabaseCommit, + { + if evm.spec_id() < SpecId::PRAGUE || evm.block().number.is_zero() { + return Ok(()); + } + + let mut account: Account = match evm.db_mut().basic(HISTORY_STORAGE_ADDRESS) { + Ok(Some(account)) => account, + Ok(None) => AccountInfo { + nonce: 1, + code: Some(Bytecode::new_raw(HISTORY_STORAGE_CODE.clone())), + ..Default::default() + }, + Err(error) => return Err(EVMError::Database(error)), + } + .into(); + + let block_num = evm.block().number.as_limbs()[0]; + let prev_block = block_num.saturating_sub(1); + + // Update the EVM state with the new value. + { + let slot = U256::from(block_num % BLOCKHASH_SERVE_WINDOW as u64); + let current_hash = match evm.db_mut().storage(HISTORY_STORAGE_ADDRESS, slot) { + Ok(current_hash) => current_hash, + Err(error) => return Err(EVMError::Database(error)), + }; + let parent_block_hash = match evm.db_mut().block_hash(prev_block) { + Ok(parent_block_hash) => parent_block_hash, + Err(error) => return Err(EVMError::Database(error)), + }; + + // Insert the state change for the slot + let value = EvmStorageSlot::new_changed(current_hash, parent_block_hash.into()); + + account.storage.insert(slot, value); + } + + // Mark the account as touched and commit the state change + account.mark_touch(); + evm.db_mut().commit(HashMap::from([(HISTORY_STORAGE_ADDRESS, account)])); + + Ok(()) + } + + /// Apply a system transaction as specified in [EIP-7002]. The EIP-7002 + /// post-block action was introduced in Prague, and calls the withdrawal + /// request contract to process withdrawal requests. + /// + /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 + pub fn apply_eip7002( + &mut self, + + evm: &mut Evm<'_, Ext, Db>, + ) -> Result<(), >::Error> + where + Db: Database + DatabaseCommit, + { + if evm.spec_id() < SpecId::PRAGUE { + return Ok(()); + } + let res = execute_system_tx(evm, &SystemTx::eip7002())?; + + // We make assumptions here: + // - The system transaction never reverts. + // - The system transaction always has an output. + // - The system contract produces correct output. + // - The output is a list of withdrawal requests. + // - The output does not contain incomplete requests. + + let Some(output) = res.output() else { + panic!("execution halted during withdrawal request system contract execution") + }; + self.requests.extend(output.chunks_exact(WITHDRAWAL_REQUEST_BYTES).map(|chunk| { + let mut req: WithdrawalRequest = Default::default(); + + req.source_address.copy_from_slice(&chunk[0..20]); + req.validator_pubkey.copy_from_slice(&chunk[20..68]); + req.amount = u64::from_be_bytes(chunk[68..76].try_into().unwrap()); + + Request::WithdrawalRequest(req) + })); + + Ok(()) + } + + /// Apply a system transaction as specified in [EIP-7251]. The EIP-7251 + /// post-block action calls the consolidation request contract to process + /// consolidation requests. + /// + /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 + pub fn apply_eip7251( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + ) -> Result<(), >::Error> + where + Db: Database + DatabaseCommit, + { + if evm.spec_id() < SpecId::PRAGUE { + return Ok(()); + } + let res = execute_system_tx(evm, &SystemTx::eip7251())?; + + // We make assumptions here: + // - The system transaction never reverts. + // - The system transaction always has an output. + // - The system contract produces correct output. + // - The output is a list of consolidation requests. + // - The output does not contain incomplete requests. + + let Some(output) = res.output() else { panic!("no output") }; + self.requests.extend(output.chunks_exact(CONSOLIDATION_REQUEST_BYTES).map(|chunk| { + let mut req: ConsolidationRequest = Default::default(); + + req.source_address.copy_from_slice(&chunk[0..20]); + req.source_pubkey.copy_from_slice(&chunk[20..68]); + req.target_pubkey.copy_from_slice(&chunk[68..116]); + + Request::ConsolidationRequest(req) + })); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{syscall::eip4788::BEACON_ROOTS_ADDRESS, NoopBlock, NoopCfg, TrevmBuilder, Tx}; + use alloy_consensus::constants::{ETH_TO_WEI, GWEI_TO_WEI}; + use alloy_eips::{ + eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, + eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, + }; + use alloy_primitives::{fixed_bytes, Address, Bytes, FixedBytes, TxKind, U256}; + + use revm::{Evm, InMemoryDB}; + + const WITHDRAWALS: &[Withdrawal] = &[Withdrawal { + validator_index: 1, + index: 1, + address: Address::with_last_byte(0x69), + amount: 100 * GWEI_TO_WEI, + }]; + + // Withdrawal tx + const WITHDRAWAL_ADDR: Address = Address::with_last_byte(0x42); + const WITHDRAWAL_AMOUNT: FixedBytes<8> = fixed_bytes!("2222222222222222"); + const VALIDATOR_PUBKEY: FixedBytes<48> = fixed_bytes!("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); + const TARGET_VALIDATOR_PUBKEY: FixedBytes<48> = fixed_bytes!("111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); + + // TODO: Remove once https://github.com/alloy-rs/alloy/issues/1103 is resolved. + const CONSOLIDATION_REQUEST_PREDEPLOY_CODE: &str = "3373fffffffffffffffffffffffffffffffffffffffe146098573615156028575f545f5260205ff35b36606014156101445760115f54600182026001905f5b5f82111560595781019083028483029004916001019190603e565b90939004341061014457600154600101600155600354806004026004013381556001015f35815560010160203581556001016040359055600101600355005b6003546002548082038060011160ac575060015b5f5b81811460f15780607402838201600402600401805490600101805490600101805490600101549260601b84529083601401528260340152906054015260010160ae565b9101809214610103579060025561010e565b90505f6002555f6003555b5f548061049d141561011d57505f5b6001546001828201116101325750505f610138565b01600190035b5f555f6001556074025ff35b5f5ffd"; + + struct WithdrawalTx; + + impl Tx for WithdrawalTx { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + // https://github.com/lightclient/7002asm/blob/e0d68e04d15f25057af7b6d180423d94b6b3bdb3/test/Contract.t.sol.in#L49-L64 + let input: Bytes = [&VALIDATOR_PUBKEY[..], &WITHDRAWAL_AMOUNT[..]].concat().into(); + + tx_env.caller = WITHDRAWAL_ADDR; + tx_env.data = input; + tx_env.transact_to = TxKind::Call(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS); + // `MIN_WITHDRAWAL_REQUEST_FEE` + tx_env.value = U256::from(1); + } + } + + struct ConsolidationTx; + + impl Tx for ConsolidationTx { + fn fill_tx_env(&self, tx_env: &mut revm::primitives::TxEnv) { + let input: Bytes = + [&VALIDATOR_PUBKEY[..], &TARGET_VALIDATOR_PUBKEY[..]].concat().into(); + + tx_env.caller = WITHDRAWAL_ADDR; + tx_env.data = input; + tx_env.transact_to = TxKind::Call(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS); + // `MIN_CONSOLIDATION_REQUEST_FEE` + tx_env.value = U256::from(1); + } + } + + fn get_shanghai_context<'a>() -> Shanghai<'a> { + let outputs = BlockOutput::default(); + let mut withdrawals = Vec::new(); + + let withdrawal = Withdrawal { + validator_index: 1, + index: 1, + address: Address::with_last_byte(0x69), + amount: 100 * GWEI_TO_WEI, + }; + + withdrawals.push(withdrawal); + + Shanghai { withdrawals: WITHDRAWALS, outputs } + } + + #[test] + fn test_shanghai_syscall() { + let mut db = InMemoryDB::default(); + + db.insert_account_info( + Address::with_last_byte(0x69), + AccountInfo { + balance: U256::ZERO, + nonce: 1, + code: None, + code_hash: Default::default(), + }, + ); + + let shanghai = get_shanghai_context(); + + let balance = Evm::builder() + .with_db(db) + .build_trevm() + .fill_cfg(&NoopCfg) + .open_block(&NoopBlock, shanghai) + .unwrap() + .close_block() + .unwrap() + .read_balance(Address::with_last_byte(0x69)); + + assert_eq!(balance, U256::from(100 * GWEI_TO_WEI)); + } + + #[test] + fn test_cancun_syscall() { + // Pre-cancun setup (Shanghai) + let mut db = InMemoryDB::default(); + + db.insert_account_info( + Address::with_last_byte(0x69), + AccountInfo { + balance: U256::ZERO, + nonce: 1, + code: None, + code_hash: Default::default(), + }, + ); + + let shanghai = get_shanghai_context(); + + // Set up cancun + // 1. Insert the beacon root contract into the EVM + let bytecode = Bytecode::new_raw(alloy_eips::eip4788::BEACON_ROOTS_CODE.clone()); + let mut beacon_roots_info = AccountInfo { + nonce: 1, + code_hash: bytecode.hash_slow(), + code: Some(bytecode), + ..Default::default() + }; + + db.insert_contract(&mut beacon_roots_info); + db.insert_account_info(BEACON_ROOTS_ADDRESS, beacon_roots_info); + + // 2. Set up the Cancun context, by loading the parent beacon root, + // which we expect to be 0x21 (33). + let expected_beacon_root = B256::with_last_byte(0x21); + let cancun = Cancun { parent_beacon_root: expected_beacon_root, shanghai }; + + // We expect the root to be stored at timestamp % HISTORY_BUFFER_LENGTH + HISTORY_BUFFER_LENGTH. + // In this case, 0 % 8192 + 8192 = 8192. quick maffs + let expected_beacon_root_slot = U256::from(8192); + + let stored_beacon_root = Evm::builder() + .with_db(db) + .build_trevm() + .fill_cfg(&NoopCfg) + .open_block(&NoopBlock, cancun) + .unwrap() + .close_block() + .unwrap() + .read_storage(BEACON_ROOTS_ADDRESS, expected_beacon_root_slot); + + assert_eq!(stored_beacon_root, expected_beacon_root.into()); + } + + #[test] + fn test_prague_syscalls() { + // Pre-prague setup (Shanghai) + let mut db = InMemoryDB::default(); + + db.insert_account_info( + Address::with_last_byte(0x69), + AccountInfo { + balance: U256::ZERO, + nonce: 1, + code: None, + code_hash: Default::default(), + }, + ); + + let shanghai = get_shanghai_context(); + + // Pre-prague setup (Cancun) + // 1. Insert the beacon root contract into the EVM + let bytecode = Bytecode::new_raw(alloy_eips::eip4788::BEACON_ROOTS_CODE.clone()); + let mut beacon_roots_info = AccountInfo { + nonce: 1, + code_hash: bytecode.hash_slow(), + code: Some(bytecode), + ..Default::default() + }; + + db.insert_contract(&mut beacon_roots_info); + db.insert_account_info(BEACON_ROOTS_ADDRESS, beacon_roots_info); + + // 2. Set up the Cancun context, by loading the parent beacon root, + // which we expect to be 0x21 (33). + let expected_beacon_root = B256::with_last_byte(0x21); + let cancun = Cancun { parent_beacon_root: expected_beacon_root, shanghai }; + + // Prague setup + // 1. Set up EIP-7002 by inserting the withdrawals contract into the EVM + let bytecode = + Bytecode::new_raw(alloy_eips::eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone()); + let mut withdrawal_request_info = AccountInfo { + nonce: 1, + code_hash: bytecode.hash_slow(), + code: Some(bytecode), + ..Default::default() + }; + + db.insert_contract(&mut withdrawal_request_info); + db.insert_account_info( + alloy_eips::eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS, + withdrawal_request_info, + ); + + // 2. Insert the consolidation requests contract into the EVM + let bytecode = Bytecode::new_raw( + alloy_primitives::hex::decode(CONSOLIDATION_REQUEST_PREDEPLOY_CODE).unwrap().into(), + ); + let mut consolidation_request_info = AccountInfo { + nonce: 1, + code_hash: bytecode.hash_slow(), + code: Some(bytecode), + ..Default::default() + }; + + db.insert_contract(&mut consolidation_request_info); + db.insert_account_info(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, consolidation_request_info); + + // 3. Insert an user account, which will send a withdrawal and consolidation request. + let user_account_info = AccountInfo { + balance: U256::from(100 * ETH_TO_WEI), + nonce: 1, + code: None, + code_hash: Default::default(), + }; + + db.insert_account_info(WITHDRAWAL_ADDR, user_account_info); + + // 4. Set up the Prague context, by loading the parent beacon root, + let prague = Prague { cancun, requests: Vec::new() }; + + // 5. Set up the EVM along with the Prague context and transactions. + let evm = Evm::builder() + .with_db(db) + .with_spec_id(SpecId::PRAGUE) + .build_trevm() + .fill_cfg(&NoopCfg) + .open_block(&NoopBlock, prague) + .unwrap() + .fill_tx(&WithdrawalTx) + .run() + .unwrap() + .accept() + .fill_tx(&ConsolidationTx) + .run() + .unwrap() + .accept() + .close_block() + .unwrap(); + + let (prague, _) = evm.take_context(); + + // We should have 1 withdrawal request processed by 7002 and one consolidation request processed by 7251. + assert_eq!(2, prague.requests.len()); + + let withdrawal_request = prague.requests[0].as_withdrawal_request().unwrap(); + + assert_eq!(withdrawal_request.validator_pubkey, VALIDATOR_PUBKEY); + assert_eq!(withdrawal_request.source_address, WITHDRAWAL_ADDR); + + let consolidation_request = prague.requests[1].as_consolidation_request().unwrap(); + + assert_eq!(consolidation_request.source_address, WITHDRAWAL_ADDR); + assert_eq!(consolidation_request.source_pubkey, VALIDATOR_PUBKEY); + assert_eq!(consolidation_request.target_pubkey, TARGET_VALIDATOR_PUBKEY); + } +} diff --git a/src/lifecycle/mod.rs b/src/lifecycle/mod.rs new file mode 100644 index 0000000..37c05aa --- /dev/null +++ b/src/lifecycle/mod.rs @@ -0,0 +1,11 @@ +mod contexts; +pub use contexts::{BasicContext, Cancun, Prague, Shanghai}; + +mod output; +pub use output::BlockOutput; + +mod postflight; +pub use postflight::{PostTx, PostflightResult}; + +mod r#trait; +pub use r#trait::BlockContext; diff --git a/src/lifecycle/output.rs b/src/lifecycle/output.rs new file mode 100644 index 0000000..847b97d --- /dev/null +++ b/src/lifecycle/output.rs @@ -0,0 +1,75 @@ +use alloy_consensus::{ReceiptEnvelope, TxReceipt}; +use alloy_primitives::{Address, Log}; + +/// Information externalized during block execution. +/// +/// This struct is used to collect the results of executing a block of +/// transactions. It accumulates the receipts and senders of the transactions. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BlockOutput { + /// The receipts of the transactions in the block, in order. + receipts: Vec, + + /// The senders of the transactions in the block, in order. + senders: Vec
, +} + +impl Default for BlockOutput { + fn default() -> Self { + Self::with_capacity(0) + } +} + +impl BlockOutput { + /// Create a new block output with memory allocated to hold `capacity` + /// transaction outcomes. + pub fn with_capacity(capacity: usize) -> Self { + Self { receipts: Vec::with_capacity(capacity), senders: Vec::with_capacity(capacity) } + } + + /// Reserve memory for `capacity` transaction outcomes. + pub fn reserve(&mut self, capacity: usize) { + self.receipts.reserve(capacity); + self.senders.reserve(capacity); + } + + /// Get a reference to the receipts of the transactions in the block. + pub fn receipts(&self) -> &[T] { + &self.receipts + } + + /// Get an iterator over the logs of the transactions in the block. + pub fn logs(&self) -> impl Iterator { + self.receipts.iter().flat_map(|r| r.logs()) + } + + /// Get a reference the senders of the transactions in the block. + pub fn senders(&self) -> &[Address] { + &self.senders + } + + /// Get the cumulative gas used in the block. + pub fn cumulative_gas_used(&self) -> u128 { + self.receipts().last().map(TxReceipt::cumulative_gas_used).unwrap_or_default() + } + + /// Accumulate the result of a transaction execution. If `parse_deposits` is + /// true, the logs of the transaction will be scanned for deposit events + /// according to the [EIP-6110] specification. + /// + /// [EIP-6110]: https://eips.ethereum.org/EIPS/eip-6110 + pub fn push_result(&mut self, receipt: T, sender: Address) { + self.push_receipt(receipt); + self.push_sender(sender); + } + + /// Push a receipt onto the list of receipts. + fn push_receipt(&mut self, receipt: T) { + self.receipts.push(receipt); + } + + /// Push a sender onto the list of senders. + fn push_sender(&mut self, sender: Address) { + self.senders.push(sender); + } +} diff --git a/src/lifecycle/postflight.rs b/src/lifecycle/postflight.rs new file mode 100644 index 0000000..c5d804e --- /dev/null +++ b/src/lifecycle/postflight.rs @@ -0,0 +1,84 @@ +use revm::primitives::ResultAndState; + +/// Control flow for transaction execution. +/// +/// This enum is used to determine whether to apply or discard the state +/// changes after a transaction is executed. +#[derive(Debug, Clone, Copy)] +pub enum PostflightResult { + /// Discard the state changes + Discard(&'static str), + /// Apply the state changes + Apply, +} + +impl From for PostflightResult { + fn from(b: bool) -> Self { + if b { + Self::Apply + } else { + Self::Discard("") + } + } +} + +impl From<&'static str> for PostflightResult { + fn from(s: &'static str) -> Self { + Self::Discard(s) + } +} + +impl From> for PostflightResult { + fn from(s: Option<&'static str>) -> Self { + s.map(Self::Discard).unwrap_or(Self::Apply) + } +} + +impl PostflightResult { + /// Returns `true` if the result is `Discard`. + pub const fn is_discard(&self) -> bool { + matches!(self, Self::Discard(_)) + } + + /// Returns the discard reason if the result is `Discard`. + pub const fn as_discard_reason(&self) -> Option<&'static str> { + match self { + Self::Discard(reason) => Some(reason), + _ => None, + } + } + + /// Returns `true` if the result is `Apply`. + pub const fn is_apply(&self) -> bool { + matches!(self, Self::Apply) + } +} + +/// Discard the transaction if the condition is true, providing a discard +/// reason. +#[macro_export] +macro_rules! discard_if { + ($a:expr, $reason:literal) => { + if $a { + tracing::debug!(reason = $reason, "Discarding transaction"); + return $crate::PostflightResult::Discard($reason); + } + }; +} + +/// Inspect the outcome of a transaction execution, and determine whether to +/// apply or discard the state changes. +pub trait PostTx { + /// Check the result of the EVM execution, potentially mutating self. + fn run_post_tx(&mut self, result: &ResultAndState) -> PostflightResult; +} + +impl PostTx for T +where + T: for<'a> FnMut(&'a ResultAndState) -> O, + O: Into, +{ + fn run_post_tx(&mut self, result: &ResultAndState) -> PostflightResult { + self(result).into() + } +} diff --git a/src/lifecycle/trait.rs b/src/lifecycle/trait.rs new file mode 100644 index 0000000..9cee4f8 --- /dev/null +++ b/src/lifecycle/trait.rs @@ -0,0 +1,110 @@ +use crate::Block; +use revm::{ + primitives::{EVMError, ResultAndState}, + Database, DatabaseCommit, Evm, +}; + +/// Block contexts apply pre-block and post-block logic to the EVM, as well as +/// post-tx logic like receipt generation. +/// +/// This trait encapsulates special pre-block and post-block logic that needs to +/// be applied to the EVM. This is useful for implementing EIPs that require +/// special system actions to be taken before and after the block is processed. +/// +/// ### Mutability and the EVM +/// +/// The [`BlockContext`] trait has mutable access to the EVM, allowing it to +/// modify the EVM state as needed. This is useful for implementing EIPs that +/// require special system actions to be taken before and after the block is +/// processed. +/// +/// Because the EVM is passed as a mutable reference, the [`BlockContext`] trait +/// can make ANY modificatiopn to the EVM state. This includes potentially +/// changing the [`BlockEnv`] and [`CfgEnv`] objects, the [`SpecId`], or any +/// other property. As such, block contexts are allowed to violate [`Trevm`] +/// state guarantees. Please be careful. +/// +/// ### Provided contexts +/// +/// Contexts are provided for [Shanghai], [Cancun], and [Prague]. While most +/// Contexts do not modify previous behavior, older context modify things like +/// the block reward in place. The [Prague] lifecycle is a superset of the +/// [Cancun] lifecycle, and the [Cancun] lifecycle is a superset of the +/// [Shanghai] lifecycle. This means that the [Prague] lifecycle includes all +/// the logic of the [Cancun] and [Shanghai] Contexts. +/// +/// [Shanghai]: crate::Shanghai +/// [Cancun]: crate::Cancun +/// [Prague]: crate::Prague +/// [`BlockEnv`]: revm::primitives::BlockEnv +/// [`CfgEnv`]: revm::primitives::CfgEnv +/// [`SpecId`]: revm::primitives::SpecId +/// [`Trevm`]: crate::Trevm +pub trait BlockContext { + /// The error type for the context. This captures logic errors that occur + /// during the lifecycle. + type Error: From>; + + /// Apply pre-block logic, and prep the EVM for the first user transaction. + fn open_block( + &mut self, + evm: &mut Evm<'_, Ext, Db>, + b: &B, + ) -> Result<(), Self::Error>; + + /// Apply post-transaction logic and then commit the transaction to the evm + /// state. This will be called by [`Trevm`] for each transaction in a block. + /// + /// This is the hook to produce receipts, update cumulative gas used, + /// inspect logs, etc etc. You can read transaction details from `evm.tx()` + /// and the result of the transaction from `result`. + /// + /// Generally this should end by calling `evm.db_mut().commit(result.state)` + /// however, it is allowed to discard the transaction instead of committing + /// it. + /// + /// ``` + /// # use revm::{ + /// # primitives::{EVMError, ResultAndState}, + /// # Database, DatabaseCommit, Evm, + /// # }; + /// # use trevm::BlockContext; + /// # struct MyContext; + /// # + /// # impl MyContext { fn make_receipt(&self, result: &ResultAndState) {} } + /// # + /// impl BlockContext for MyContext + /// where + /// Db: Database + DatabaseCommit + /// { + /// # type Error = EVMError; + /// # fn open_block( + /// # &mut self, + /// # _evm: &mut Evm<'_, Ext, Db>, + /// # _b: &B + /// # ) -> Result<(), Self::Error> { Ok(()) } + /// fn after_tx( + /// &mut self, + /// evm: &mut Evm<'_, Ext, Db>, + /// result: ResultAndState + /// ) { + /// // Do something with the result + /// self.make_receipt(&result); + /// evm.db_mut().commit(result.state); + /// } + /// # + /// # fn close_block( + /// # &mut self, + /// # _evm: &mut Evm<'_, Ext, Db> + /// # ) -> Result<(), Self::Error> { + /// # Ok(()) + /// # } + /// } + /// ``` + /// + /// [`Trevm`]: crate::Trevm + fn after_tx(&mut self, evm: &mut Evm<'_, Ext, Db>, result: ResultAndState); + + /// Apply post-block logic and close the block. + fn close_block(&mut self, evm: &mut Evm<'_, Ext, Db>) -> Result<(), Self::Error>; +} diff --git a/src/states.rs b/src/states.rs new file mode 100644 index 0000000..e2068e0 --- /dev/null +++ b/src/states.rs @@ -0,0 +1,378 @@ +use crate::{BlockContext, Trevm}; +use sealed::*; + +/// A [`Trevm`] that requires a [`Cfg`]. +/// +/// Expected continuations include: +/// - [`EvmNeedsCfg::fill_cfg`] +/// +/// [`Cfg`]: crate::Cfg +pub type EvmNeedsCfg<'a, Ext, Db> = Trevm<'a, Ext, Db, NeedsCfg>; + +/// A [`Trevm`] that requires a [`Block`] and contains no +/// outputs. This EVM has not yet executed any transactions or state changes. +/// +/// Expected continuations include: +/// - [`EvmNeedsFirstBlock::open_block`] +/// +/// [`Block`]: crate::Block +pub type EvmNeedsFirstBlock<'a, Ext, Db> = Trevm<'a, Ext, Db, NeedsFirstBlock>; + +/// A [`Trevm`] that has completed a block and contains the block's populated +/// lifecycle object. +/// +/// Expected continuations include: +/// - [`EvmBlockComplete::take_context`] +/// - [`EvmBlockComplete::discard_context`] +pub type EvmBlockComplete<'a, Ext, Db, T> = Trevm<'a, Ext, Db, BlockComplete>; + +/// A [`Trevm`] that requires a [`Block`]. +/// +/// Expected continuations include: +/// - [`EvmNeedsFirstBlock::open_block`] +/// +/// [`Block`]: crate::Block +pub type EvmNeedsNextBlock<'a, Ext, Db> = Trevm<'a, Ext, Db, NeedsNextBlock>; + +/// A [`Trevm`] that requires a [`Tx`]. +/// +/// Expected continuations include: +/// - [`EvmNeedsTx::fill_tx`] +/// - [`EvmNeedsTx::run_tx`] +/// - [`EvmNeedsTx::finish`] +/// +/// [`Tx`]: crate::Tx +pub type EvmNeedsTx<'a, Ext, Db, C> = Trevm<'a, Ext, Db, NeedsTx>; + +/// A [`Trevm`] that is ready to execute a transaction. +/// +/// The transaction may be executed with [`EvmReady::run`] or cleared +/// with [`EvmReady::clear_tx`] +pub type EvmReady<'a, Ext, Db, C> = Trevm<'a, Ext, Db, Ready>; + +/// A [`Trevm`] that encountered an error during transaction execution. +/// +/// Expected continuations include: +/// - [`EvmTransacted::reject`] +/// - [`EvmTransacted::accept`] +pub type EvmTransacted<'a, Ext, Db, C> = Trevm<'a, Ext, Db, TransactedState>; + +/// A [`Trevm`] that encountered an error during transaction execution. +/// +/// Expected continuations include: +/// - [`EvmErrored::discard_error`] +/// - [`EvmErrored::into_error`] +pub type EvmErrored<'a, Ext, Db, C, E = >::Error> = + Trevm<'a, Ext, Db, ErroredState>; + +#[allow(dead_code)] +#[allow(unnameable_types)] +pub(crate) mod sealed { + use revm::primitives::ResultAndState; + + macro_rules! states { + ($($name:ident),+) => { + $( + + /// A state for the [`Trevm`]. + /// + /// [`Trevm`]: crate::Trevm + #[derive(Debug)] + pub struct $name { _private: () } + + impl $name { + /// Create a new state. + pub(crate) const fn new() -> Self { + Self { _private: () } + } + } + + )* + }; + } + + states!(NeedsCfg, NeedsFirstBlock, NeedsNextBlock); + + /// A state for the [`Trevm`]. + /// + /// [`Trevm`]: crate::Trevm + #[derive(Debug)] + pub struct NeedsTx(pub T); + + /// A state for the [`Trevm`]. + /// + /// [`Trevm`]: crate::Trevm + #[derive(Debug)] + pub struct Ready(pub T); + + /// A state for the [`Trevm`]. + /// + /// [`Trevm`]: crate::Trevm + #[derive(Debug)] + pub struct BlockComplete(pub T); + + /// A state for the [`Trevm`]. + /// + /// [`Trevm`]: crate::Trevm + #[derive(Debug)] + pub struct TransactedState { + /// The context for the block. + pub context: T, + /// The transaction result. + pub result: ResultAndState, + } + + /// A state for the [`Trevm`]. + /// + /// [`Trevm`]: crate::Trevm + #[derive(Debug)] + pub struct ErroredState { + /// The context for the block. + pub context: T, + /// The transaction error. + pub error: E, + } + + /// Trait for states where block execution can be started. + #[allow(unnameable_types)] + pub trait NeedsBlock {} + impl NeedsBlock for NeedsFirstBlock {} + impl NeedsBlock for NeedsNextBlock {} + + /// Trait for states where thcare outputs vec is non-empty. + #[allow(unnameable_types)] + pub trait HasOutputs {} + impl HasOutputs for NeedsNextBlock {} + impl HasOutputs for NeedsTx {} + impl HasOutputs for TransactedState {} + impl HasOutputs for ErroredState {} + impl HasOutputs for Ready {} + + #[allow(unnameable_types)] + pub trait HasCfg {} + #[allow(unnameable_types)] + impl HasCfg for NeedsFirstBlock {} + impl HasCfg for NeedsNextBlock {} + impl HasCfg for NeedsTx {} + impl HasCfg for TransactedState {} + impl HasCfg for ErroredState {} + impl HasCfg for Ready {} +} + +#[macro_export] +/// Declare type aliases for trevm with a concrete `Ext` and `Db` type. +/// +/// This will create aliases with your concrete types for the following types: +/// - [`EvmNeedsCfg`] +/// - [`EvmNeedsFirstBlock`] +/// - [`EvmBlockComplete`] +/// - [`EvmNeedsNextBlock`] +/// - [`EvmNeedsTx`] +/// - [`EvmReady`] +/// +/// ## Basic usage: +/// +/// Invoking with just a DB type will use [`()`] for the ext +/// +/// ``` +/// use trevm::trevm_aliases; +/// use revm::db::InMemoryDB; +/// +/// // produces types that look like this: +/// // type EvmNeedsCfg = trevm::EvmNeedsCfg<'static, (), InMemoryDB>; +/// trevm_aliases!(revm::db::InMemoryDB); +/// ``` +/// +/// Invoking with an ext and DB type will use the provided ext type and the +/// static lifetime: +/// +/// ``` +/// # mod t { +/// # use trevm::trevm_aliases; +/// # use revm::db::InMemoryDB; +/// # pub struct SomeExtType; +/// // produces types that look like this: +/// // type EvmNeedsCfg = trevm::EvmNeedsCfg<'static, SomeExtType, InMemoryDB>; +/// trevm_aliases!(SomeExtType, InMemoryDB); +/// # } +/// ``` +/// +/// To add a lifetime to the ext type, add the word lifetime: +/// +/// ``` +/// # mod t { +/// # use trevm::trevm_aliases; +/// # use revm::db::InMemoryDB; +/// # pub struct SomeExtType; +/// // produces types that look like this: +/// // type EvmNeedsCfg<'a> = trevm::EvmNeedsCfg<'a, SomeExtType, InMemoryDB>; +/// trevm_aliases!(lifetime: SomeExtType, InMemoryDB); +/// # } +/// ``` +macro_rules! trevm_aliases { + ($db:ty) => { + trevm_aliases!((), $db); + }; + + (lifetime: $ext:ty, $db:ty) => { + #[allow(unused_imports, unreachable_pub, dead_code)] + pub use __aliases::*; + + #[allow(unused_imports, unreachable_pub, dead_code)] + mod __aliases { + use super::*; + // bring these in scope so that doclinks work in generated docs + use $crate::Block; + use $crate::Cfg; + use $crate::Trevm; + use $crate::Tx; + + /// A [`Trevm`] that requires a [`Cfg`]. + /// + /// Expected continuations include: + /// - [`Trevm::fill_cfg`] + /// + /// [`Cfg`]: crate::Cfg + /// [`Trevm`]: crate::Trevm + pub type EvmNeedsCfg<'a> = $crate::EvmNeedsCfg<'a, $ext, $db>; + + /// A [`Trevm`] that requires a [`Block`] and contains no + /// outputs. This EVM has not yet executed any transactions or state changes. + /// + /// Expected continuations include: + /// - [`EvmNeedsFirstBlock::open_block`] + /// + /// [`Block`]: crate::Block + pub type EvmNeedsFirstBlock<'a> = $crate::EvmNeedsFirstBlock<'a, $ext, $db>; + + /// A [`Trevm`] that has completed a block and contains the block's populated + /// lifecycle object. + /// + /// Expected continuations include: + /// - [`EvmBlockComplete::into_parts`] + /// - [`EvmBlockComplete::discard_context`] + pub type EvmBlockComplete<'a, C> = $crate::EvmBlockComplete<'a, $ext, $db, C>; + + /// A [`Trevm`] that requires a [`Block`]. + /// + /// Expected continuations include: + /// - [`EvmNeedsFirstBlock::open_block`] + /// + /// [`Block`]: crate::Block + pub type EvmNeedsNextBlock<'a> = $crate::EvmNeedsNextBlock<'a, $ext, $db>; + + /// A [`Trevm`] that requires a [`Tx`]. + /// + /// Expected continuations include: + /// - [`EvmNeedsTx::fill_tx`] + /// - [`EvmNeedsTx::execute_tx`] + /// - [`EvmNeedsTx::apply_tx`] + /// - [`EvmNeedsTx::finish`] + /// + /// [`Tx`]: crate::Tx + pub type EvmNeedsTx<'a, C> = $crate::EvmNeedsTx<'a, $ext, $db, C>; + + /// A [`Trevm`] that is ready to execute a transaction. + /// + /// The transaction may be executed with [`Trevm::execute_tx`] or + /// cleared with [`Trevm::clear_tx`] + pub type EvmReady<'a, C> = $crate::EvmReady<'a, $ext, $db, C>; + + /// A [`Trevm`] that encountered an error during transaction execution. + /// + /// Expected continuations include: + /// - [`EvmTransacted::reject`] + /// - [`EvmTransacted::accept`] + pub type EvmTransacted<'a, C> = $crate::EvmTransacted<'a, $ext, $db, C>; + + /// A [`Trevm`] that encountered an error during transaction execution. + /// + /// Expected continuations include: + /// - [`EvmErrored::discard_error`] + /// - [`EvmErrored::into_error`] + pub type EvmErrored<'a, C, E = >::Error> = + $crate::EvmErrored<'a, $ext, $db, C, E>; + } + }; + + ($ext:ty, $db:ty) => { + #[allow(unused_imports, unreachable_pub, dead_code)] + pub use __aliases::*; + + #[allow(unused_imports, unreachable_pub, dead_code)] + mod __aliases { + use super::*; + // bring these in scope so that doclinks work in generated docs + use $crate::Block; + use $crate::Cfg; + use $crate::Trevm; + use $crate::Tx; + + /// A [`Trevm`] that requires a [`Cfg`]. + /// + /// Expected continuations include: + /// - [`Trevm::fill_cfg`] + /// + /// [`Cfg`]: crate::Cfg + /// [`Trevm`]: crate::Trevm + pub type EvmNeedsCfg = $crate::EvmNeedsCfg<'static, $ext, $db>; + + /// A [`Trevm`] that requires a [`Block`] and contains no + /// outputs. This EVM has not yet executed any transactions or state changes. + /// + /// Expected continuations include: + /// - [`EvmNeedsFirstBlock::open_block`] + /// + /// [`Block`]: crate::Block + pub type EvmNeedsFirstBlock = $crate::EvmNeedsFirstBlock<'static, $ext, $db>; + + /// A [`Trevm`] that has completed a block and contains the block's populated + /// lifecycle object. + /// + /// Expected continuations include: + /// - [`EvmBlockComplete::into_parts`] + /// - [`EvmBlockComplete::discard_context`] + pub type EvmBlockComplete = $crate::EvmBlockComplete<'static, $ext, $db, C>; + + /// A [`Trevm`] that requires a [`Block`]. + /// + /// Expected continuations include: + /// - [`EvmNeedsFirstBlock::open_block`] + /// + /// [`Block`]: crate::Block + pub type EvmNeedsNextBlock = $crate::EvmNeedsNextBlock<'static, $ext, $db>; + + /// A [`Trevm`] that requires a [`Tx`]. + // + /// Expected continuations include: + /// - [`EvmNeedsTx::fill_tx`] + /// - [`EvmNeedsTx::execute_tx`] + /// - [`EvmNeedsTx::apply_tx`] + /// - [`EvmNeedsTx::finish`] + /// + /// [`Tx`]: crate::Tx + pub type EvmNeedsTx = $crate::EvmNeedsTx<'static, $ext, $db, C>; + + /// A [`Trevm`] that is ready to execute a transaction. + /// + /// The transaction may be executed with [`Trevm::execute_tx`] or + /// cleared with [`Trevm::clear_tx`] + pub type EvmReady = $crate::EvmReady<'static, $ext, $db, C>; + + /// A [`Trevm`] that encountered an error during transaction execution. + /// + /// Expected continuations include: + /// - [`EvmTransacted::reject`] + /// - [`EvmTransacted::accept`] + pub type EvmTransacted = $crate::EvmTransacted<'static, $ext, $db, C>; + + /// A [`Trevm`] that encountered an error during transaction execution. + /// + /// Expected continuations include: + /// - [`EvmErrored::discard_error`] + /// - [`EvmErrored::into_error`] + pub type EvmErrored>::Error> = + $crate::EvmErrored<'static, $ext, $db, C, E>; + } + }; +} diff --git a/src/syscall/eip4788.rs b/src/syscall/eip4788.rs new file mode 100644 index 0000000..275df4f --- /dev/null +++ b/src/syscall/eip4788.rs @@ -0,0 +1,37 @@ +use crate::syscall::SystemTx; +use alloy_primitives::{Address, Bytes, B256}; + +/// The address for the [EIP-4788] beacon roots contract. +/// +/// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 +pub use alloy_eips::eip4788::BEACON_ROOTS_ADDRESS; + +impl SystemTx { + /// Instantiate a system call for the pre-block beacon roots as specified in + /// [EIP-4788]. + /// + /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 + pub fn eip4788(parent_beacon_root: B256) -> Self { + Self::eip4788_with_target(parent_beacon_root, BEACON_ROOTS_ADDRESS) + } + + /// Instantiate a system call for the pre-block beacon roots as specified in + /// [EIP-4788], with a custom target address. + /// + /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 + pub fn eip4788_with_target(parent_beacon_root: B256, target: Address) -> Self { + Self::new(target, Bytes::from(parent_beacon_root)) + } + + /// Instantiate a system call for the pre-block beacon roots as specified in + /// [EIP-4788], with a custom target address and caller address. + /// + /// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 + pub fn eip4788_with_target_and_caller( + parent_beacon_root: B256, + target: Address, + caller: Address, + ) -> Self { + Self::new_with_caller(target, Bytes::from(parent_beacon_root), caller) + } +} diff --git a/src/syscall/eip6110.rs b/src/syscall/eip6110.rs new file mode 100644 index 0000000..c9243f9 --- /dev/null +++ b/src/syscall/eip6110.rs @@ -0,0 +1,83 @@ +use alloy_eips::eip6110::DepositRequest; +use alloy_primitives::Log; +use alloy_sol_types::sol; + +/// The address for the Ethereum 2.0 deposit contract on the mainnet. +pub use alloy_eips::eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS; + +sol! { + #[allow(missing_docs)] + event DepositEvent( + bytes pubkey, + bytes withdrawal_credentials, + bytes amount, + bytes signature, + bytes index + ); +} + +/// Parse deposit requests from logs. +/// +/// When parsing logs, the following assumptions are made +/// +/// - The `DepositEvent` is the only event in the deposit contract. +/// - The deposit contract enforces the length of the fields. +pub fn parse_deposit_from_log(log: &Log) -> DepositRequest { + // SAFETY: These `expect` https://github.com/ethereum/consensus-specs/blob/5f48840f4d768bf0e0a8156a3ed06ec333589007/solidity_deposit_contract/deposit_contract.sol#L107-L110 + // are safe because the `DepositEvent` is the only event in the deposit contract and the length + // checks are done there. + DepositRequest { + pubkey: log + .pubkey + .as_ref() + .try_into() + .expect("pubkey length should be enforced in deposit contract"), + withdrawal_credentials: log + .withdrawal_credentials + .as_ref() + .try_into() + .expect("withdrawal_credentials length should be enforced in deposit contract"), + amount: u64::from_le_bytes( + log.amount + .as_ref() + .try_into() + .expect("amount length should be enforced in deposit contract"), + ), + signature: log + .signature + .as_ref() + .try_into() + .expect("signature length should be enforced in deposit contract"), + index: u64::from_le_bytes( + log.index + .as_ref() + .try_into() + .expect("deposit index length should be enforced in deposit contract"), + ), + } +} + +// Some code above is reproduced from `reth`. It is reused here under the MIT +// license. +// +// The MIT License (MIT) +// +// Copyright (c) 2022-2024 Reth Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. diff --git a/src/syscall/eip7002.rs b/src/syscall/eip7002.rs new file mode 100644 index 0000000..a503f1c --- /dev/null +++ b/src/syscall/eip7002.rs @@ -0,0 +1,37 @@ +use crate::syscall::SystemTx; +use alloy_primitives::{Address, Bytes}; + +/// The address for the [EIP-7002] withdrawal requests contract. +/// +/// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 +pub use alloy_eips::eip7002::WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS; + +/// The size of a withdrawal request in bytes. +pub const WITHDRAWAL_REQUEST_BYTES: usize = 20 + 48 + 8; + +impl SystemTx { + /// Instantiate a system call for the post-block withdrawal requests as + /// specified in [EIP-7002]. + /// + /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 + pub const fn eip7002() -> Self { + Self::eip7002_with_target(WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS) + } + + /// Instantiate a system call for the post-block withdrawal requests as + /// specified in [EIP-7002], with a custom target address. + /// + /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 + pub const fn eip7002_with_target(target: Address) -> Self { + Self::new(target, Bytes::new()) + } + + /// Instantiate a system call for the post-block withdrawal requests as + /// specified in [EIP-7002], with a custom target address and caller + /// address. + /// + /// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 + pub const fn eip7002_with_target_and_caller(target: Address, caller: Address) -> Self { + Self::new_with_caller(target, Bytes::new(), caller) + } +} diff --git a/src/syscall/eip7251.rs b/src/syscall/eip7251.rs new file mode 100644 index 0000000..f7fff10 --- /dev/null +++ b/src/syscall/eip7251.rs @@ -0,0 +1,37 @@ +use crate::syscall::SystemTx; +use alloy_primitives::{Address, Bytes}; + +/// The address for the [EIP-7251] consolidation requests contract +/// +/// [`EIP-7251`]: https://eips.ethereum.org/EIPS/eip-7251 +pub use alloy_eips::eip7251::CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS; + +/// The size of a consolidation request in bytes. +pub const CONSOLIDATION_REQUEST_BYTES: usize = 20 + 48 + 48; + +impl SystemTx { + /// Instantiate a system call for the post-block consolidation requests as + /// specified in [EIP-7251]. + /// + /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 + pub const fn eip7251() -> Self { + Self::eip7251_with_target(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS) + } + + /// Instantiate a system call for the post-block consolidation requests as + /// specified in [EIP-7251], with a custom target address. + /// + /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 + pub const fn eip7251_with_target(target: Address) -> Self { + Self::new(target, Bytes::new()) + } + + /// Instantiate a system call for the post-block consolidation requests as + /// specified in [EIP-7251], with a custom target address and caller + /// address. + /// + /// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 + pub const fn eip7251_with_target_and_caller(target: Address, caller: Address) -> Self { + Self::new_with_caller(target, Bytes::new(), caller) + } +} diff --git a/src/syscall/fill.rs b/src/syscall/fill.rs new file mode 100644 index 0000000..31ad13c --- /dev/null +++ b/src/syscall/fill.rs @@ -0,0 +1,83 @@ +use crate::Tx; +use alloy_primitives::{address, Address, Bytes, U256}; +use revm::primitives::TxEnv; + +/// System smart contract calls as specified in [EIP-4788], [EIP-7002], +/// and [EIP-7251]. +/// +/// By default, these calls are sent from a special system caller address +/// specified in the EIPs, but this can be overridden using the +/// [`SystemTx::new_with_caller`] method. +/// +/// +/// [`EIP-4788`]: https://eips.ethereum.org/EIPS/eip-4788 +/// [`EIP-7002`]: https://eips.ethereum.org/EIPS/eip-7002 +/// [`EIP-7251`]: https://eips.ethereum.org/EIPS/eip-7251 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SystemTx { + /// The target address of the system call. + pub target: Address, + /// The input data of the system call. + pub input: Bytes, + /// The caller address of the system call. + pub caller: Address, +} + +/// The system caller as specified in [EIP-4788], [EIP-7002], and [EIP-7251]. +/// +/// [`EIP-4788`]: https://eips.ethereum.org/EIPS/eip-4788 +/// [`EIP-7002`]: https://eips.ethereum.org/EIPS/eip-7002 +/// [`EIP-7251`]: https://eips.ethereum.org/EIPS/eip-7251 +pub const DEFAULT_SYSTEM_CALLER: Address = address!("fffffffffffffffffffffffffffffffffffffffe"); + +impl SystemTx { + /// Instantiate a new [`SystemTx`]. + pub const fn new(target: Address, input: Bytes) -> Self { + Self { caller: DEFAULT_SYSTEM_CALLER, target, input } + } + + /// Instantiate a new [`SystemTx`] with a custom caller address. + pub const fn new_with_caller(target: Address, input: Bytes, caller: Address) -> Self { + Self { caller, target, input } + } +} + +impl Tx for SystemTx { + fn fill_tx_env(&self, tx_env: &mut TxEnv) { + let TxEnv { + caller, + gas_limit, + gas_price, + transact_to, + value, + data, + nonce, + chain_id, + access_list, + gas_priority_fee, + blob_hashes, + max_fee_per_blob_gas, + authorization_list, + } = tx_env; + *caller = self.caller; + *gas_limit = 30_000_000; + // 0 gas price + *gas_price = U256::ZERO; + *transact_to = self.target.into(); + *value = U256::ZERO; + *data = self.input.clone(); + // disable revm nonce checks + nonce.take(); + // disable chain id checks + chain_id.take(); + // set priority fee to 0 + gas_priority_fee.take(); + // disable eip-2930 + access_list.clear(); + // disable eip-4844 + blob_hashes.clear(); + max_fee_per_blob_gas.take(); + // disable eip-7702 + authorization_list.take(); + } +} diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs new file mode 100644 index 0000000..fc49257 --- /dev/null +++ b/src/syscall/mod.rs @@ -0,0 +1,102 @@ +//! Helpers for system actions including [EIP-4788], [EIP-6110], [EIP-7002] and +//! [EIP-7251]. +//! +//! System actions are special state changes or smart contract calls made +//! before or after transaction exection. These actions are introduced via +//! hardfork. System actions are sometimes modeled as transactions with special +//! properties (as in [EIP-4788], [EIP-7002] and [EIP-7251]) or as special state +//! changes outside of the transaction lifecycle (as in [EIP-6110]). +//! +//! System transactions are modeled by the [`SystemTx`] struct, which implements +//! the [`Tx`] trait. The system transactions are sent from a special system +//! caller address: [`DEFAULT_SYSTEM_CALLER`]. Note that the system caller is +//! specified independently in each EIP, which allows introduction off +//! different system callers in future EIPs +//! +//! [`Tx`]: crate::Tx +//! +//! [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 +//! [EIP-6110]: https://eips.ethereum.org/EIPS/eip-6110 +//! [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 +//! [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 + +mod fill; +pub use fill::{SystemTx, DEFAULT_SYSTEM_CALLER}; + +/// Helpers for Cancun beacon root [EIP-4788] system actions. +/// +/// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 +pub mod eip4788; + +/// Helpers for Shanghai withdrawal [EIP-6110] system actions. +/// +/// [EIP-6110]: https://eips.ethereum.org/EIPS/eip-6110 +pub mod eip6110; + +/// Helpers for Prague withdrawal request [EIP-7002] system actions. +/// +/// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 +pub mod eip7002; + +/// Helpers for Prague consolidation request [EIP-7251] system actions. +/// +/// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 +pub mod eip7251; + +use crate::Tx; +use alloy_primitives::U256; +use revm::{ + primitives::{EVMError, ExecutionResult, ResultAndState}, + Database, DatabaseCommit, Evm, +}; + +/// Clean up the system call, restoring the block env. +fn cleanup_syscall( + evm: &mut Evm<'_, Ext, Db>, + result: &mut ResultAndState, + syscall: &SystemTx, + old_gas_limit: U256, + old_base_fee: U256, +) where + Db: Database + DatabaseCommit, +{ + evm.block_mut().gas_limit = old_gas_limit; + evm.block_mut().basefee = old_base_fee; + + // Remove the system caller and fees from the state + let coinbase = evm.block().coinbase; + let state = &mut result.state; + state.remove(&syscall.caller); + state.remove(&coinbase); +} + +/// Apply a system transaction as specified in [EIP-4788], [EIP-7002], or +/// [EIP-7251]. This function will execute the system transaction and apply +/// the result if non-error, cleaning up any extraneous state changes, and +/// restoring the block environment. +/// +/// [EIP-4788]: https://eips.ethereum.org/EIPS/eip-4788 +/// [EIP-7002]: https://eips.ethereum.org/EIPS/eip-7002 +/// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251 +pub fn execute_system_tx( + evm: &mut Evm<'_, Ext, Db>, + syscall: &SystemTx, +) -> Result> +where + Db: Database + DatabaseCommit, +{ + let limit = U256::from(evm.tx().gas_limit); + let old_gas_limit = std::mem::replace(&mut evm.block_mut().gas_limit, limit); + let old_base_fee = std::mem::replace(&mut evm.block_mut().basefee, U256::ZERO); + + syscall.fill_tx(evm); + let mut result = evm.transact()?; + + // Cleanup the syscall. + cleanup_syscall(evm, &mut result, syscall, old_gas_limit, old_base_fee); + + evm.db_mut().commit(result.state); + + // apply result, remove receipt from block outputs. + Ok(result.result) +} diff --git a/src/test_utils.rs b/src/test_utils.rs new file mode 100644 index 0000000..08682d8 --- /dev/null +++ b/src/test_utils.rs @@ -0,0 +1,113 @@ +use crate::{EvmNeedsCfg, Trevm, TrevmBuilder}; +use alloy_primitives::{Address, U256}; +use revm::{ + db::{CacheDB, EmptyDB, InMemoryDB}, + inspector_handle_register, + primitives::{AccountInfo, Bytecode}, + Database, DatabaseCommit, EvmBuilder, GetInspector, +}; + +pub use revm::test_utils as revm_test_utils; + +impl<'a, Ext, Db: Database + DatabaseCommit, State> Trevm<'a, Ext, Db, State> { + /// Make a new [`Trevm`] with a [`InMemoryDB`]. + pub fn test_trevm() -> EvmNeedsCfg<'static, (), InMemoryDB> { + test_trevm() + } + + /// Make a new [`Trevm`], funding the provided accounts with the given + /// amounts. + pub fn test_trevm_with_funds<'b, I>(i: I) -> EvmNeedsCfg<'static, (), InMemoryDB> + where + I: IntoIterator, + { + let mut trevm = test_trevm(); + + for (address, amount) in i { + trevm.test_set_balance(*address, *amount); + } + + trevm + } +} + +impl<'a, Ext, State> Trevm<'a, Ext, InMemoryDB, State> { + /// Modify an account with the provide closure. Returns the original + /// account info + pub fn test_modify_account(&mut self, address: Address, f: F) -> AccountInfo + where + F: FnOnce(&mut AccountInfo), + { + self.modify_account_unchecked(address, f) + } + + /// Set the nonce of an account, returning the previous nonce. + pub fn test_set_nonce(&mut self, address: Address, nonce: u64) -> u64 { + self.set_nonce_unchecked(address, nonce) + } + + /// Increment the nonce of an account, returning the previous nonce. + /// + /// If this would cause the nonce to overflow, the nonce will be set to the + /// maximum value. + pub fn test_increment_nonce(&mut self, address: Address) -> u64 { + self.increment_nonce_unchecked(address) + } + + /// Decrement the nonce of an account, returning the previous nonce. + /// + /// If this would cause the nonce to underflow, the nonce will be set to + /// 0. + pub fn test_decrement_nonce(&mut self, address: Address) -> u64 { + self.decrement_nonce_unchecked(address) + } + + /// Set the EVM storage at a slot. + pub fn test_set_storage(&mut self, address: Address, slot: U256, value: U256) -> U256 { + self.set_storage_unchecked(address, slot, value) + } + + /// Set the bytecode at a specific address, returning the previous bytecode + /// at that address. + pub fn test_set_bytecode(&mut self, address: Address, bytecode: Bytecode) -> Option { + self.set_bytecode_unchecked(address, bytecode) + } + + /// Increase the balance of an account. Returns the previous balance. + /// + /// If this would cause the balance to overflow, the balance will be set + /// to `U256::MAX`. + pub fn test_increase_balance(&mut self, address: Address, amount: U256) -> U256 { + self.increase_balance_unchecked(address, amount) + } + + /// Decrease the balance of an account. Returns the previous balance. + /// + /// If this would cause the balance to underflow, the balance will be set + /// to `U256::ZERO`. + pub fn test_decrease_balance(&mut self, address: Address, amount: U256) -> U256 { + self.decrease_balance_unchecked(address, amount) + } + + /// Set the balance of an account. Returns the previous balance. + pub fn test_set_balance(&mut self, address: Address, amount: U256) -> U256 { + self.set_balance_unchecked(address, amount) + } +} + +/// Make a new [`Trevm`] with the given inspector and an in-memory database. +pub fn test_trevm_with_inspector(inspector: I) -> EvmNeedsCfg<'static, I, CacheDB> +where + I: GetInspector, +{ + EvmBuilder::default() + .with_db(CacheDB::new(EmptyDB::default())) + .with_external_context(inspector) + .append_handler_register(inspector_handle_register) + .build_trevm() +} + +/// Make a new [`Trevm`] with an in-memory database. +pub fn test_trevm() -> EvmNeedsCfg<'static, (), CacheDB> { + EvmBuilder::default().with_db(CacheDB::new(EmptyDB::default())).build_trevm() +}