diff --git a/.github/workflows/megaeth-live-validate.yml b/.github/workflows/megaeth-live-validate.yml new file mode 100644 index 0000000000000..ec149e3cfbc01 --- /dev/null +++ b/.github/workflows/megaeth-live-validate.yml @@ -0,0 +1,92 @@ +name: MegaETH Live Cross-Validation + +# Runs the `megaeth_live_cross_validate` test, which installs the upstream +# `mega-evme` binary from crates.io and executes the same bytecodes through +# both engines, failing if forge's `--megaeth` output drifts from the library +# reference. +# +# Not in the default Build & Test workflow because the first run compiles +# mega-evme (revm + alloy, ~2 minutes) and requires network access. + +on: + schedule: + # Daily 00:00 UTC — catches mega-evm version/semantics drift quickly. + # With cache hits this runs in ~5 min. + - cron: "0 0 * * *" + workflow_dispatch: + # Trigger on changes to the MegaETH integration code or the test itself. + # Errs on the side of over-triggering — the whole point of this workflow is + # consistency, so a broad filter is cheaper than missing a drift. + pull_request: + paths: + # Full EVM core + inspectors: backend, inspect_mega, result conversion, + # opts (validate_megaeth), inspector stack, etc. + - "crates/evm/core/src/**" + - "crates/evm/evm/src/**" + # Forge command plumbing: test / coverage / multi_runner / runner + - "crates/forge/src/cmd/**" + - "crates/forge/src/multi_runner.rs" + - "crates/forge/src/runner.rs" + # CLI flag + config definitions + - "crates/common/src/evm.rs" + - "crates/config/src/lib.rs" + # Tests + fixtures + - "crates/forge/tests/cli/megaeth.rs" + - "testdata/megaeth/**" + # Dependency pins + - "Cargo.toml" + - "Cargo.lock" + # Workflow itself + - ".github/workflows/megaeth-live-validate.yml" + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + RUST_BACKTRACE: full + RUST_MIN_STACK: 4194304 + MEGA_EVME_VERSION: "1.5.1" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + live-validate: + runs-on: ubuntu-24.04 + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Install nextest + uses: taiki-e/install-action@nextest + + # Cache the `cargo install mega-evme` output so only the first run + # (or a version bump) pays the ~2 minute compile cost. + - name: Cache mega-evme install + id: cache-mega-evme + uses: actions/cache@v4 + with: + path: target/mega-evme-${{ env.MEGA_EVME_VERSION }} + key: mega-evme-${{ env.MEGA_EVME_VERSION }}-${{ runner.os }} + + - name: Build forge tests + run: cargo build -p forge --tests + + # Runs all 8 `megaeth_*` tests, including the normally-ignored + # `megaeth_live_cross_validate` (via --run-ignored=all). The live test + # installs mega-evme from crates.io on the first run; subsequent runs + # hit the cache above. + - name: Run megaeth tests (including live cross-validation) + run: > + cargo nextest run -p forge --test cli + -E 'test(~megaeth)' + --run-ignored=all + --no-fail-fast diff --git a/.gitignore b/.gitignore index d076a432f9b2b..505293e5c1c44 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,9 @@ out.json node_modules dist bin -_ \ No newline at end of file +_ + +# testdata/megaeth installs forge-std via `forge install` — keep it untracked. +/testdata/megaeth/lib/ +/testdata/megaeth/cache/ +/testdata/megaeth/out/ \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 60b729343b2a7..caf1ad5ea5b99 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -113,3 +113,38 @@ When the agent is requested to implement a new feature or bug fix, it should con When writing markdown or similar format files, put each sentence in a separate line. - **Review guidelines are in `REVIEW.md`.** Refer to it for code review conventions and fork-specific review rules. + +## MegaETH Integration + +This fork adds `forge test --megaeth` and `forge coverage --megaeth`, routing execution through the [`mega-evm`](https://crates.io/crates/mega-evm) library instead of stock revm. + +### Where the integration lives + +| Concern | Path | +| --- | --- | +| EVM execution entry point | `crates/evm/core/src/backend/{mod,cow}.rs` (`inspect_mega`) | +| MegaCtx inspector fan-out | `crates/evm/evm/src/inspectors/stack.rs` | +| CLI flag validation | `crates/evm/core/src/opts.rs` (`EvmOpts::validate_megaeth`) | +| Test / coverage command guards | `crates/forge/src/cmd/{test,coverage}/*` | +| Builder + inline-config guards | `crates/forge/src/{multi_runner,runner}.rs` | +| Result/state conversion | `crates/evm/core/src/evm.rs` (`convert_mega_result_and_state`) | +| E2E tests | `crates/forge/tests/cli/megaeth.rs` | +| Manual test fixtures | `testdata/megaeth/` (forge-std lib is gitignored) | +| Live cross-validation CI | `.github/workflows/megaeth-live-validate.yml` (daily + on PR touching mega paths) | + +### Caveats when modifying MegaETH code + +- **`mega-evm` version is pinned.** + Bumping it requires updating the `MEGA_EVME_VERSION` constant in `crates/forge/tests/cli/megaeth.rs` AND the hardcoded reference values in `testdata/megaeth/test/CrossValidate.t.sol` (e.g. `MEGA_EVME_REFERENCE_GAS = 95086`). + Run `cargo nextest run -p forge --test cli megaeth_live_cross_validate --run-ignored=only` locally to regenerate. +- **`--megaeth` + `--isolate` / `--fork-url` / `--gas-report` must stay rejected.** + Silent degradation is worse than a hard error. + Any new code path that builds a runner or executes tests under MegaETH must call `EvmOpts::validate_megaeth()` before any network request. +- **Cheatcodes are skipped under `--megaeth`.** + Do not add `vm.*` calls to `testdata/megaeth/` tests — they appear to succeed but do nothing. +- **System contract deployment must be idempotent.** + `Backend::ensure_system_contract` checks `code_hash` before writing and preserves existing `balance` / `nonce`. + Mirrors mega-evme's pattern — do not revert to `AccountInfo::default()`. +- **`MegaCtx<'a, E>` is generic over the external-env provider.** + Defaults to `TestExternalEnvs` (empty oracle, `MIN_BUCKET_SIZE` SALT buckets). + A fork-backed implementation should plug in as `MegaCtx<'_, ForkExternalEnvs>` rather than replacing the alias — inspector impls are `impl Inspector>`. diff --git a/Cargo.lock b/Cargo.lock index f12459aa72779..fa5bccb224e7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,17 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addchain" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e33f6a175ec6a9e0aca777567f9ff7c3deefc255660df887e7fa3585e9801d8" +dependencies = [ + "num-bigint 0.3.3", + "num-integer", + "num-traits", +] + [[package]] name = "addr2line" version = "0.24.2" @@ -25,7 +36,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -70,9 +81,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3b746060277f3d7f9c36903bb39b593a741cb7afcb0044164c28f0e9b673f0" +checksum = "1b6093bc69509849435a2d68237a2e9fea79d27390c8e62f1e4012c460aabad8" dependencies = [ "alloy-eips", "alloy-primitives", @@ -95,9 +106,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf98679329fa708fa809ea596db6d974da892b068ad45e48ac1956f582edf946" +checksum = "8d1cfed4fefd13b5620cb81cdb6ba397866ff0de514c1b24806e6e79cdff5570" dependencies = [ "alloy-consensus", "alloy-eips", @@ -109,9 +120,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a10e47f5305ea08c37b1772086c1573e9a0a257227143996841172d37d3831bb" +checksum = "f28074a21cd4f7c3a7ab218c4f38fae6be73944e1feae3b670c68b60bf85ca40" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -189,9 +200,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f562a81278a3ed83290e68361f2d1c75d018ae3b8589a314faf9303883e18ec9" +checksum = "5937e2d544e9b71000942d875cbc57965b32859a666ea543cc57aae5a06d602d" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -209,9 +220,9 @@ dependencies = [ [[package]] name = "alloy-ens" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0cccb56364fd3ba0b886370d030e8056ea118e2c35a8f0d1292361fd40d00b" +checksum = "5b7b88162405231467e75e4230785f88672780bbaf19710fe4aaae597f793126" dependencies = [ "alloy-contract", "alloy-primitives", @@ -235,7 +246,7 @@ dependencies = [ "alloy-sol-types", "auto_impl", "derive_more 2.0.1", - "op-alloy-consensus 0.18.11", + "op-alloy-consensus 0.18.14", "op-revm", "revm", "thiserror 2.0.12", @@ -243,9 +254,9 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc41384e9ab8c9b2fb387c52774d9d432656a28edcda1c2d4083e96051524518" +checksum = "c51b4c13e02a8104170a4de02ccf006d7c233e6c10ab290ee16e7041e6ac221d" dependencies = [ "alloy-eips", "alloy-primitives", @@ -281,9 +292,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12c454fcfcd5d26ed3b8cae5933cbee9da5f0b05df19b46d4bd4446d1f082565" +checksum = "b590caa6b6d8bc10e6e7a7696c59b1e550e89f27f50d1ee13071150d3a3e3f66" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -296,9 +307,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d6d39eabe5c7b3d8f23ac47b0b683b99faa4359797114636c66e0743103d05" +checksum = "36fe5af1fca03277daa56ad4ce5f6d623d3f4c2273ea30b9ee8674d18cefc1fa" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -322,9 +333,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3704fa8b7ba9ba3f378d99b3d628c8bc8c2fc431b709947930f154e22a8368b6" +checksum = "793df1e3457573877fbde8872e4906638fde565ee2d3bd16d04aad17d43dbf0e" dependencies = [ "alloy-consensus", "alloy-eips", @@ -345,7 +356,7 @@ dependencies = [ "alloy-op-hardforks", "alloy-primitives", "auto_impl", - "op-alloy-consensus 0.18.11", + "op-alloy-consensus 0.18.14", "op-revm", "revm", ] @@ -363,16 +374,15 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6177ed26655d4e84e00b65cb494d4e0b8830e7cae7ef5d63087d445a2600fb55" +checksum = "bc9485c56de23438127a731a6b4c87803d49faf1a7068dcd1d8768aca3a9edb9" dependencies = [ "alloy-rlp", "arbitrary", "bytes", "cfg-if", "const-hex", - "derive_arbitrary", "derive_more 2.0.1", "foldhash", "getrandom 0.3.3", @@ -394,9 +404,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08800e8cbe70c19e2eb7cf3d7ff4b28bdd9b3933f8e1c8136c7d910617ba03bf" +checksum = "d59879a772ebdcde9dc4eb38b2535d32e8503d3175687cc09e763a625c5fcf32" dependencies = [ "alloy-chains", "alloy-consensus", @@ -440,9 +450,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae68457a2c2ead6bd7d7acb5bf5f1623324b1962d4f8e7b0250657a3c3ab0a0b" +checksum = "fbdfb2899b54b7cb0063fa8e61938320f9be6b81b681be69c203abf130a87baa" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -483,9 +493,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162301b5a57d4d8f000bf30f4dcb82f9f468f3e5e846eeb8598dd39e7886932c" +checksum = "7f060e3bb9f319eb01867a2d6d1ff9e0114e8877f5ca8f5db447724136106cae" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -509,9 +519,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cd8ca94ae7e2b32cc3895d9981f3772aab0b4756aa60e9ed0bcfee50f0e1328" +checksum = "d47b637369245d2dafef84b223b1ff5ea59e6cd3a98d2d3516e32788a0b216df" dependencies = [ "alloy-primitives", "alloy-rpc-types-anvil", @@ -525,9 +535,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3ff6a778ebda3deaed9af17930d678611afe1effa895c4260b61009c314f82" +checksum = "c0b1f499acb3fc729615147bc113b8b798b17379f19d43058a687edc5792c102" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -537,9 +547,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076b47e834b367d8618c52dd0a0d6a711ddf66154636df394805300af4923b8a" +checksum = "1e26b4dd90b33bd158975307fb9cf5fafa737a0e33cbb772a8648bf8be13c104" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -548,19 +558,21 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "1.0.22" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a2a86ad7b7d718c15e79d0779bd255561b6b22968dc5ed2e7c0fbc43bb55fe" +checksum = "c2fe118e6c152d54cb4549b9835fb87d38b12754bb121375183ee3ec84bd0849" dependencies = [ "alloy-primitives", + "derive_more 2.0.1", "serde", + "serde_with", ] [[package]] name = "alloy-rpc-types-engine" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ba838417c42e8f1fe5eb4f4bbfacb7b5d4b9e615b8d2e831b921e04bf0bed62" +checksum = "f2f9cbf5f781b9ee39cfdddea078fdef6015424f4c8282ef0e5416d15ca352c4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -576,9 +588,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c2f847e635ec0be819d06e2ada4bcc4e4204026a83c4bfd78ae8d550e027ae7" +checksum = "46586ec3c278639fc0e129f0eb73dbfa3d57f683c44b2ff5e066fab7ba63fa1f" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -597,9 +609,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc58180302a94c934d455eeedb3ecb99cdc93da1dbddcdbbdb79dd6fe618b2a" +checksum = "bc9a2184493c374ca1dbba9569d37215c23e489970f8c3994f731cb3ed6b0b7d" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -611,9 +623,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9f089d78bb94148e0fcfda087d4ce5fd35a7002847b5e90610c0fcb140f7b4" +checksum = "a3aaf142f4f6c0bdd06839c422179bae135024407d731e6f365380f88cd4730e" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -623,9 +635,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae699248d02ade9db493bbdae61822277dc14ae0f82a5a4153203b60e34422a6" +checksum = "1e1722bc30feef87cc0fa824e43c9013f9639cc6c037be7be28a31361c788be2" dependencies = [ "alloy-primitives", "serde", @@ -634,9 +646,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf7d793c813515e2b627b19a15693960b3ed06670f9f66759396d06ebe5747b" +checksum = "d3674beb29e68fbbc7be302b611cf35fe07b736e308012a280861df5a2361395" dependencies = [ "alloy-dyn-abi", "alloy-primitives", @@ -651,9 +663,9 @@ dependencies = [ [[package]] name = "alloy-signer-aws" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2169ae52e6ec638abbf45ceae0315522eaa554778b3f40040a9c36af70a7bb80" +checksum = "605b1659b320b16708bb84b41038b2f0e2a60d90972c28319c4f5a4866f0efd4" dependencies = [ "alloy-consensus", "alloy-network", @@ -669,9 +681,9 @@ dependencies = [ [[package]] name = "alloy-signer-gcp" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e61cac6f668f4783bae90224928150ac631979d903db129ddf00ca77d4f716" +checksum = "0a207671ef0bf6f61e9c80c9ccb6d203439071252fb35886d6a89aae5431cd9c" dependencies = [ "alloy-consensus", "alloy-network", @@ -687,9 +699,9 @@ dependencies = [ [[package]] name = "alloy-signer-ledger" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b67bd231209051d428426a149fdcc4cbc2ab413161e667ef1ccd4f586ca8d1" +checksum = "cce5e8c97a526d39052035a99652b9cfacf0d646d4a3625fac9c919d20a46fb0" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -707,9 +719,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51a424bc5a11df0d898ce0fd15906b88ebe2a6e4f17a514b51bc93946bb756bd" +checksum = "ad7094c39cd41b03ed642145b0bd37251e31a9cf2ed19e1ce761f089867356a6" dependencies = [ "alloy-consensus", "alloy-network", @@ -726,9 +738,9 @@ dependencies = [ [[package]] name = "alloy-signer-trezor" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db971aeb5431f947f67d5cda772cb5f255cd38bee4051b7444cad2e7c590d1ee" +checksum = "90fc3a082bf4dcf2e330b040378c73a113d31fe03068607a4d80368b47e60c44" dependencies = [ "alloy-consensus", "alloy-network", @@ -816,9 +828,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f317d20f047b3de4d9728c556e2e9a92c9a507702d2016424cd8be13a74ca5e" +checksum = "f89bec2f59a41c0e259b6fe92f78dfc49862c17d10f938db9c33150d5a7f42b6" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -839,9 +851,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff084ac7b1f318c87b579d221f11b748341d68b9ddaa4ffca5e62ed2b8cfefb4" +checksum = "0d3615ec64d775fec840f4e9d5c8e1f739eb1854d8d28db093fb3d4805e0cb53" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -854,9 +866,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb099cdad8ed2e6a80811cdf9bbf715ebf4e34c981b4a6e2d1f9daacbf8b218" +checksum = "374db72669d8ee09063b9aa1a316e812d5cdfce7fc9a99a3eceaa0e5512300d2" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -874,9 +886,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e915e1250dc129ad48d264573ccd08e4716fdda564a772fd217875b8459aff9" +checksum = "f5dbaa6851875d59c8803088f4b6ec72eaeddf7667547ae8995c1a19fbca6303" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -908,9 +920,9 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1154c8187a5ff985c95a8b2daa2fedcf778b17d7668e5e50e556c4ff9c881154" +checksum = "9f916ff6d52f219c44a9684aea764ce2c7e1d53bd4a724c9b127863aeacc30bb" dependencies = [ "alloy-primitives", "darling 0.20.11", @@ -1213,7 +1225,7 @@ dependencies = [ "fnv", "hashbrown 0.15.4", "itertools 0.13.0", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits", "zeroize", @@ -1230,7 +1242,7 @@ dependencies = [ "ark-serialize 0.3.0", "ark-std 0.3.0", "derivative", - "num-bigint", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version 0.3.3", @@ -1250,7 +1262,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version 0.4.1", @@ -1271,7 +1283,7 @@ dependencies = [ "digest 0.10.7", "educe", "itertools 0.13.0", - "num-bigint", + "num-bigint 0.4.6", "num-traits", "paste", "zeroize", @@ -1313,7 +1325,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "quote", "syn 1.0.109", @@ -1325,7 +1337,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "proc-macro2", "quote", @@ -1338,7 +1350,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "proc-macro2", "quote", @@ -1371,7 +1383,7 @@ dependencies = [ "ark-relations", "ark-std 0.5.0", "educe", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits", "tracing", @@ -1407,7 +1419,7 @@ checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ "ark-std 0.4.0", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.6", ] [[package]] @@ -1420,7 +1432,7 @@ dependencies = [ "ark-std 0.5.0", "arrayvec", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.6", ] [[package]] @@ -2095,6 +2107,15 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.69.5" @@ -2104,7 +2125,7 @@ dependencies = [ "bitflags 2.9.1", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.12.1", "lazy_static", "lazycell", "log", @@ -2177,6 +2198,40 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "blake2b_simd" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "blake3" +version = "1.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d2d5991425dfd0785aed03aedcf0b321d61975c9b5b3689c774a2610ae0b51e" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.0", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -2195,6 +2250,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bls12_381" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" +dependencies = [ + "ff 0.12.1", + "group 0.12.1", + "pairing 0.22.0", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "blst" version = "0.3.15" @@ -2856,7 +2924,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83e22e0ed40b96a48d3db274f72fd365bd78f67af39b6bbd47e8a15e1c6207ff" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "hex", "proptest", "serde", @@ -2888,6 +2956,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + [[package]] name = "convert_case" version = "0.7.1" @@ -2932,6 +3006,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.3.0" @@ -3186,6 +3269,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "delegate" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780eb241654bf097afb00fc5f054a09b687dad862e485fdcf8399bb056565370" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "der" version = "0.7.10" @@ -3462,6 +3556,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -3471,9 +3571,9 @@ dependencies = [ "base16ct", "crypto-bigint", "digest 0.10.7", - "ff", + "ff 0.13.1", "generic-array", - "group", + "group 0.13.0", "pem-rfc7468", "pkcs8", "rand_core 0.6.4", @@ -3720,16 +3820,45 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "bitvec", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "ff" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ + "bitvec", + "byteorder", + "ff_derive", "rand_core 0.6.4", "subtle", ] +[[package]] +name = "ff_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f10d12652036b0e99197587c6ba87a8fc3031986499973c030d8b44fcc151b60" +dependencies = [ + "addchain", + "num-bigint 0.3.3", + "num-integer", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "figment" version = "0.10.19" @@ -4506,6 +4635,7 @@ dependencies = [ "foundry-test-utils", "futures", "itertools 0.14.0", + "mega-evm", "op-revm", "parking_lot", "revm", @@ -4831,6 +4961,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + [[package]] name = "gcloud-sdk" version = "0.27.3" @@ -4946,13 +5082,25 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "memuse", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff", + "ff 0.13.1", "rand_core 0.6.4", "subtle", ] @@ -4986,6 +5134,29 @@ dependencies = [ "crunchy", ] +[[package]] +name = "halo2" +version = "0.1.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a23c779b38253fe1538102da44ad5bd5378495a61d2c4ee18d64eaa61ae5995" +dependencies = [ + "halo2_proofs", +] + +[[package]] +name = "halo2_proofs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e925780549adee8364c7f2b685c753f6f3df23bde520c67416e93bf615933760" +dependencies = [ + "blake2b_simd", + "ff 0.12.1", + "group 0.12.1", + "pasta_curves 0.4.1", + "rand_core 0.6.4", + "rayon", +] + [[package]] name = "handlebars" version = "6.3.2" @@ -5554,6 +5725,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -5665,6 +5845,20 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "jubjub" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a575df5f985fe1cd5b2b05664ff6accfc46559032b954529fd225a2168d27b0f" +dependencies = [ + "bitvec", + "bls12_381", + "ff 0.12.1", + "group 0.12.1", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "k256" version = "0.13.4" @@ -5686,7 +5880,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -5719,6 +5913,20 @@ dependencies = [ "libc", ] +[[package]] +name = "kzg-rs" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8b4f55c3dedcfaa8668de1dfc8469e7a32d441c28edf225ed1f566fb32977d" +dependencies = [ + "ff 0.13.1", + "hex", + "serde_arrays", + "sha2 0.10.9", + "sp1_bls12_381", + "spin", +] + [[package]] name = "lalrpop" version = "0.22.2" @@ -5755,6 +5963,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "lazycell" @@ -6050,6 +6261,47 @@ dependencies = [ "topological-sort", ] +[[package]] +name = "mega-evm" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cedce958be6acff0a94764bdf02f4152a846716ea696a65571b066350b03e9" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-hardforks", + "alloy-op-evm", + "alloy-op-hardforks", + "alloy-primitives", + "alloy-sol-types", + "auto_impl", + "bitflags 2.9.1", + "delegate", + "derive_more 2.0.1", + "mega-system-contracts", + "once_cell", + "op-alloy-consensus 0.18.14", + "op-alloy-flz", + "op-revm", + "revm", + "serde", + "thiserror 2.0.12", +] + +[[package]] +name = "mega-system-contracts" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f79f037bb224e42e0104e0542682da1a7c25701bb019b8876b1b87cb7a6532" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "semver 1.0.26", + "serde", + "serde_json", +] + [[package]] name = "memchr" version = "2.7.5" @@ -6065,6 +6317,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memuse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" + [[package]] name = "mesc" version = "0.3.0" @@ -6308,7 +6566,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-complex", "num-integer", "num-iter", @@ -6316,6 +6574,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -6392,7 +6661,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits", ] @@ -6521,9 +6790,9 @@ dependencies = [ [[package]] name = "op-alloy-consensus" -version = "0.18.11" +version = "0.18.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18986c5cf19a790b8b9e8c856a950b48ed6dd6a0259d0efd5f5c9bebbba1fc3a" +checksum = "0c88d2940558fd69f8f07b3cbd7bb3c02fc7d31159c1a7ba9deede50e7881024" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6647,6 +6916,147 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "p3-bn254-fr" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9abf208fbfe540d6e2a6caaa2a9a345b1c8cb23ffdcdfcc6987244525d4fc821" +dependencies = [ + "ff 0.13.1", + "num-bigint 0.4.6", + "p3-field", + "p3-poseidon2", + "p3-symmetric", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "p3-challenger" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b725b453bbb35117a1abf0ddfd900b0676063d6e4231e0fa6bb0d76018d8ad" +dependencies = [ + "p3-field", + "p3-maybe-rayon", + "p3-symmetric", + "p3-util", + "serde", + "tracing", +] + +[[package]] +name = "p3-dft" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a1f81101bff744b7ebba7f4497e917a2c6716d6e62736e4a56e555a2d98cb7" +dependencies = [ + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36459d4acb03d08097d713f336c7393990bb489ab19920d4f68658c7a5c10968" +dependencies = [ + "itertools 0.12.1", + "num-bigint 0.4.6", + "num-traits", + "p3-util", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "p3-koala-bear" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1f52bcb6be38bdc8fa6b38b3434d4eedd511f361d4249fd798c6a5ef817b40" +dependencies = [ + "num-bigint 0.4.6", + "p3-field", + "p3-mds", + "p3-poseidon2", + "p3-symmetric", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "p3-matrix" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e9cd136a4095a25c41a9edfdcce2dfae58ef01639317813bdbbd5b55c583" +dependencies = [ + "itertools 0.12.1", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand 0.8.5", + "serde", + "tracing", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e524d47a49fb4265611303339c4ef970d892817b006cc330dad18afb91e411b1" + +[[package]] +name = "p3-mds" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f6cb8edcb276033d43769a3725570c340d2ed6f35c3cca4cddeee07718fa376" +dependencies = [ + "itertools 0.12.1", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-symmetric", + "p3-util", + "rand 0.8.5", +] + +[[package]] +name = "p3-poseidon2" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a26197df2097b98ab7038d59a01e1fe1a0f545e7e04aa9436b2454b1836654f" +dependencies = [ + "gcd", + "p3-field", + "p3-mds", + "p3-symmetric", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "p3-symmetric" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1d3b5202096bca57cde912fbbb9cbaedaf5ac7c42a924c7166b98709d64d21" +dependencies = [ + "itertools 0.12.1", + "p3-field", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec5f0388aa6d935ca3a17444086120f393f0b2f0816010b5ff95998c1c4095e3" +dependencies = [ + "serde", +] + [[package]] name = "pad" version = "0.1.6" @@ -6656,6 +7066,24 @@ dependencies = [ "unicode-width 0.1.14", ] +[[package]] +name = "pairing" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135590d8bdba2b31346f9cd1fb2a912329f5135e832a4f422942eb6ead8b6b3b" +dependencies = [ + "group 0.12.1", +] + +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group 0.13.0", +] + [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -6713,6 +7141,36 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "pasta_curves" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc65faf8e7313b4b1fbaa9f7ca917a0eed499a9663be71477f87993604341d8" +dependencies = [ + "blake2b_simd", + "ff 0.12.1", + "group 0.12.1", + "lazy_static", + "rand 0.8.5", + "static_assertions", + "subtle", +] + +[[package]] +name = "pasta_curves" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" +dependencies = [ + "blake2b_simd", + "ff 0.13.1", + "group 0.13.0", + "lazy_static", + "rand 0.8.5", + "static_assertions", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" @@ -7699,11 +8157,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61495e01f01c343dd90e5cb41f406c7081a360e3506acf1be0fc7880bfb04eb" dependencies = [ "alloy-eips", + "alloy-provider", + "alloy-transport", "revm-bytecode", "revm-database-interface", "revm-primitives", "revm-state", "serde", + "tokio", ] [[package]] @@ -7717,6 +8178,7 @@ dependencies = [ "revm-primitives", "revm-state", "serde", + "tokio", ] [[package]] @@ -7803,6 +8265,7 @@ dependencies = [ "c-kzg", "cfg-if", "k256", + "kzg-rs", "libsecp256k1", "once_cell", "p256", @@ -7934,7 +8397,7 @@ dependencies = [ "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits", "parity-scale-codec", @@ -8386,6 +8849,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_arrays" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a16b99c5ea4fe3daccd14853ad260ec00ea043b2708d1fd1da3106dcd8d9df" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.219" @@ -8519,7 +8991,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -8531,7 +9003,7 @@ checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.9.0", "opaque-debug", ] @@ -8543,7 +9015,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -8660,7 +9132,7 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "thiserror 2.0.12", "time", @@ -8678,6 +9150,88 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +[[package]] +name = "slop-algebra" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733912d564a68ff209707e71fdc517d4ff82d4362b6a409f6a8241dfcb7a576a" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "serde", +] + +[[package]] +name = "slop-bn254" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a71b23ede427299e139fb822c5d0ea8fb931dc297eba0c6e2f30f774c04ebc81" +dependencies = [ + "ff 0.13.1", + "p3-bn254-fr", + "serde", + "slop-algebra", + "slop-challenger", + "slop-poseidon2", + "slop-symmetric", + "zkhash", +] + +[[package]] +name = "slop-challenger" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59e4993210936ab317c0d56ee8257e1cdfe6c2fae4df1e158737f034e21d45f9" +dependencies = [ + "futures", + "p3-challenger", + "serde", + "slop-algebra", + "slop-symmetric", +] + +[[package]] +name = "slop-koala-bear" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8986e94b9a43d58fc8ce5bf111b0985479ab888ced923e3052fb19943f7859b4" +dependencies = [ + "lazy_static", + "p3-koala-bear", + "serde", + "slop-algebra", + "slop-challenger", + "slop-poseidon2", + "slop-symmetric", +] + +[[package]] +name = "slop-poseidon2" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b06e4a24cba104a0a39740eedd97e60e8896926cc38e6a58d5866cc9811affa" +dependencies = [ + "p3-poseidon2", +] + +[[package]] +name = "slop-primitives" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b0b66701c82f6aab97f4990b5d9ed7463beb5b5042dbe5eda5f6c71a6207b35" +dependencies = [ + "slop-algebra", +] + +[[package]] +name = "slop-symmetric" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d159948b924fd00f280064d7a049e43dceb2f26067f32fb99570d3169969ee" +dependencies = [ + "p3-symmetric", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -8738,7 +9292,7 @@ dependencies = [ "alloy-primitives", "bumpalo", "either", - "num-bigint", + "num-bigint 0.4.6", "num-rational", "semver 1.0.26", "solar-data-structures", @@ -8823,7 +9377,7 @@ dependencies = [ "bumpalo", "itertools 0.14.0", "memchr", - "num-bigint", + "num-bigint 0.4.6", "num-rational", "num-traits", "smallvec", @@ -8911,6 +9465,56 @@ dependencies = [ "zip-extract", ] +[[package]] +name = "sp1-lib" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b96392c1b1c197beaa6b0806099a8d73643a09d5ac0874e26c9c5153a7fcb4c" +dependencies = [ + "bincode", + "serde", + "sp1-primitives", +] + +[[package]] +name = "sp1-primitives" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b77098dae9d62e080be3af253188c08e7e96e666423306654eede0110bf363" +dependencies = [ + "bincode", + "blake3", + "elf", + "hex", + "itertools 0.14.0", + "lazy_static", + "num-bigint 0.4.6", + "serde", + "sha2 0.10.9", + "slop-algebra", + "slop-bn254", + "slop-challenger", + "slop-koala-bear", + "slop-poseidon2", + "slop-primitives", + "slop-symmetric", +] + +[[package]] +name = "sp1_bls12_381" +version = "0.8.0-sp1-6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23e41cd36168cc2e51e5d3e35ff0c34b204d945769a65591a76286d04b51e43" +dependencies = [ + "cfg-if", + "ff 0.13.1", + "group 0.13.0", + "pairing 0.23.0", + "rand_core 0.6.4", + "sp1-lib", + "subtle", +] + [[package]] name = "spanned" version = "0.3.0" @@ -8921,6 +9525,12 @@ dependencies = [ "color-eyre", ] +[[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" @@ -10878,6 +11488,33 @@ dependencies = [ "zip", ] +[[package]] +name = "zkhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4352d1081da6922701401cdd4cbf29a2723feb4cfabb5771f6fee8e9276da1c7" +dependencies = [ + "ark-ff 0.4.2", + "ark-std 0.4.0", + "bitvec", + "blake2", + "bls12_381", + "byteorder", + "cfg-if", + "group 0.12.1", + "group 0.13.0", + "halo2", + "hex", + "jubjub", + "lazy_static", + "pasta_curves 0.5.1", + "rand 0.8.5", + "serde", + "sha2 0.10.9", + "sha3", + "subtle", +] + [[package]] name = "zlib-rs" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index c96c8f77330ea..e478cfe612674 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -215,28 +215,28 @@ solar-sema = { version = "=0.1.5", default-features = false } solar-data-structures = { version = "=0.1.5", default-features = false } ## alloy -alloy-consensus = { version = "1.0.22", default-features = false } -alloy-contract = { version = "1.0.22", default-features = false } -alloy-eips = { version = "1.0.22", default-features = false } -alloy-ens = { version = "1.0.22", default-features = false } -alloy-genesis = { version = "1.0.22", default-features = false } -alloy-json-rpc = { version = "1.0.22", default-features = false } -alloy-network = { version = "1.0.22", default-features = false } -alloy-provider = { version = "1.0.22", default-features = false } -alloy-pubsub = { version = "1.0.22", default-features = false } -alloy-rpc-client = { version = "1.0.22", default-features = false } -alloy-rpc-types = { version = "1.0.22", default-features = true } -alloy-serde = { version = "1.0.22", default-features = false } -alloy-signer = { version = "1.0.22", default-features = false } -alloy-signer-aws = { version = "1.0.22", default-features = false } -alloy-signer-gcp = { version = "1.0.22", default-features = false } -alloy-signer-ledger = { version = "1.0.22", default-features = false } -alloy-signer-local = { version = "1.0.22", default-features = false } -alloy-signer-trezor = { version = "1.0.22", default-features = false } -alloy-transport = { version = "1.0.22", default-features = false } -alloy-transport-http = { version = "1.0.22", default-features = false } -alloy-transport-ipc = { version = "1.0.22", default-features = false } -alloy-transport-ws = { version = "1.0.22", default-features = false } +alloy-consensus = { version = "=1.0.23", default-features = false } +alloy-contract = { version = "=1.0.23", default-features = false } +alloy-eips = { version = "=1.0.23", default-features = false } +alloy-ens = { version = "=1.0.23", default-features = false } +alloy-genesis = { version = "=1.0.23", default-features = false } +alloy-json-rpc = { version = "=1.0.23", default-features = false } +alloy-network = { version = "=1.0.23", default-features = false } +alloy-provider = { version = "=1.0.23", default-features = false } +alloy-pubsub = { version = "=1.0.23", default-features = false } +alloy-rpc-client = { version = "=1.0.23", default-features = false } +alloy-rpc-types = { version = "=1.0.23", default-features = true } +alloy-serde = { version = "=1.0.23", default-features = false } +alloy-signer = { version = "=1.0.23", default-features = false } +alloy-signer-aws = { version = "=1.0.23", default-features = false } +alloy-signer-gcp = { version = "=1.0.23", default-features = false } +alloy-signer-ledger = { version = "=1.0.23", default-features = false } +alloy-signer-local = { version = "=1.0.23", default-features = false } +alloy-signer-trezor = { version = "=1.0.23", default-features = false } +alloy-transport = { version = "=1.0.23", default-features = false } +alloy-transport-http = { version = "=1.0.23", default-features = false } +alloy-transport-ipc = { version = "=1.0.23", default-features = false } +alloy-transport-ws = { version = "=1.0.23", default-features = false } alloy-hardforks = { version = "0.2.12", default-features = false } alloy-op-hardforks = { version = "0.2.12", default-features = false } @@ -267,6 +267,9 @@ revm = { version = "27.0.3", default-features = false } revm-inspectors = { version = "0.26.5", features = ["serde"] } op-revm = { version = "8.0.3", default-features = false } +## mega-evm +mega-evm = { version = "1.5.1", default-features = false, features = ["std", "test-utils"] } + ## alloy-evm alloy-evm = "0.15.0" alloy-op-evm = "0.15.0" diff --git a/README.md b/README.md index 9d065d306b3b2..da92d665cf433 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,31 @@ MegaETH's fork of [Foundry](https://github.com/foundry-rs/foundry), pinned at **v1.3.0**. +## MegaETH Mode + +Run `forge test` and `forge coverage` with MegaETH EVM semantics (gas forwarding, SALT gas metering, system contracts) via the `--megaeth` flag: + +```bash +forge test --megaeth -vvv +forge coverage --megaeth --report lcov +``` + +**Unsupported combinations** (rejected with a clear error rather than silently degrading): + +- `--megaeth --isolate` / inline `isolate = true` — isolation mode not implemented. +- `--megaeth --fork-url ` — fork-aware external environment (Oracle / SALT bucket) not implemented. + +**Cheatcodes** (`vm.prank`, `vm.deal`, `vm.expectRevert`, etc.) are silently skipped under `--megaeth`. Use pure Solidity assertions (`require`). + +See [`testdata/megaeth/`](./testdata/megaeth/) for gas-divergence examples and cross-validation harness. + +## MegaETH ↔ mega-evm consistency + +`--megaeth` is backed by the [`mega-evm`](https://crates.io/crates/mega-evm) library. To detect library-level drift: + +- [`crates/forge/tests/cli/megaeth.rs`](./crates/forge/tests/cli/megaeth.rs) holds E2E tests, including a `megaeth_live_cross_validate` test that installs the upstream `mega-evme` binary from crates.io and compares outputs byte-for-byte. +- [`.github/workflows/megaeth-live-validate.yml`](./.github/workflows/megaeth-live-validate.yml) runs this cross-validation daily and on any PR touching MegaETH integration code. + ## Key Dependencies | Crate | Version | @@ -10,6 +35,7 @@ MegaETH's fork of [Foundry](https://github.com/foundry-rs/foundry), pinned at ** | op-revm | 8.0.3 | | alloy-evm | 0.15.0 | | alloy | 1.0.23 | +| mega-evm | 1.5.1 | ## Upstream diff --git a/crates/common/src/evm.rs b/crates/common/src/evm.rs index 90f973a7d06c5..aa49fefdf08a2 100644 --- a/crates/common/src/evm.rs +++ b/crates/common/src/evm.rs @@ -143,6 +143,11 @@ pub struct EvmArgs { #[arg(long, alias = "alphanet")] #[serde(skip)] pub odyssey: bool, + + /// Enable MegaETH EVM semantics for test and coverage. + #[arg(long)] + #[serde(skip)] + pub megaeth: bool, } // Make this set of options a `figment::Provider` so that it can be merged into the `Config` @@ -173,6 +178,10 @@ impl Provider for EvmArgs { dict.insert("odyssey".to_string(), self.odyssey.into()); } + if self.megaeth { + dict.insert("megaeth".to_string(), self.megaeth.into()); + } + if self.always_use_create_2_factory { dict.insert( "always_use_create_2_factory".to_string(), diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 9d265867a11d3..21fbb3777ba2c 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -520,6 +520,9 @@ pub struct Config { #[serde(alias = "alphanet")] pub odyssey: bool, + /// Enable MegaETH EVM semantics. + pub megaeth: bool, + /// Timeout for transactions in seconds. pub transaction_timeout: u64, @@ -2441,6 +2444,7 @@ impl Default for Config { warnings: vec![], extra_args: vec![], odyssey: false, + megaeth: false, transaction_timeout: 120, additional_compiler_profiles: Default::default(), compilation_restrictions: Default::default(), diff --git a/crates/evm/core/Cargo.toml b/crates/evm/core/Cargo.toml index 222bf550c5ba3..01bd0e25674b9 100644 --- a/crates/evm/core/Cargo.toml +++ b/crates/evm/core/Cargo.toml @@ -52,6 +52,7 @@ revm = { workspace = true, features = [ revm-inspectors.workspace = true op-revm.workspace = true alloy-op-evm.workspace = true +mega-evm.workspace = true auto_impl.workspace = true eyre.workspace = true diff --git a/crates/evm/core/src/backend/cow.rs b/crates/evm/core/src/backend/cow.rs index b23aa750027e4..165426b889929 100644 --- a/crates/evm/core/src/backend/cow.rs +++ b/crates/evm/core/src/backend/cow.rs @@ -61,6 +61,9 @@ impl<'a> CowBackend<'a> { /// Executes the configured transaction of the `env` without committing state changes /// + /// Runs the stock revm path regardless of the inner backend's MegaETH flag; callers + /// that want MegaETH semantics must route to [`CowBackend::inspect_mega`] themselves. + /// /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] @@ -83,6 +86,18 @@ impl<'a> CowBackend<'a> { Ok(res) } + /// Execute a transaction using MegaETH EVM semantics. + #[instrument(name = "inspect_mega", level = "debug", skip_all)] + pub fn inspect_mega revm::Inspector>>( + &mut self, + env: &mut Env, + inspector: &mut I, + ) -> eyre::Result { + self.is_initialized = false; + self.spec_id = env.evm_env.cfg_env.spec; + self.backend_mut(&env.as_env_mut()).inspect_mega(env, inspector) + } + /// Returns whether there was a state snapshot failure in the backend. /// /// This is bubbled up from the underlying Copy-On-Write backend when a revert occurs. diff --git a/crates/evm/core/src/backend/mod.rs b/crates/evm/core/src/backend/mod.rs index abe2448523c6e..354c5d0b106c5 100644 --- a/crates/evm/core/src/backend/mod.rs +++ b/crates/evm/core/src/backend/mod.rs @@ -434,6 +434,8 @@ struct _ObjectSafe(dyn DatabaseExt); #[derive(Clone, Debug)] #[must_use] pub struct Backend { + /// Whether this backend runs with MegaETH EVM semantics. + pub(crate) is_megaeth: bool, /// The access point for managing forks forks: MultiFork, // The default in memory db @@ -487,6 +489,7 @@ impl Backend { }; let mut backend = Self { + is_megaeth: false, forks, mem_db: CacheDB::new(Default::default()), fork_init_journaled_state: inner.new_journaled_state(), @@ -528,6 +531,7 @@ impl Backend { /// Creates a new instance with a `BackendDatabase::InMemory` cache layer for the `CacheDB` pub fn clone_empty(&self) -> Self { Self { + is_megaeth: self.is_megaeth, forks: self.forks.clone(), mem_db: CacheDB::new(Default::default()), fork_init_journaled_state: self.inner.new_journaled_state(), @@ -536,6 +540,17 @@ impl Backend { } } + /// Mark this backend as MegaETH-enabled. + /// + /// This is a flag read by [`crate::executors::Executor`] to dispatch to + /// [`Backend::inspect_mega`] instead of [`Backend::inspect`]. Calling + /// [`Backend::inspect`] directly ignores this flag and always runs the + /// stock revm path — use [`Backend::inspect_mega`] explicitly when + /// bypassing the executor. + pub fn set_megaeth(&mut self, enabled: bool) { + self.is_megaeth = enabled; + } + pub fn insert_account_info(&mut self, address: Address, account: AccountInfo) { if let Some(db) = self.active_fork_db_mut() { db.insert_account_info(address, account) @@ -764,6 +779,9 @@ impl Backend { /// Executes the configured test call of the `env` without committing state changes. /// + /// Runs the stock revm path regardless of [`Backend::is_megaeth`]; callers that want + /// MegaETH semantics must route to [`Backend::inspect_mega`] themselves. + /// /// Note: in case there are any cheatcodes executed that modify the environment, this will /// update the given `env` with the new values. #[instrument(name = "inspect", level = "debug", skip_all)] @@ -773,6 +791,7 @@ impl Backend { inspector: &mut I, ) -> eyre::Result { self.initialize(env); + let mut evm = crate::evm::new_evm_with_inspector(self, env.to_owned(), inspector); let res = evm.transact(env.tx.clone()).wrap_err("EVM error")?; @@ -782,6 +801,105 @@ impl Backend { Ok(res) } + /// Whether this backend is marked for MegaETH execution. + /// + /// Read by [`crate::executors::Executor`] to route to [`Backend::inspect_mega`]. + /// [`Backend::inspect`] does not consult this flag. + pub fn is_megaeth(&self) -> bool { + self.is_megaeth + } + + /// Execute a transaction using MegaETH EVM semantics. + pub fn inspect_mega revm::Inspector>>( + &mut self, + env: &mut Env, + inspector: &mut I, + ) -> eyre::Result { + use crate::evm::convert_mega_result_and_state; + use mega_evm::{MegaContext, MegaEvm, MegaSpecId, MegaTransaction, TestExternalEnvs}; + use revm::InspectEvm; + + let spec = MegaSpecId::REX4; + self.deploy_mega_system_contracts(spec); + + let mut mega_cfg = mega_evm::revm::context::CfgEnv::default(); + mega_cfg.chain_id = env.evm_env.cfg_env.chain_id; + mega_cfg.spec = spec; + mega_cfg.disable_nonce_check = env.evm_env.cfg_env.disable_nonce_check; + mega_cfg.disable_balance_check = env.evm_env.cfg_env.disable_balance_check; + mega_cfg.disable_block_gas_limit = env.evm_env.cfg_env.disable_block_gas_limit; + mega_cfg.disable_base_fee = env.evm_env.cfg_env.disable_base_fee; + mega_cfg.disable_eip3607 = env.evm_env.cfg_env.disable_eip3607; + + let external_envs = TestExternalEnvs::new(); + let db: &mut dyn DatabaseExt = self; + let ctx = MegaContext::new(db, spec) + .with_cfg(mega_cfg) + .with_block(env.evm_env.block_env.clone()) + .with_external_envs(external_envs.into()); + + let mega_tx = MegaTransaction { base: env.tx.clone(), ..Default::default() }; + let mut evm = MegaEvm::new(ctx).with_inspector(inspector); + let res = evm.inspect_tx(mega_tx).map_err(|e| eyre::eyre!("MegaETH EVM error: {e}"))?; + + Ok(convert_mega_result_and_state(res)) + } + + /// Deploy MegaETH system contracts (Oracle, HP Timestamp, Keyless Deploy). + // TODO: use `mega_evm::system_contracts(spec)` once mega-evm exposes it. + fn deploy_mega_system_contracts(&mut self, spec: mega_evm::MegaSpecId) { + use mega_evm::{ + HIGH_PRECISION_TIMESTAMP_ORACLE_ADDRESS, HIGH_PRECISION_TIMESTAMP_ORACLE_CODE, + HIGH_PRECISION_TIMESTAMP_ORACLE_CODE_HASH, KEYLESS_DEPLOY_ADDRESS, KEYLESS_DEPLOY_CODE, + KEYLESS_DEPLOY_CODE_HASH, MegaSpecId, ORACLE_CONTRACT_ADDRESS, ORACLE_CONTRACT_CODE, + ORACLE_CONTRACT_CODE_HASH, ORACLE_CONTRACT_CODE_HASH_REX2, ORACLE_CONTRACT_CODE_REX2, + }; + + if spec >= MegaSpecId::MINI_REX { + let (oracle_code, oracle_hash) = if spec >= MegaSpecId::REX2 { + (ORACLE_CONTRACT_CODE_REX2, ORACLE_CONTRACT_CODE_HASH_REX2) + } else { + (ORACLE_CONTRACT_CODE, ORACLE_CONTRACT_CODE_HASH) + }; + self.ensure_system_contract(ORACLE_CONTRACT_ADDRESS, oracle_code, oracle_hash); + self.ensure_system_contract( + HIGH_PRECISION_TIMESTAMP_ORACLE_ADDRESS, + HIGH_PRECISION_TIMESTAMP_ORACLE_CODE, + HIGH_PRECISION_TIMESTAMP_ORACLE_CODE_HASH, + ); + } + + if spec >= MegaSpecId::REX2 { + self.ensure_system_contract( + KEYLESS_DEPLOY_ADDRESS, + KEYLESS_DEPLOY_CODE, + KEYLESS_DEPLOY_CODE_HASH, + ); + } + + trace!(target: "backend", "MegaETH system contracts deployed for spec {:?}", spec); + } + + /// Deploy `code` at `address` if not already present with the expected hash. + fn ensure_system_contract( + &mut self, + address: Address, + code: alloy_primitives::Bytes, + expected_hash: B256, + ) { + let existing = self.basic_ref(address).ok().flatten(); + if let Some(ref info) = existing + && info.code_hash == expected_hash + { + return; + } + + let mut info = existing.unwrap_or_default(); + info.code_hash = expected_hash; + info.code = Some(Bytecode::new_raw(code)); + self.insert_account_info(address, info); + } + /// Returns true if the address is a precompile pub fn is_existing_precompile(&self, addr: &Address) -> bool { self.inner.precompiles().contains(addr) diff --git a/crates/evm/core/src/evm.rs b/crates/evm/core/src/evm.rs index 050326ed8b14f..2915c3f27b0f3 100644 --- a/crates/evm/core/src/evm.rs +++ b/crates/evm/core/src/evm.rs @@ -14,6 +14,17 @@ use alloy_evm::{ }; use alloy_primitives::{Address, Bytes, U256}; use foundry_fork_db::DatabaseError; +// MegaETH EVM types +pub use mega_evm::{ExternalEnvTypes, MegaContext, MegaHaltReason, MegaSpecId}; + +/// Type alias for the MegaETH context used throughout Foundry. +/// +/// `E` is the external-env provider (SALT bucket + Oracle); defaults to +/// `TestExternalEnvs` for local execution. A fork-backed implementation can be +/// plugged in via `MegaCtx<'_, ForkExternalEnvs>` without touching the +/// inspector stack. +pub type MegaCtx<'a, E = mega_evm::TestExternalEnvs> = MegaContext<&'a mut dyn DatabaseExt, E>; + use revm::{ Context, Journal, context::{ @@ -422,3 +433,46 @@ impl InspectorHandler for FoundryHandler<'_, I> { } } } + +// --------------------------------------------------------------------------- +// MegaETH EVM support +// --------------------------------------------------------------------------- + +/// Converts a `ResultAndState` with `MegaHaltReason` to one with standard `HaltReason`. +/// +/// This is a lossy conversion: Mega-specific resource limit halts are mapped to `OutOfGas`. +pub fn convert_mega_result_and_state( + result: mega_evm::revm::context::result::ResultAndState, +) -> ResultAndState { + use mega_evm::revm::context::result::ExecutionResult as MegaExecResult; + use revm::context::result::OutOfGasError; + + let state = result.state; + let exec_result = match result.result { + MegaExecResult::Success { reason, gas_used, gas_refunded, logs, output } => { + ExecutionResult::Success { reason, gas_used, gas_refunded, logs, output } + } + MegaExecResult::Revert { gas_used, output } => ExecutionResult::Revert { gas_used, output }, + MegaExecResult::Halt { reason, gas_used } => { + let mapped = match reason { + MegaHaltReason::Base(op_halt) => match op_halt { + mega_evm::op_revm::OpHaltReason::Base(eth_halt) => eth_halt, + mega_evm::op_revm::OpHaltReason::FailedDeposit => HaltReason::NotActivated, + }, + // All Mega-specific resource limits → OutOfGas + MegaHaltReason::DataLimitExceeded { .. } + | MegaHaltReason::KVUpdateLimitExceeded { .. } + | MegaHaltReason::ComputeGasLimitExceeded { .. } + | MegaHaltReason::StateGrowthLimitExceeded { .. } + | MegaHaltReason::VolatileDataAccessOutOfGas { .. } => { + HaltReason::OutOfGas(OutOfGasError::Basic) + } + // MegaETH-specific; no direct revm equivalent. + MegaHaltReason::SystemTxInvalidCallee { .. } => HaltReason::NotActivated, + }; + ExecutionResult::Halt { reason: mapped, gas_used } + } + }; + + ResultAndState { result: exec_result, state } +} diff --git a/crates/evm/core/src/opts.rs b/crates/evm/core/src/opts.rs index 5cf7321647acb..cbd129af8d26b 100644 --- a/crates/evm/core/src/opts.rs +++ b/crates/evm/core/src/opts.rs @@ -75,6 +75,9 @@ pub struct EvmOpts { /// whether to enable Odyssey features. pub odyssey: bool, + /// Enable MegaETH EVM semantics. + pub megaeth: bool, + /// The CREATE2 deployer's address. pub create2_deployer: Address, } @@ -100,12 +103,31 @@ impl Default for EvmOpts { isolate: false, disable_block_gas_limit: false, odyssey: false, + megaeth: false, create2_deployer: DEFAULT_CREATE2_DEPLOYER, } } } impl EvmOpts { + /// Rejects flag combinations that MegaETH v1 does not support. + pub fn validate_megaeth(&self) -> eyre::Result<()> { + if !self.megaeth { + return Ok(()); + } + if self.fork_url.is_some() { + eyre::bail!( + "`--fork-url` is not supported with `--megaeth` (MegaETH v1 does not implement fork-aware external environments)" + ); + } + if self.isolate { + eyre::bail!( + "`--isolate` is not supported with `--megaeth` (MegaETH v1 does not implement isolation mode)" + ); + } + Ok(()) + } + /// Configures a new `revm::Env` /// /// If a `fork_url` is set, it gets configured with settings fetched from the endpoint (chain diff --git a/crates/evm/evm/src/executors/mod.rs b/crates/evm/evm/src/executors/mod.rs index 8b6eeb126e485..c1cc427d3b150 100644 --- a/crates/evm/evm/src/executors/mod.rs +++ b/crates/evm/evm/src/executors/mod.rs @@ -491,7 +491,11 @@ impl Executor { pub fn call_with_env(&self, mut env: Env) -> eyre::Result { let mut inspector = self.inspector().clone(); let mut backend = CowBackend::new_borrowed(self.backend()); - let result = backend.inspect(&mut env, &mut inspector)?; + let result = if self.backend().is_megaeth() { + backend.inspect_mega(&mut env, &mut inspector)? + } else { + backend.inspect(&mut env, &mut inspector)? + }; convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure()) } @@ -500,7 +504,11 @@ impl Executor { pub fn transact_with_env(&mut self, mut env: Env) -> eyre::Result { let mut inspector = self.inspector().clone(); let backend = self.backend_mut(); - let result = backend.inspect(&mut env, &mut inspector)?; + let result = if backend.is_megaeth() { + backend.inspect_mega(&mut env, &mut inspector)? + } else { + backend.inspect(&mut env, &mut inspector)? + }; let mut result = convert_executed_result(env, inspector, result, backend.has_state_snapshot_failure())?; self.commit(&mut result); diff --git a/crates/evm/evm/src/inspectors/stack.rs b/crates/evm/evm/src/inspectors/stack.rs index 3f63dddcbfb58..d14f63576e1d8 100644 --- a/crates/evm/evm/src/inspectors/stack.rs +++ b/crates/evm/evm/src/inspectors/stack.rs @@ -1202,3 +1202,175 @@ impl DerefMut for InspectorStack { &mut self.inner } } + +// MegaETH Inspector support. Cheatcodes are skipped (hardcoded to EthEvmContext +// in upstream); all other sub-inspectors work via `impl Inspector`. + +use foundry_evm_core::evm::{ExternalEnvTypes, MegaCtx}; + +/// Like `call_inspectors!` but for `MegaContext`, excluding cheatcodes. +macro_rules! call_mega_inspectors { + ([$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => { + $( + if let Some($id) = $inspector { + $crate::utils::cold_path(); + $body; + } + )+ + }; + (#[ret] [$($inspector:expr),+ $(,)?], |$id:ident $(,)?| $body:expr $(,)?) => {{ + $( + if let Some($id) = $inspector { + $crate::utils::cold_path(); + if let Some(result) = $body { + return result; + } + } + )+ + }}; +} + +impl Inspector> for InspectorStack { + fn initialize_interp(&mut self, interpreter: &mut Interpreter, ecx: &mut MegaCtx<'_, E>) { + call_mega_inspectors!( + [ + &mut self.inner.line_coverage, + &mut self.inner.tracer, + // cheatcodes skipped — not supported under MegaETH yet + &mut self.inner.script_execution_inspector, + &mut self.inner.printer + ], + |inspector| inspector.initialize_interp(interpreter, ecx), + ); + } + + fn step(&mut self, interpreter: &mut Interpreter, ecx: &mut MegaCtx<'_, E>) { + call_mega_inspectors!( + [ + &mut self.inner.fuzzer, + &mut self.inner.tracer, + &mut self.inner.line_coverage, + &mut self.inner.edge_coverage, + &mut self.inner.script_execution_inspector, + &mut self.inner.printer, + &mut self.inner.revert_diag // cheatcodes skipped + ], + |inspector| (*inspector).step(interpreter, ecx), + ); + } + + fn step_end(&mut self, interpreter: &mut Interpreter, ecx: &mut MegaCtx<'_, E>) { + call_mega_inspectors!( + [ + &mut self.inner.tracer, + &mut self.inner.chisel_state, + &mut self.inner.printer, + &mut self.inner.revert_diag // cheatcodes skipped + ], + |inspector| (*inspector).step_end(interpreter, ecx), + ); + } + + // The macro fans `log` out across multiple inspectors. + #[allow(clippy::redundant_clone)] + fn log(&mut self, interpreter: &mut Interpreter, ecx: &mut MegaCtx<'_, E>, log: Log) { + call_mega_inspectors!( + [ + &mut self.inner.tracer, + &mut self.inner.log_collector, + // cheatcodes skipped + &mut self.inner.printer + ], + |inspector| inspector.log(interpreter, ecx, log.clone()), + ); + } + + fn call(&mut self, ecx: &mut MegaCtx<'_, E>, call: &mut CallInputs) -> Option { + call_mega_inspectors!( + #[ret] + [ + &mut self.inner.fuzzer, + &mut self.inner.tracer, + &mut self.inner.log_collector, + &mut self.inner.printer, + &mut self.inner.revert_diag + ], + |inspector| { + let mut out = None; + if let Some(output) = inspector.call(ecx, call) { + out = Some(Some(output)); + } + out + }, + ); + + // cheatcodes and isolation mode skipped for MegaETH v1 + None + } + + fn call_end( + &mut self, + ecx: &mut MegaCtx<'_, E>, + inputs: &CallInputs, + outcome: &mut CallOutcome, + ) { + let result = outcome.result.result; + call_mega_inspectors!( + [ + &mut self.inner.fuzzer, + &mut self.inner.tracer, + // cheatcodes skipped + &mut self.inner.printer, + &mut self.inner.revert_diag + ], + |inspector| { + inspector.call_end(ecx, inputs, outcome); + }, + ); + + if result.is_revert() && self.inner.reverter.is_none() { + self.inner.reverter = Some(inputs.target_address); + } + } + + fn create( + &mut self, + ecx: &mut MegaCtx<'_, E>, + create: &mut CreateInputs, + ) -> Option { + call_mega_inspectors!( + #[ret] + [ + &mut self.inner.tracer, + &mut self.inner.line_coverage // cheatcodes skipped + ], + |inspector| inspector.create(ecx, create).map(Some), + ); + + // isolation mode skipped for MegaETH v1 + None + } + + fn create_end( + &mut self, + ecx: &mut MegaCtx<'_, E>, + call: &CreateInputs, + outcome: &mut CreateOutcome, + ) { + call_mega_inspectors!( + [ + &mut self.inner.tracer // cheatcodes skipped + ], + |inspector| { + inspector.create_end(ecx, call, outcome); + }, + ); + } + + fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) { + // Delegates to existing implementation — selfdestruct doesn't take context + call_mega_inspectors!([&mut self.inner.tracer], |inspector| { + Inspector::>::selfdestruct(inspector, contract, target, value) + },); + } +} diff --git a/crates/forge/src/cmd/coverage.rs b/crates/forge/src/cmd/coverage.rs index ce42d73269c41..55e67a0589746 100644 --- a/crates/forge/src/cmd/coverage.rs +++ b/crates/forge/src/cmd/coverage.rs @@ -91,6 +91,9 @@ impl CoverageArgs { pub async fn run(mut self) -> Result<()> { let (mut config, evm_opts) = self.load_config_and_evm_opts()?; + // Reject unsupported MegaETH flag combinations before any network request. + evm_opts.validate_megaeth()?; + // install missing dependencies if install::install_missing_dependencies(&mut config) && config.auto_detect_remappings { // need to re-configure here to also catch additional remappings @@ -275,6 +278,8 @@ impl CoverageArgs { .evm_spec(config.evm_spec_id()) .sender(evm_opts.sender) .with_fork(evm_opts.get_fork(&config, env.clone())) + .enable_isolation(evm_opts.isolate) + .odyssey(evm_opts.odyssey) .set_coverage(true) .build::(root, output, env, evm_opts)?; diff --git a/crates/forge/src/cmd/test/mod.rs b/crates/forge/src/cmd/test/mod.rs index e6171f0cf17c5..986a4a3aac2fb 100644 --- a/crates/forge/src/cmd/test/mod.rs +++ b/crates/forge/src/cmd/test/mod.rs @@ -286,6 +286,16 @@ impl TestArgs { // Merge all configs. let (mut config, mut evm_opts) = self.load_config_and_evm_opts()?; + // Reject unsupported MegaETH flag combinations. `--gas-report` is checked + // before the isolation mutation below so the error attributes to the flag + // the user actually passed. + if evm_opts.megaeth && self.gas_report { + eyre::bail!( + "`--gas-report` is not supported with `--megaeth` (gas reports require isolation mode, which MegaETH v1 does not implement)" + ); + } + evm_opts.validate_megaeth()?; + // Explicitly enable isolation for gas reports for more correct gas accounting. if self.gas_report { evm_opts.isolate = true; diff --git a/crates/forge/src/multi_runner.rs b/crates/forge/src/multi_runner.rs index 76ae5d6da10e9..8da9a395b05f7 100644 --- a/crates/forge/src/multi_runner.rs +++ b/crates/forge/src/multi_runner.rs @@ -301,9 +301,16 @@ pub struct TestRunnerConfig { impl TestRunnerConfig { /// Reconfigures all fields using the given `config`. /// This is for example used to override the configuration with inline config. - pub fn reconfigure_with(&mut self, config: Arc) { + pub fn reconfigure_with(&mut self, config: Arc) -> eyre::Result<()> { debug_assert!(!Arc::ptr_eq(&self.config, &config)); + if self.evm_opts.megaeth && config.isolate { + eyre::bail!( + "inline config sets `isolate = true` but `--megaeth` is enabled \ + (MegaETH v1 does not implement isolation mode)" + ); + } + self.spec_id = config.evm_spec_id(); self.sender = config.sender; self.odyssey = config.odyssey; @@ -317,6 +324,7 @@ impl TestRunnerConfig { // self.decode_internal = N/A; self.config = config; + Ok(()) } /// Configures the given executor with this configuration. @@ -348,6 +356,10 @@ impl TestRunnerConfig { artifact_id: &ArtifactId, db: Backend, ) -> Executor { + // Propagate MegaETH flag to Backend + let mut db = db; + db.set_megaeth(self.evm_opts.megaeth); + let cheats_config = Arc::new(CheatsConfig::new( &self.config, self.evm_opts.clone(), @@ -476,6 +488,13 @@ impl MultiContractRunnerBuilder { env: Env, evm_opts: EvmOpts, ) -> Result { + evm_opts.validate_megaeth()?; + if evm_opts.megaeth && self.isolation { + eyre::bail!( + "`--isolate` is not supported with `--megaeth` (MegaETH v1 does not implement isolation mode)" + ); + } + let contracts = output .artifact_ids() .map(|(id, v)| (id.with_stripped_file_prefixes(root), v)) diff --git a/crates/forge/src/runner.rs b/crates/forge/src/runner.rs index 4129f8938d793..d613bb74aecdd 100644 --- a/crates/forge/src/runner.rs +++ b/crates/forge/src/runner.rs @@ -209,7 +209,7 @@ impl<'a> ContractRunner<'a> { fn apply_contract_inline_config(&mut self) -> Result<()> { if self.inline_config.contains_contract(self.name) { let new_config = Arc::new(self.inline_config(None)?); - self.tcfg.to_mut().reconfigure_with(new_config); + self.tcfg.to_mut().reconfigure_with(new_config)?; let prev_tracer = self.executor.inspector_mut().tracer.take(); self.tcfg.configure_executor(&mut self.executor); // Don't set tracer here. @@ -491,7 +491,7 @@ impl<'a> FunctionRunner<'a> { fn apply_function_inline_config(&mut self, func: &Function) -> Result<()> { if self.inline_config.contains_function(self.cr.name, &func.name) { let new_config = Arc::new(self.cr.inline_config(Some(func))?); - self.tcfg.to_mut().reconfigure_with(new_config); + self.tcfg.to_mut().reconfigure_with(new_config)?; self.tcfg.configure_executor(self.executor.to_mut()); } Ok(()) diff --git a/crates/forge/tests/cli/config.rs b/crates/forge/tests/cli/config.rs index aa27ba775495c..793f114e34297 100644 --- a/crates/forge/tests/cli/config.rs +++ b/crates/forge/tests/cli/config.rs @@ -168,6 +168,7 @@ forgetest!(can_extract_config_values, |prj, cmd| { legacy_assertions: false, extra_args: vec![], odyssey: false, + megaeth: false, transaction_timeout: 120, additional_compiler_profiles: Default::default(), compilation_restrictions: Default::default(), @@ -1045,6 +1046,7 @@ create2_deployer = "0x4e59b44847b379578588920ca78fbf26c0b4956c" assertions_revert = true legacy_assertions = false odyssey = false +megaeth = false transaction_timeout = 120 additional_compiler_profiles = [] compilation_restrictions = [] @@ -1330,6 +1332,7 @@ exclude = [] "assertions_revert": true, "legacy_assertions": false, "odyssey": false, + "megaeth": false, "transaction_timeout": 120, "additional_compiler_profiles": [], "compilation_restrictions": [], diff --git a/crates/forge/tests/cli/main.rs b/crates/forge/tests/cli/main.rs index 4a59aa2b51e63..e16dc001c08bd 100644 --- a/crates/forge/tests/cli/main.rs +++ b/crates/forge/tests/cli/main.rs @@ -21,6 +21,7 @@ mod geiger; mod inline_config; mod install; mod lint; +mod megaeth; mod multi_script; mod script; mod soldeer; diff --git a/crates/forge/tests/cli/megaeth.rs b/crates/forge/tests/cli/megaeth.rs new file mode 100644 index 0000000000000..f081f984ac1ef --- /dev/null +++ b/crates/forge/tests/cli/megaeth.rs @@ -0,0 +1,409 @@ +// Tests for MegaETH (`--megaeth`) flag interactions and cross-validation +// against mega-evm reference outputs. + +use foundry_test_utils::util::OutputExt; +use std::path::PathBuf; + +// ---------- Flag conflict tests ---------- + +// Verifies that `--megaeth --isolate` is rejected at the CLI level. +forgetest_init!(megaeth_isolate_rejected, |_prj, cmd| { + cmd.args(["test", "--megaeth", "--isolate"]).assert_failure().stderr_eq(str![[r#" +Error: `--isolate` is not supported with `--megaeth` (MegaETH v1 does not implement isolation mode) + +"#]]); +}); + +// Verifies that `--megaeth --fork-url` is rejected. +forgetest_init!(megaeth_fork_rejected, |_prj, cmd| { + cmd.args(["test", "--megaeth", "--fork-url", "http://127.0.0.1:1"]) + .assert_failure() + .stderr_eq(str![[r#" +Error: `--fork-url` is not supported with `--megaeth` (MegaETH v1 does not implement fork-aware external environments) + +"#]]); +}); + +// Verifies that `--megaeth --gas-report` is rejected with a clear message +// attributing the cause to `--gas-report` (which implies isolation), not a +// misleading "--isolate is not supported" from the builder layer. +forgetest_init!(megaeth_gas_report_rejected, |_prj, cmd| { + cmd.args(["test", "--megaeth", "--gas-report"]) + .assert_failure() + .stderr_eq(str![[r#" +Error: `--gas-report` is not supported with `--megaeth` (gas reports require isolation mode, which MegaETH v1 does not implement) + +"#]]); +}); + +// Verifies that inline `isolate = true` under --megaeth is rejected at test time +// (not silently ignored). The error surfaces as a test failure with the guard +// message embedded in the failure reason. +forgetest_init!(megaeth_inline_isolate_rejected, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "MegaInline.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract Dummy { + uint256 public x; + function setX(uint256 v) public { x = v; } +} + +/// forge-config: default.isolate = true +contract InlineIsolateTest is Test { + Dummy dummy; + + function setUp() public { + dummy = new Dummy(); + } + + function test_runs_without_isolation() public { + dummy.setX(42); + require(dummy.x() == 42, "mismatch"); + } +} + "#, + ) + .unwrap(); + + // The command itself succeeds (test runner runs) but the suite fails because + // the inline config conflict is surfaced as a setUp/test error. + let out = cmd.args(["test", "--megaeth"]).assert_failure().get_output().stdout_lossy(); + assert!( + out.contains("isolate = true") && out.contains("--megaeth"), + "expected inline-isolate rejection message, got:\n{out}" + ); +}); + +// Verifies `forge coverage --megaeth --fork-url` is rejected before any network +// request (matches the `forge test` behavior). +forgetest_init!(megaeth_coverage_fork_rejected, |_prj, cmd| { + cmd.args(["coverage", "--megaeth", "--fork-url", "http://127.0.0.1:1"]) + .assert_failure() + .stderr_eq(str![[r#" +Error: `--fork-url` is not supported with `--megaeth` (MegaETH v1 does not implement fork-aware external environments) + +"#]]); +}); + +// Verifies `forge coverage --megaeth --isolate` is rejected (previously silently +// ignored because the coverage runner never forwarded `--isolate`). +forgetest_init!(megaeth_coverage_isolate_rejected, |_prj, cmd| { + cmd.args(["coverage", "--megaeth", "--isolate"]).assert_failure().stderr_eq(str![[r#" +Error: `--isolate` is not supported with `--megaeth` (MegaETH v1 does not implement isolation mode) + +"#]]); +}); + +// Exercises the read-only `call_raw` / `call_sol_default` path under --megaeth +// (fuzz harness → `Executor::call_raw` → `CowBackend::inspect_mega`). The regular +// `transact_with_env` path is covered by `megaeth_basic_pass` and `megaeth_cross_validate`; +// this test guards against regressions specific to the read-only dispatch. +forgetest_init!(megaeth_fuzz_call_raw, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "FuzzCallRaw.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract Target { + function identity(uint128 x) external pure returns (uint128) { + return x; + } +} + +contract FuzzCallRawTest is Test { + Target target; + + function setUp() public { + target = new Target(); + } + + function testFuzz_identity(uint128 x) public view { + require(target.identity(x) == x, "identity broke"); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--megaeth", "--match-test", "testFuzz_identity"]).assert_success(); +}); + +// Basic sanity: --megaeth alone works for simple tests. +forgetest_init!(megaeth_basic_pass, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "Basic.t.sol", + r#" +import {Test} from "forge-std/Test.sol"; + +contract MegaBasic is Test { + function test_addition() public pure { + require(1 + 1 == 2, "math broke"); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--megaeth"]).assert_success(); +}); + +// ---------- Cross-validation against mega-evm reference values ---------- + +// Cross-validate forge --megaeth against mega-evm reference outputs. +// +// The Solidity test file embeds reference values produced by `mega-evme run` +// (the standalone mega-evm CLI) for fixed bytecodes. If forge's mega-evm +// integration diverges from the library's behavior, the `require()` checks +// fail and this test fails. +// +// Reference values are regenerated offline via `megaeth_live_cross_validate` +// (requires mega-evme binary, cache-installed from crates.io on first run). +forgetest_init!(megaeth_cross_validate, |prj, cmd| { + prj.wipe_contracts(); + + prj.add_test( + "CrossValidate.t.sol", + r#" +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.sol"; + +/// Reference values sourced from: +/// mega-evme run --json --spec Rex4 [--input ] +contract CrossValidate is Test { + function _deployRuntime(bytes memory runtime) internal returns (address addr) { + bytes memory code = abi.encodePacked( + uint8(0x60), uint8(runtime.length), + uint8(0x80), + uint8(0x60), uint8(11), + uint8(0x60), uint8(0), + uint8(0x39), + uint8(0x60), uint8(0), + uint8(0xf3), + runtime + ); + assembly { addr := create(0, add(code, 0x20), mload(code)) } + require(addr != address(0), "deploy failed"); + } + + // mega-evme: output = 0x...42, success = true + // bytecode: PUSH1 0x42 | MSTORE | RETURN 32 bytes at offset 0 + function test_basicReturn() public { + address t = _deployRuntime(hex"604260005260206000f3"); + (bool ok, bytes memory ret) = t.call(""); + require(ok, "call failed"); + require(ret.length == 32, "bad len"); + uint256 val; + assembly { val := mload(add(ret, 0x20)) } + require(val == 0x42, "wrong val"); + } + + // mega-evme: output = 0xdeadbeef (echo of input), success = true + // bytecode: CALLDATACOPY then RETURN + function test_inputEcho() public { + address t = _deployRuntime(hex"366000600037366000f3"); + (bool ok, bytes memory ret) = t.call(hex"deadbeef"); + require(ok, "call failed"); + require(ret.length == 4, "bad len"); + require(keccak256(ret) == keccak256(hex"deadbeef"), "wrong echo"); + } + + // mega-evme: success = false + // bytecode: PUSH0 | PUSH0 | REVERT + function test_revert() public { + address t = _deployRuntime(hex"5f5ffd"); + (bool ok, ) = t.call(""); + require(!ok, "should revert"); + } +} + +/// GasProbe measures its own gas consumption across SSTORE + compute + STATICCALL. +/// This exercises SALT bucket gas, opcode gas, and call gas forwarding — the core +/// semantics that differ between Ethereum and MegaETH. +contract GasProbe { + uint256 public slot0; + uint256 public slot1; + + function run() external returns (uint256 gasUsed) { + uint256 g0 = gasleft(); + slot0 = 42; + slot1 = 84; + uint256 x = 1; + for (uint256 i = 0; i < 100; i++) { x = x * 3 + 1; } + address(this).staticcall(abi.encodeWithSelector(this.slot0.selector)); + uint256 g1 = gasleft(); + gasUsed = g0 - g1; + slot0 = gasUsed; + } +} + +contract CrossValidateGasProbe is Test { + // mega-evme reference: `run --json --spec Rex4 --input 0xc0406226 ` + // → output = 0x...1736e = 95086 + uint256 constant MEGA_EVME_REFERENCE_GAS = 95086; + + function test_gasProbe() public { + GasProbe probe = new GasProbe(); + uint256 reported = probe.run(); + require( + reported == MEGA_EVME_REFERENCE_GAS, + "GasProbe gas drift: forge --megaeth output does not match mega-evme" + ); + } +} + "#, + ) + .unwrap(); + + cmd.args(["test", "--megaeth"]).assert_success(); +}); + +// ---------- mega-evme binary management ---------- + +/// mega-evme version pinned in our cross-validation tests. +/// Must match the `mega-evm` version in workspace Cargo.toml. +const MEGA_EVME_VERSION: &str = "1.5.1"; + +/// Ensure `mega-evme` is installed and return its path. +/// +/// Resolution order: +/// 1. `MEGA_EVME` env var (explicit override — point at any prebuilt binary) +/// 2. `/target/mega-evme-/bin/mega-evme` (cached crates.io install) +/// 3. Install from crates.io into the cache path above (one-time cost) +fn ensure_mega_evme() -> PathBuf { + // 1. Explicit override + if let Ok(p) = std::env::var("MEGA_EVME") { + let path = PathBuf::from(p); + if path.exists() { + return path; + } + } + + // 2. Cached crates.io install under foundry's target dir + let cache_root = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .unwrap() + .parent() + .unwrap() + .join("target") + .join(format!("mega-evme-{MEGA_EVME_VERSION}")); + let cached_bin = cache_root.join("bin/mega-evme"); + if cached_bin.exists() { + return cached_bin; + } + + // 3. Install from crates.io (slow: compiles revm, alloy, etc.). + // `cargo install` prints its own progress, so no extra status line needed. + let status = std::process::Command::new("cargo") + .args([ + "install", + "mega-evme", + "--version", + MEGA_EVME_VERSION, + "--root", + cache_root.to_str().unwrap(), + "--locked", + ]) + .status() + .expect("failed to spawn `cargo install mega-evme`"); + assert!(status.success(), "cargo install mega-evme failed"); + assert!(cached_bin.exists(), "mega-evme not found after install"); + cached_bin +} + +fn run_mega_evme(bin: &PathBuf, bytecode: &str, input: Option<&str>) -> serde_json::Value { + let mut args = vec!["run", "--json", "--spec", "Rex4"]; + if let Some(inp) = input { + args.push("--input"); + args.push(inp); + } + args.push(bytecode); + let out = + std::process::Command::new(bin).args(&args).output().expect("failed to run mega-evme"); + serde_json::from_slice(&out.stdout).expect("mega-evme bad JSON") +} + +// Live cross-validation: run the same bytecodes through both engines and +// assert they agree. Installs `mega-evme` from crates.io on first run +// (cached under `target/mega-evme-/`). +// +// Marked `#[ignore]` because the first run compiles mega-evme (~2 minutes) +// and requires network. Opt-in via `cargo test -- --ignored`. +forgetest_init!( + #[ignore = "slow: installs mega-evme on first run; use --ignored to opt in"] + megaeth_live_cross_validate, + |prj, cmd| { + let bin = ensure_mega_evme(); + + // Raw bytecodes with known mega-evme outputs — these must match the + // values encoded in `megaeth_cross_validate`'s CrossValidate.t.sol. + let raw_cases: &[(&str, &str, Option<&str>, &str)] = &[ + ( + "basicReturn", + "0x604260005260206000f3", + None, + "0x0000000000000000000000000000000000000000000000000000000000000042", + ), + ("inputEcho", "0x366000600037366000f3", Some("0xdeadbeef"), "0xdeadbeef"), + ]; + for (name, bytecode, input, expected) in raw_cases { + let r = run_mega_evme(&bin, bytecode, *input); + assert_eq!(r["success"], true, "{name} should succeed"); + assert_eq!(r["output"].as_str().unwrap(), *expected, "{name} output drift"); + } + // Revert case + let r = run_mega_evme(&bin, "0x5f5ffd", None); + assert_eq!(r["success"], false, "revert case should fail"); + + // GasProbe: extract bytecode from forge, run through mega-evme, + // assert the returned gas matches our hardcoded reference (95086). + prj.wipe_contracts(); + prj.add_test( + "GasProbe.t.sol", + r#" +contract GasProbe { + uint256 public slot0; + uint256 public slot1; + function run() external returns (uint256 gasUsed) { + uint256 g0 = gasleft(); + slot0 = 42; slot1 = 84; + uint256 x = 1; + for (uint256 i = 0; i < 100; i++) { x = x * 3 + 1; } + address(this).staticcall(abi.encodeWithSelector(this.slot0.selector)); + uint256 g1 = gasleft(); + gasUsed = g0 - g1; slot0 = gasUsed; + } +} + "#, + ) + .unwrap(); + + let bytecode_out = cmd + .args(["inspect", "GasProbe", "deployedBytecode"]) + .assert_success() + .get_output() + .stdout_lossy(); + let bytecode = bytecode_out.trim(); + + let r = run_mega_evme(&bin, bytecode, Some("0xc0406226")); + assert_eq!(r["success"], true, "GasProbe should succeed"); + let output_hex = r["output"].as_str().unwrap(); + let output_clean = output_hex.trim_start_matches("0x").trim_start_matches('0'); + let mega_gas = u64::from_str_radix(output_clean, 16).expect("bad hex"); + assert_eq!( + mega_gas, 95086, + "GasProbe gas drift: mega-evme {MEGA_EVME_VERSION} now reports {mega_gas}, \ + but CrossValidate.t.sol hardcodes 95086. \ + Update MEGA_EVME_REFERENCE_GAS in CrossValidate.t.sol." + ); + } +); diff --git a/testdata/megaeth/README.md b/testdata/megaeth/README.md new file mode 100644 index 0000000000000..fdffc14c57373 --- /dev/null +++ b/testdata/megaeth/README.md @@ -0,0 +1,106 @@ +# MegaETH Test Cases + +Verify `forge test --megaeth` produces correct MegaETH EVM semantics. + +For automated E2E tests covering CLI flag rejection and cross-validation, see +[`crates/forge/tests/cli/megaeth.rs`](../../crates/forge/tests/cli/megaeth.rs) — +those run in CI via `.github/workflows/build-and-test.yml`, plus a daily live +cross-validation in `.github/workflows/megaeth-live-validate.yml`. + +This directory is for **manual** exploration and gas inspection. + +## Setup + +```bash +cd /path/to/foundry + +# Build +cargo build -p forge +export PATH="$(pwd)/target/debug:$PATH" + +# Install forge-std (ignored via .gitignore — do not commit) +cd testdata/megaeth +forge install foundry-rs/forge-std --no-git +``` + +## Run + +```bash +# Ethereum mode (baseline) +forge test -vvv + +# MegaETH mode +forge test --megaeth -vvv + +# Coverage +forge coverage --megaeth --report lcov +``` + +## Expected + +11 tests across 2 files pass in both modes. Gas values differ between modes: + +### `MegaETH.t.sol` — gas semantics divergence (7 tests) + +| Test | Ethereum | MegaETH | What it verifies | +|---|---|---|---| +| test_gasForwardingRatio | ~50k | ~90k | 63/64 vs 98/100 gas forwarding | +| test_maxRecursionDepth | ~925k | ~667k | Different retention → different depth | +| test_twoHopGasDecay | ~106k | ~145k | Multi-hop gas decay | +| test_mint | ~11k | ~51k | Storage gas metering | +| test_transfer | ~37k | ~77k | Storage gas metering | +| test_transferInsufficientBalance | ~8k | ~48k | Revert handling | +| test_crossValidate | ~123k | ~162k | Composite: SSTORE + compute + CALL | + +### `CrossValidate.t.sol` — mega-evme reference values (4 tests) + +| Test | Verifies | +|---|---| +| test_basicReturn | `0x604260005260206000f3` → return `0x...42` | +| test_inputEcho | `0x366000600037366000f3` with `0xdeadbeef` input → echoes back | +| test_revert | `0x5f5ffd` → reverts | +| test_gasProbe_crossValidate | `GasProbe.run()` returns exactly **95086** (hardcoded mega-evme ref) | + +Reference values are pinned to `mega-evm = 1.5.1`. See the automated +`megaeth_live_cross_validate` test for how to detect drift. + +## Unsupported flag combinations + +These now error out rather than silently degrade: + +| Combination | Behavior | +|---|---| +| `forge test --megaeth --isolate` | rejected — isolation not implemented | +| `forge test --megaeth --fork-url ` | rejected — no fork-aware external env | +| `forge coverage --megaeth --isolate` | rejected | +| `forge coverage --megaeth --fork-url ` | rejected | +| `/// forge-config: default.isolate = true` + `--megaeth` | rejected at test time | + +## Cross-Validate with mega-evme (manual) + +For deeper debugging. The automated version lives in +`crates/forge/tests/cli/megaeth.rs::megaeth_live_cross_validate`. + +```bash +# 1. Get runtime bytecode for GasProbe +forge inspect GasProbe deployedBytecode + +# 2. Run with mega-evme (reference) +# selector for run() = 0xc0406226 +mega-evme run --json --spec Rex4 --gas 10000000 \ + --input 0xc0406226 "0x" + +# 3. Run with forge --megaeth +forge test --megaeth --match-test test_gasProbe_crossValidate -vvvv + +# 4. Compare: +# mega-evme "output" field (hex, decoded) = forge trace return value +# Both must return 95086 (0x1736e) for GasProbe::run(). +``` + +## Limitations + +- **Cheatcodes silently skipped**: `vm.prank`, `vm.deal`, `vm.expectRevert` do nothing under `--megaeth`. Tests here use pure Solidity assertions only. +- **console.log disabled**: use `-vvvv` trace output instead. +- **Isolation mode**: not implemented — `--isolate` and inline `isolate = true` are rejected (see table above). +- **Fork mode**: not implemented — external environment reads (Oracle / SALT bucket) use stubbed defaults, so `--fork-url` is rejected to prevent incorrect results. diff --git a/testdata/megaeth/foundry.toml b/testdata/megaeth/foundry.toml new file mode 100644 index 0000000000000..25b918f9c9a96 --- /dev/null +++ b/testdata/megaeth/foundry.toml @@ -0,0 +1,6 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/testdata/megaeth/test/CrossValidate.t.sol b/testdata/megaeth/test/CrossValidate.t.sol new file mode 100644 index 0000000000000..535b284d24184 --- /dev/null +++ b/testdata/megaeth/test/CrossValidate.t.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.sol"; + +/// @notice Cross-validate forge --megaeth against mega-evme. +/// +/// NOTE: Cheatcodes are silently skipped under --megaeth, so all assertions +/// use `require()` instead of `assertEq()`. +/// +/// mega-evme defaults: --gas 10000000, --spec Rex4, --basefee 0 +contract CrossValidate is Test { + /// Deploy raw runtime bytecode via CREATE. + function _deployRuntime(bytes memory runtime) internal returns (address addr) { + // Init code layout (11 bytes header + runtime): + // PUSH1 60 xx + // DUP1 80 + // PUSH1 11 60 0b ← runtime starts at offset 11 + // PUSH1 0 60 00 + // CODECOPY 39 + // PUSH1 0 60 00 + // RETURN f3 + // + bytes memory creationCode = abi.encodePacked( + uint8(0x60), uint8(runtime.length), + uint8(0x80), + uint8(0x60), uint8(11), + uint8(0x60), uint8(0), + uint8(0x39), + uint8(0x60), uint8(0), + uint8(0xf3), + runtime + ); + assembly { + addr := create(0, add(creationCode, 0x20), mload(creationCode)) + } + require(addr != address(0), "deploy failed"); + } + + /// @dev mega-evme reference: + /// mega-evme run --json --spec Rex4 0x604260005260206000f3 + /// → success=true, output=0x...42 + function test_basicReturn() public { + address target = _deployRuntime(hex"604260005260206000f3"); + + (bool ok, bytes memory ret) = target.call(""); + require(ok, "call failed"); + require(ret.length == 32, "unexpected return length"); + + uint256 val; + assembly { val := mload(add(ret, 0x20)) } + require(val == 0x42, "wrong return value"); + } + + /// @dev mega-evme reference: + /// mega-evme run --json --spec Rex4 --input 0xdeadbeef 0x366000600037366000f3 + /// → success=true, output=0xdeadbeef + function test_inputEcho() public { + address target = _deployRuntime(hex"366000600037366000f3"); + + (bool ok, bytes memory ret) = target.call(hex"deadbeef"); + require(ok, "call failed"); + require(ret.length == 4, "unexpected return length"); + require( + keccak256(ret) == keccak256(hex"deadbeef"), + "wrong echo" + ); + } + + /// @dev mega-evme reference: + /// mega-evme run --json --spec Rex4 0x5f5ffd + /// → success=false + function test_revert() public { + address target = _deployRuntime(hex"5f5ffd"); + + (bool ok, ) = target.call(""); + require(!ok, "should have reverted"); + } + + /// @dev Cross-validate GasProbe self-measurement. + /// + /// mega-evme run --json --spec Rex4 --gas 10000000 --input 0xc0406226 + /// → output = 0x...1736e = 95086 + /// + /// forge test --megaeth trace → GasProbe::run() returns 95086 + /// + /// Both engines agree on the internal gas measurement. This is the + /// strongest cross-validation: the contract measures its own gas via + /// gasleft(), which exercises SSTORE, compute loop, and STATICCALL gas. + function test_gasProbe_crossValidate() public { + GasProbe probe = new GasProbe(); + uint256 reported = probe.run(); + + // Must match mega-evme's reference value exactly. + require(reported == 95086, "gasProbe mismatch vs mega-evme"); + } +} + +/// @dev Composite gas measurement contract. +/// Exercises SSTORE (cold + warm), compute loop, and STATICCALL. +/// run() returns the gasleft() delta — the same value mega-evme reports +/// when invoked with selector 0xc0406226. +contract GasProbe { + uint256 public slot0; + uint256 public slot1; + + function run() external returns (uint256 gasUsed) { + uint256 g0 = gasleft(); + slot0 = 42; + slot1 = 84; + uint256 x = 1; + for (uint256 i = 0; i < 100; i++) { + x = x * 3 + 1; + } + address(this).staticcall(abi.encodeWithSelector(this.slot0.selector)); + uint256 g1 = gasleft(); + gasUsed = g0 - g1; + slot0 = gasUsed; + } +} diff --git a/testdata/megaeth/test/MegaETH.t.sol b/testdata/megaeth/test/MegaETH.t.sol new file mode 100644 index 0000000000000..5f3667eea71ea --- /dev/null +++ b/testdata/megaeth/test/MegaETH.t.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import {Test} from "forge-std/Test.sol"; + +contract GasReporter { + uint256 public storedGas; + + function reportGas() external returns (uint256) { + storedGas = gasleft(); + return storedGas; + } + + function recurse(uint256 depth) external returns (uint256) { + if (gasleft() < 10000) return depth; + try this.recurse(depth + 1) returns (uint256 d) { + return d; + } catch { + return depth; + } + } + + function chainCall(address next) external returns (uint256 myGas, uint256 childGas) { + myGas = gasleft(); + childGas = GasReporter(next).reportGas(); + } +} + +// Gas forwarding ratio: Ethereum 63/64 ≈ 98.44% vs MegaETH 98/100 = 98.00% +contract Case1_GasForwarding is Test { + GasReporter reporter; + + function setUp() public { + reporter = new GasReporter(); + } + + function test_gasForwardingRatio() public { + uint256 parentGas = gasleft(); + uint256 childGas = reporter.reportGas(); + uint256 ratioBps = (childGas * 10000) / parentGas; + assembly { sstore(0xff, ratioBps) } + } +} + +// Recursive depth: different gas retention → different max depth +contract Case2_RecursionDepth is Test { + GasReporter reporter; + + function setUp() public { + reporter = new GasReporter(); + } + + function test_maxRecursionDepth() public { + uint256 depth = reporter.recurse(0); + assembly { sstore(0xff, depth) } + } +} + +// Chained calls: two-hop gas decay amplifies the forwarding difference +contract Case3_ChainedCalls is Test { + GasReporter a; + GasReporter b; + GasReporter c; + + function setUp() public { + a = new GasReporter(); + b = new GasReporter(); + c = new GasReporter(); + } + + function test_twoHopGasDecay() public { + uint256 directGas = c.reportGas(); + (, uint256 twoHopGas) = a.chainCall(address(b)); + assembly { + sstore(0xfe, directGas) + sstore(0xff, twoHopGas) + } + } +} + +// Basic functionality without cheatcodes +contract SimpleToken { + mapping(address => uint256) public balanceOf; + uint256 public totalSupply; + + function mint(address to, uint256 amount) external { + balanceOf[to] += amount; + totalSupply += amount; + } + + function transfer(address to, uint256 amount) external { + require(balanceOf[msg.sender] >= amount, "insufficient balance"); + balanceOf[msg.sender] -= amount; + balanceOf[to] += amount; + } +} + +contract Case4_BasicFunctionality is Test { + SimpleToken token; + + function setUp() public { + token = new SimpleToken(); + token.mint(address(this), 1000); + } + + function test_mint() public view { + assertEq(token.balanceOf(address(this)), 1000); + assertEq(token.totalSupply(), 1000); + } + + function test_transfer() public { + address bob = address(0xB0B); + token.transfer(bob, 300); + assertEq(token.balanceOf(address(this)), 700); + assertEq(token.balanceOf(bob), 300); + } + + function test_transferInsufficientBalance() public { + address bob = address(0xB0B); + try token.transfer(bob, 2000) { + fail(); + } catch {} + } +} + +// Cross-validation with mega-evme: run() returns internal gasUsed +contract GasProbe { + uint256 public slot0; + uint256 public slot1; + + function run() external returns (uint256 gasUsed) { + uint256 g0 = gasleft(); + slot0 = 42; + slot1 = 84; + uint256 x = 1; + for (uint256 i = 0; i < 100; i++) { + x = x * 3 + 1; + } + address(this).staticcall(abi.encodeWithSelector(this.slot0.selector)); + uint256 g1 = gasleft(); + gasUsed = g0 - g1; + slot0 = gasUsed; + } +} + +contract Case5_CrossValidation is Test { + GasProbe probe; + + function setUp() public { + probe = new GasProbe(); + } + + function test_crossValidate() public { + uint256 gasUsed = probe.run(); + assembly { sstore(0xff, gasUsed) } + } +}