diff --git a/.cargo/config.toml.example b/.cargo/config.toml.example new file mode 100644 index 00000000000..b6eae13d3ec --- /dev/null +++ b/.cargo/config.toml.example @@ -0,0 +1,7 @@ +# Local development overrides for cross-repo work with ethrex-tooling. +# Append this section to your .cargo/config.toml to use local ethrex-tooling sources +# instead of git-fetched versions. Adjust paths to match your local layout. + +[patch."https://github.com/lambdaclass/ethrex-tooling"] +ethrex-monitor = { path = "../ethrex-tooling/monitor" } +ethrex-repl = { path = "../ethrex-tooling/repl" } diff --git a/.dockerignore b/.dockerignore index 3cf0943fe09..7ae42e33eca 100644 --- a/.dockerignore +++ b/.dockerignore @@ -19,7 +19,5 @@ docs/ # Local development hive/ ethereum-package/ -tooling/ef_tests/blockchain/vectors -tooling/ef_tests/state/vectors dev_ethrex_l1/ dev_ethrex_l2/ diff --git a/.github/workflows/daily_hive_report.yaml b/.github/workflows/daily_hive_report.yaml index feec5ef2de5..52bf2b02a8e 100644 --- a/.github/workflows/daily_hive_report.yaml +++ b/.github/workflows/daily_hive_report.yaml @@ -169,6 +169,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Setup Rust Environment uses: ./.github/actions/setup-rust diff --git a/.github/workflows/daily_loc_report.yaml b/.github/workflows/daily_loc_report.yaml index 3ae68fb08c1..921ec546e62 100644 --- a/.github/workflows/daily_loc_report.yaml +++ b/.github/workflows/daily_loc_report.yaml @@ -31,6 +31,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Setup Rust Environment uses: ./.github/actions/setup-rust diff --git a/.github/workflows/pr-main_l1.yaml b/.github/workflows/pr-main_l1.yaml index f1962402b8d..d5ed44558f6 100644 --- a/.github/workflows/pr-main_l1.yaml +++ b/.github/workflows/pr-main_l1.yaml @@ -65,6 +65,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Free Disk Space uses: ./.github/actions/free-disk @@ -110,6 +116,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Free Disk Space uses: ./.github/actions/free-disk @@ -343,6 +355,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Free Disk Space uses: ./.github/actions/free-disk @@ -381,6 +399,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Free Disk Space uses: ./.github/actions/free-disk diff --git a/.github/workflows/pr-main_levm.yaml b/.github/workflows/pr-main_levm.yaml index 2702731e167..012738f0c7b 100644 --- a/.github/workflows/pr-main_levm.yaml +++ b/.github/workflows/pr-main_levm.yaml @@ -6,11 +6,9 @@ on: pull_request: branches: ["**"] paths: - - "tooling/ef_tests/state/**" - "crates/vm/levm/**" - "crates/common/crypto/**" - ".github/workflows/pr-main_levm.yaml" - - "tooling/ef_tests/state" workflow_dispatch: concurrency: @@ -36,6 +34,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Setup Rust Environment uses: ./.github/actions/setup-rust @@ -81,6 +85,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Setup Rust Environment uses: ./.github/actions/setup-rust @@ -96,6 +106,12 @@ jobs: uses: actions/checkout@v4 with: ref: main + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Setup Rust Environment uses: ./.github/actions/setup-rust @@ -135,6 +151,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Download main branch ef tests uses: actions/download-artifact@v4 diff --git a/.github/workflows/pr_lint_license.yaml b/.github/workflows/pr_lint_license.yaml index 508c0c51c4c..9e72224b55e 100644 --- a/.github/workflows/pr_lint_license.yaml +++ b/.github/workflows/pr_lint_license.yaml @@ -24,6 +24,12 @@ jobs: steps: - name: Checkout sources uses: actions/checkout@v4 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Check license files exist run: | diff --git a/.github/workflows/pr_loc.yaml b/.github/workflows/pr_loc.yaml index c583c382083..9f9f379f455 100644 --- a/.github/workflows/pr_loc.yaml +++ b/.github/workflows/pr_loc.yaml @@ -42,6 +42,12 @@ jobs: with: fetch-depth: 0 ref: ${{ steps.find_merge_base.outputs.merge_base }} + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Setup Rust Environment uses: ./.github/actions/setup-rust @@ -60,6 +66,12 @@ jobs: with: clean: "false" # Don't clean the workspace, so we can keep the previous report ref: ${{ github.event.pull_request.head.sha }} + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Setup Rust Environment uses: ./.github/actions/setup-rust diff --git a/.github/workflows/pr_upgradeability.yaml b/.github/workflows/pr_upgradeability.yaml index c6f8a18ba7a..00c4cb209c9 100644 --- a/.github/workflows/pr_upgradeability.yaml +++ b/.github/workflows/pr_upgradeability.yaml @@ -19,6 +19,12 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Checkout ethrex-tooling + uses: actions/checkout@v4 + with: + repository: lambdaclass/ethrex-tooling + ref: main + path: tooling - name: Setup Node.js uses: actions/setup-node@v4 diff --git a/Cargo.lock b/Cargo.lock index 334280fb331..ac01475ad9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1386,9 +1386,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.16.2" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" dependencies = [ "aws-lc-sys", "zeroize", @@ -1396,9 +1396,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.39.1" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" +checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" dependencies = [ "cc", "cmake", @@ -2349,9 +2349,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -2371,9 +2371,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -4174,7 +4174,8 @@ dependencies = [ [[package]] name = "ethrex-monitor" -version = "9.0.0" +version = "4.0.0" +source = "git+https://github.com/lambdaclass/ethrex-tooling?branch=main#839e211e84fe5096212f2702af3ee6766225907c" dependencies = [ "bytes", "chrono", @@ -4280,7 +4281,8 @@ dependencies = [ [[package]] name = "ethrex-repl" -version = "9.0.0" +version = "4.0.0" +source = "git+https://github.com/lambdaclass/ethrex-tooling?branch=main#839e211e84fe5096212f2702af3ee6766225907c" dependencies = [ "clap", "colored 2.2.0", @@ -4687,12 +4689,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" -[[package]] -name = "fixedbitset" -version = "0.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" - [[package]] name = "flate2" version = "1.1.9" @@ -5682,9 +5678,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.8" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2b52f86d1d4bc0d6b4e6826d960b1b333217e07d36b882dca570a5e1c48895b" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http 1.4.0", "hyper 1.9.0", @@ -10175,17 +10171,7 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ - "fixedbitset 0.4.2", - "indexmap 2.14.0", -] - -[[package]] -name = "petgraph" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" -dependencies = [ - "fixedbitset 0.5.7", + "fixedbitset", "indexmap 2.14.0", ] @@ -10687,7 +10673,7 @@ dependencies = [ "log", "multimap", "once_cell", - "petgraph 0.6.5", + "petgraph", "prettyplease", "prost 0.12.6", "prost-types 0.12.6", @@ -10707,7 +10693,7 @@ dependencies = [ "log", "multimap", "once_cell", - "petgraph 0.7.1", + "petgraph", "prettyplease", "prost 0.13.5", "prost-types 0.13.5", @@ -11240,7 +11226,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.9.0", - "hyper-rustls 0.27.8", + "hyper-rustls 0.27.9", "hyper-tls", "hyper-util", "js-sys", @@ -11285,7 +11271,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.9.0", - "hyper-rustls 0.27.8", + "hyper-rustls 0.27.9", "hyper-util", "js-sys", "log", @@ -13930,9 +13916,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.51.1" +version = "1.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" +checksum = "a91135f59b1cbf38c91e73cf3386fca9bb77915c45ce2771460c9d92f0f3d776" dependencies = [ "bytes", "libc", @@ -14519,9 +14505,9 @@ dependencies = [ [[package]] name = "typewit" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc19094686c694eb41b3b99dcc2f2975d4b078512fa22ae6c63f7ca318bdcff7" +checksum = "214ca0b2191785cbc06209b9ca1861e048e39b5ba33574b3cedd58363d5bb5f6" [[package]] name = "ucd-trie" diff --git a/Cargo.toml b/Cargo.toml index c23d8199802..d757cc20de2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ members = [ "crates/vm/levm", "crates/vm/levm/runner", "crates/common/config", - "tooling/repl", "test", ] exclude = ["crates/vm/levm/bench/revm_comparison"] @@ -77,8 +76,8 @@ ethrex-guest-program = { path = "./crates/guest-program", default-features = fal ethrex-storage-rollup = { path = "./crates/l2/storage" } ethrex = { path = "./cmd/ethrex" } ethrex-l2-rpc = { path = "./crates/l2/networking/rpc" } -ethrex-monitor = { path = "./tooling/monitor" } -ethrex-repl = { path = "./tooling/repl" } +ethrex-monitor = { git = "https://github.com/lambdaclass/ethrex-tooling", branch = "main" } +ethrex-repl = { git = "https://github.com/lambdaclass/ethrex-tooling", branch = "main" } tracing = { version = "0.1", features = ["log"] } tracing-subscriber = { version = "0.3.0", features = ["env-filter"] } @@ -144,5 +143,29 @@ libssz-types = { git = "https://github.com/lambdaclass/libssz", rev = "7262a4f" libssz-merkle = { git = "https://github.com/lambdaclass/libssz", rev = "7262a4f" } libssz-derive = { git = "https://github.com/lambdaclass/libssz", rev = "7262a4f" } +# Ensure ethrex-tooling's transitive deps resolve to local workspace crates +# instead of fetching a second copy from git (avoids "two versions of the same crate" errors). +[patch."https://github.com/lambdaclass/ethrex"] +ethrex = { path = "./cmd/ethrex" } +ethrex-blockchain = { path = "./crates/blockchain" } +ethrex-common = { path = "./crates/common" } +ethrex-config = { path = "./crates/common/config" } +ethrex-crypto = { path = "./crates/common/crypto" } +ethrex-guest-program = { path = "./crates/guest-program" } +ethrex-l2 = { path = "./crates/l2" } +ethrex-l2-common = { path = "./crates/l2/common" } +ethrex-l2-prover = { path = "./crates/l2/prover" } +ethrex-l2-rpc = { path = "./crates/l2/networking/rpc" } +ethrex-levm = { path = "./crates/vm/levm" } +ethrex-p2p = { path = "./crates/networking/p2p" } +ethrex-prover = { path = "./crates/prover" } +ethrex-rlp = { path = "./crates/common/rlp" } +ethrex-rpc = { path = "./crates/networking/rpc" } +ethrex-sdk = { path = "./crates/l2/sdk" } +ethrex-storage = { path = "./crates/storage" } +ethrex-storage-rollup = { path = "./crates/l2/storage" } +ethrex-trie = { path = "./crates/common/trie" } +ethrex-vm = { path = "./crates/vm" } + [workspace.lints.clippy] redundant_clone = "warn" diff --git a/Dockerfile b/Dockerfile index 20be01ed9f3..988bbf03b05 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,6 @@ COPY crates ./crates COPY metrics ./metrics COPY cmd ./cmd COPY test ./test -COPY tooling ./tooling COPY Cargo.* . COPY .cargo/ ./.cargo @@ -42,7 +41,13 @@ ARG PROFILE="release" ARG BUILD_FLAGS="" COPY --from=planner /ethrex/recipe.json recipe.json -RUN cargo chef cook --release --recipe-path recipe.json $BUILD_FLAGS +# cargo-chef cook may fail on git dependencies (ethrex-monitor/ethrex-repl) +# because the [patch] section references local paths that don't have real +# source code yet (only stubs). This is fine — cook is a caching optimization, +# and the real build below has all sources available. +COPY Cargo.* ./ +COPY .cargo/ ./.cargo +RUN cargo chef cook --release --recipe-path recipe.json $BUILD_FLAGS || true RUN if [ "$(uname -m)" = aarch64 ]; \ then \ @@ -57,7 +62,6 @@ COPY benches ./benches COPY crates ./crates COPY cmd ./cmd COPY metrics ./metrics -COPY tooling ./tooling COPY fixtures/genesis ./fixtures/genesis COPY .git ./.git COPY Cargo.* ./ diff --git a/README.md b/README.md index 8579458e62a..7ec40c35930 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,10 @@ Minimalist, stable, modular, fast, and ZK native implementation of the Ethereum [tg-badge]: https://img.shields.io/endpoint?url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Fethrex_client%2F&logo=telegram&label=chat&color=neon [tg-url]: https://t.me/ethrex_client +## Tooling + +Development tools (EF tests, load tests, monitor TUI, REPL, benchmarks, etc.) live in a separate repository: [lambdaclass/ethrex-tooling](https://github.com/lambdaclass/ethrex-tooling). + ## Getting started For instructions on how to get started using ethrex L1 and/or L2, please refer to the ["Getting started" section of the docs](https://docs.ethrex.xyz/getting-started/index.html). diff --git a/crates/vm/levm/README.md b/crates/vm/levm/README.md index 47f7e8a0888..664579af1b3 100644 --- a/crates/vm/levm/README.md +++ b/crates/vm/levm/README.md @@ -18,7 +18,7 @@ There is a large amount of docs in comments inside the code. For more informatio ## Testing -We run `EELS`, `ethereum/tests` and `legacyTests` both in their [state](../../../tooling/ef_tests/state/README.md) and [blockchain](../../../tooling/ef_tests/blockchain/README.md) form. More info on each README. +We run `EELS`, `ethereum/tests` and `legacyTests` both in their [state](https://github.com/lambdaclass/ethrex-tooling/tree/main/ef_tests/state) and [blockchain](https://github.com/lambdaclass/ethrex-tooling/tree/main/ef_tests/blockchain) form. More info on each README. For running state tests from the current directory use: ``` diff --git a/docs/developers/l1/testing/ef-tests.md b/docs/developers/l1/testing/ef-tests.md index bc0a16b27b0..6680ae68473 100644 --- a/docs/developers/l1/testing/ef-tests.md +++ b/docs/developers/l1/testing/ef-tests.md @@ -1,5 +1,10 @@ # Ethereum foundation tests +> **Note:** EF test tooling lives in the [ethrex-tooling](https://github.com/lambdaclass/ethrex-tooling) repository. Clone it first: +> ```sh +> git clone https://github.com/lambdaclass/ethrex-tooling.git +> ``` + These are the official execution spec tests. There are two kinds: `state tests` and `blockchain tests`, you can execute them with: ### State tests @@ -10,7 +15,7 @@ See [docs](https://eest.ethereum.org/v4.1.0/consuming_tests/state_test/). To run the test first: ```sh -cd tooling/ef_tests/state +cd ethrex-tooling/ef_tests/state ``` then download the test vectors: @@ -34,7 +39,7 @@ See [docs](https://eest.ethereum.org/v4.1.0/consuming_tests/blockchain_test). To run the tests first: ```sh -cd tooling/ef_tests/blockchain +cd ethrex-tooling/ef_tests/blockchain ``` then run the tests: diff --git a/docs/developers/l1/testing/hive.md b/docs/developers/l1/testing/hive.md index 7db3e6ad690..9261e08c318 100644 --- a/docs/developers/l1/testing/hive.md +++ b/docs/developers/l1/testing/hive.md @@ -248,7 +248,7 @@ Log files include: To generate a formatted report from test results: ```bash -cargo run --manifest-path tooling/Cargo.toml -p hive_report +cargo run --manifest-path ethrex-tooling/Cargo.toml -p hive_report ``` This reads all JSON files in `hive/workspace/logs/` and produces: @@ -300,8 +300,8 @@ fi ### Fixtures URL Files -- `tooling/ef_tests/blockchain/.fixtures_url` — Used by `run-hive-eels` Makefile target (non-Amsterdam forks) -- `tooling/ef_tests/blockchain/.fixtures_url_amsterdam` — Amsterdam-specific fixtures with BAL support +- `ethrex-tooling/ef_tests/blockchain/.fixtures_url` — Used by `run-hive-eels` Makefile target (non-Amsterdam forks) +- `ethrex-tooling/ef_tests/blockchain/.fixtures_url_amsterdam` — Amsterdam-specific fixtures with BAL support Contents: @@ -345,14 +345,14 @@ To update to a different fork or newer versions: ```bash # For Amsterdam fixtures - echo "https://github.com/ethereum/execution-spec-tests/releases/download/bal@/fixtures_bal.tar.gz" > tooling/ef_tests/blockchain/.fixtures_url_amsterdam + echo "https://github.com/ethereum/execution-spec-tests/releases/download/bal@/fixtures_bal.tar.gz" > ethrex-tooling/ef_tests/blockchain/.fixtures_url_amsterdam # For other forks - echo "https://github.com/ethereum/execution-spec-tests/releases/download/v/fixtures_develop.tar.gz" > tooling/ef_tests/blockchain/.fixtures_url + echo "https://github.com/ethereum/execution-spec-tests/releases/download/v/fixtures_develop.tar.gz" > ethrex-tooling/ef_tests/blockchain/.fixtures_url ``` 4. **Update fork references** in code if switching to a different fork: - `.github/workflows/daily_hive_report.yaml` - Test names and patterns - - `tooling/hive_report/src/main.rs` - Fork ranking and result processing + - `ethrex-tooling/hive_report/src/main.rs` - Fork ranking and result processing ## Troubleshooting @@ -381,7 +381,7 @@ If EELS tests fail due to missing fixtures: ```bash # Verify fixtures URL is accessible -curl -I $(cat tooling/ef_tests/blockchain/.fixtures_url) +curl -I $(cat ethrex-tooling/ef_tests/blockchain/.fixtures_url) # Re-download fixtures (they're downloaded automatically during test execution) ``` diff --git a/docs/eip-8025.md b/docs/eip-8025.md index 3e1496949de..4811a54eef7 100644 --- a/docs/eip-8025.md +++ b/docs/eip-8025.md @@ -551,7 +551,7 @@ zkevm fixture JSON ```bash # Download zkevm fixtures -cd tooling/ef_tests/blockchain +cd ethrex-tooling/ef_tests/blockchain make zkevm-vectors # Run all blockchain EF tests (includes zkevm Amsterdam fixtures) @@ -572,7 +572,7 @@ The `zkevm@v0.2.0` Amsterdam fork definition differs from ethrex's: | EIP-7778 (Block Gas Without Refunds) | No | Yes | | EIP-7843 (SLOTNUM opcode) | No | Yes | -To handle this, zkevm tests run with an Osaka-level config (`ZKEVM_AMSTERDAM_CONFIG`) that excludes the unsupported EIPs, plus supplementary validation for edge cases. See the `TODO(zkevm)` comment in `tooling/ef_tests/blockchain/fork.rs` for the cleanup plan when zkevm adds support for these EIPs. +To handle this, zkevm tests run with an Osaka-level config (`ZKEVM_AMSTERDAM_CONFIG`) that excludes the unsupported EIPs, plus supplementary validation for edge cases. See the `TODO(zkevm)` comment in [`ethrex-tooling/ef_tests/blockchain/fork.rs`](https://github.com/lambdaclass/ethrex-tooling/blob/main/ef_tests/blockchain/fork.rs) for the cleanup plan when zkevm adds support for these EIPs. --- diff --git a/docs/workflows/prover_benchmarking.md b/docs/workflows/prover_benchmarking.md index bb65977da1e..81a3fa8bec9 100644 --- a/docs/workflows/prover_benchmarking.md +++ b/docs/workflows/prover_benchmarking.md @@ -92,7 +92,7 @@ Build the `ethrex` binary with the features needed for the chosen backend. The ` ssh "bash -l -c 'cd ~/ethrex && COMPILE_CONTRACTS=true cargo build --release --features --bin ethrex'" # Build load test -ssh "bash -l -c 'cd ~/ethrex && cargo build --release --manifest-path ./tooling/load_test/Cargo.toml'" +ssh "bash -l -c 'cd ~/ethrex-tooling && cargo build --release --manifest-path ./load_test/Cargo.toml'" ``` `COMPILE_CONTRACTS=true` compiles the Solidity contracts and embeds them in the binary. This only needs to happen once — the same binary is used for deploying, running the L2, and proving. @@ -185,10 +185,10 @@ Run the pre-compiled binary directly (built in step 1b) instead of `cargo run` t ```bash # ETH transfers (default): -ssh "bash -l -c 'cd ~/ethrex && nohup env LOAD_TEST_RPC_URL=http://localhost:1729 LOAD_TEST_TX_AMOUNT= ./tooling/target/release/load_test -k ./fixtures/keys/private_keys.txt -t eth-transfers > ~/loadtest.log 2>&1 &'" +ssh "bash -l -c 'cd ~/ethrex-tooling && nohup env LOAD_TEST_RPC_URL=http://localhost:1729 LOAD_TEST_TX_AMOUNT= ./target/release/load_test -k ../ethrex/fixtures/keys/private_keys.txt -t eth-transfers > ~/loadtest.log 2>&1 &'" # ERC20 transactions: -ssh "bash -l -c 'cd ~/ethrex && nohup env LOAD_TEST_RPC_URL=http://localhost:1729 LOAD_TEST_TX_AMOUNT= ./tooling/target/release/load_test -k ./fixtures/keys/private_keys.txt -t erc20 > ~/loadtest.log 2>&1 &'" +ssh "bash -l -c 'cd ~/ethrex-tooling && nohup env LOAD_TEST_RPC_URL=http://localhost:1729 LOAD_TEST_TX_AMOUNT= ./target/release/load_test -k ../ethrex/fixtures/keys/private_keys.txt -t erc20 > ~/loadtest.log 2>&1 &'" # For continuous load, add LOAD_TEST_ENDLESS=true to env vars. ``` diff --git a/tooling/Cargo.lock b/tooling/Cargo.lock deleted file mode 100644 index e4b0fa567e7..00000000000 --- a/tooling/Cargo.lock +++ /dev/null @@ -1,11548 +0,0 @@ -# This file is automatically @generated by Cargo. -# 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.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array 0.14.7", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures 0.2.17", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "agg_mode_sdk" -version = "0.1.0" -source = "git+https://github.com/yetanotherco/aligned_layer?rev=54ca2471624700536561b6bd369ed9f4d327991e#54ca2471624700536561b6bd369ed9f4d327991e" -dependencies = [ - "alloy", - "bincode", - "lambdaworks-crypto 0.12.0", - "reqwest 0.12.28", - "serde", - "serde_json", - "sha3", - "sp1-sdk", - "tokio", - "tracing", - "tracing-subscriber 0.3.23", -] - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "alloy" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50ab0cd8afe573d1f7dc2353698a51b1f93aec362c8211e28cfd3948c6adba39" -dependencies = [ - "alloy-consensus", - "alloy-contract", - "alloy-core", - "alloy-eips", - "alloy-genesis", - "alloy-network", - "alloy-provider", - "alloy-rpc-client", - "alloy-rpc-types", - "alloy-serde", - "alloy-signer", - "alloy-signer-local", - "alloy-transport", - "alloy-transport-http", - "alloy-trie", -] - -[[package]] -name = "alloy-chains" -version = "0.2.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e9e31d834fe25fe991b8884e4b9f0e59db4a97d86e05d1464d6899c013cd62" -dependencies = [ - "alloy-primitives", - "num_enum 0.7.6", - "strum 0.27.2", -] - -[[package]] -name = "alloy-consensus" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f16daaf7e1f95f62c6c3bf8a3fc3d78b08ae9777810c0bb5e94966c7cd57ef0" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "alloy-trie", - "alloy-tx-macros", - "auto_impl", - "borsh", - "c-kzg", - "derive_more 2.1.1", - "either", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell", - "rand 0.8.5", - "secp256k1 0.30.0", - "serde", - "serde_json", - "serde_with", - "thiserror 2.0.18", -] - -[[package]] -name = "alloy-consensus-any" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "118998d9015332ab1b4720ae1f1e3009491966a0349938a1f43ff45a8a4c6299" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "serde", -] - -[[package]] -name = "alloy-contract" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ac9e0c34dc6bce643b182049cdfcca1b8ce7d9c260cbdd561f511873b7e26cd" -dependencies = [ - "alloy-consensus", - "alloy-dyn-abi", - "alloy-json-abi", - "alloy-network", - "alloy-network-primitives", - "alloy-primitives", - "alloy-provider", - "alloy-rpc-types-eth", - "alloy-sol-types", - "alloy-transport", - "futures", - "futures-util", - "serde_json", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "alloy-core" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e8604b0c092fabc80d075ede181c9b9e596249c70b99253082d7e689836529" -dependencies = [ - "alloy-dyn-abi", - "alloy-json-abi", - "alloy-primitives", - "alloy-rlp", - "alloy-sol-types", -] - -[[package]] -name = "alloy-dyn-abi" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2db5c583aaef0255aa63a4fe827f826090142528bba48d1bf4119b62780cad" -dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-type-parser", - "alloy-sol-types", - "itoa", - "serde", - "serde_json", - "winnow 0.7.15", -] - -[[package]] -name = "alloy-eip2124" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "741bdd7499908b3aa0b159bba11e71c8cddd009a2c2eb7a06e825f1ec87900a5" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "crc", - "serde", - "thiserror 2.0.18", -] - -[[package]] -name = "alloy-eip2930" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9441120fa82df73e8959ae0e4ab8ade03de2aaae61be313fbf5746277847ce25" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "borsh", - "serde", -] - -[[package]] -name = "alloy-eip7702" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2919c5a56a1007492da313e7a3b6d45ef5edc5d33416fdec63c0d7a2702a0d20" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "borsh", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde", - "thiserror 2.0.18", -] - -[[package]] -name = "alloy-eip7928" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "borsh", - "serde", -] - -[[package]] -name = "alloy-eips" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ef28c9fdad22d4eec52d894f5f2673a0895f1e5ef196734568e68c0f6caca8" -dependencies = [ - "alloy-eip2124", - "alloy-eip2930", - "alloy-eip7702", - "alloy-eip7928", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "auto_impl", - "borsh", - "c-kzg", - "derive_more 2.1.1", - "either", - "serde", - "serde_with", - "sha2 0.10.9", -] - -[[package]] -name = "alloy-genesis" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbf9480307b09d22876efb67d30cadd9013134c21f3a17ec9f93fd7536d38024" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "alloy-serde", - "alloy-trie", - "borsh", - "serde", - "serde_with", -] - -[[package]] -name = "alloy-json-abi" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" -dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-json-rpc" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422d110f1c40f1f8d0e5562b0b649c35f345fccb7093d9f02729943dcd1eef71" -dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "http", - "serde", - "serde_json", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "alloy-network" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7197a66d94c4de1591cdc16a9bcea5f8cccd0da81b865b49aef97b1b4016e0fa" -dependencies = [ - "alloy-consensus", - "alloy-consensus-any", - "alloy-eips", - "alloy-json-rpc", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rpc-types-any", - "alloy-rpc-types-eth", - "alloy-serde", - "alloy-signer", - "alloy-sol-types", - "async-trait", - "auto_impl", - "derive_more 2.1.1", - "futures-utils-wasm", - "serde", - "serde_json", - "thiserror 2.0.18", -] - -[[package]] -name = "alloy-network-primitives" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb82711d59a43fdfd79727c99f270b974c784ec4eb5728a0d0d22f26716c87ef" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-serde", - "serde", -] - -[[package]] -name = "alloy-primitives" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" -dependencies = [ - "alloy-rlp", - "bytes", - "cfg-if", - "const-hex", - "derive_more 2.1.1", - "foldhash 0.2.0", - "hashbrown 0.16.1", - "indexmap 2.14.0", - "itoa", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "keccak-asm", - "paste", - "proptest", - "rand 0.9.3", - "rapidhash", - "ruint", - "rustc-hash 2.1.2", - "serde", - "sha3", -] - -[[package]] -name = "alloy-provider" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6b18b929ef1d078b834c3631e9c925177f3b23ddc6fa08a722d13047205876" -dependencies = [ - "alloy-chains", - "alloy-consensus", - "alloy-eips", - "alloy-json-rpc", - "alloy-network", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rpc-client", - "alloy-rpc-types-eth", - "alloy-signer", - "alloy-sol-types", - "alloy-transport", - "alloy-transport-http", - "async-stream", - "async-trait", - "auto_impl", - "dashmap 6.1.0", - "either", - "futures", - "futures-utils-wasm", - "lru 0.16.3", - "parking_lot 0.12.5", - "pin-project", - "reqwest 0.13.2", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", - "url", - "wasmtimer", -] - -[[package]] -name = "alloy-rlp" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" -dependencies = [ - "alloy-rlp-derive", - "arrayvec", - "bytes", -] - -[[package]] -name = "alloy-rlp-derive" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36834a5c0a2fa56e171bf256c34d70fca07d0c0031583edea1c4946b7889c9e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "alloy-rpc-client" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fcc9604042ca80bd37aa5e232ea1cd851f337e31e2babbbb345bc0b1c30de3" -dependencies = [ - "alloy-json-rpc", - "alloy-primitives", - "alloy-transport", - "alloy-transport-http", - "futures", - "pin-project", - "reqwest 0.13.2", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tower 0.5.3", - "tracing", - "url", - "wasmtimer", -] - -[[package]] -name = "alloy-rpc-types" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4faad925d3a669ffc15f43b3deec7fbdf2adeb28a4d6f9cf4bc661698c0f8f4b" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-serde", - "serde", -] - -[[package]] -name = "alloy-rpc-types-any" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3823026d1ed239a40f12364fac50726c8daf1b6ab8077a97212c5123910429ed" -dependencies = [ - "alloy-consensus-any", - "alloy-rpc-types-eth", - "alloy-serde", -] - -[[package]] -name = "alloy-rpc-types-engine" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb9b97b6e7965679ad22df297dda809b11cebc13405c1b537e5cffecc95834fa" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "derive_more 2.1.1", - "rand 0.8.5", - "serde", - "strum 0.27.2", -] - -[[package]] -name = "alloy-rpc-types-eth" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c095f92c4e1ff4981d89e9aa02d5f98c762a1980ab66bec49c44be11349da2" -dependencies = [ - "alloy-consensus", - "alloy-consensus-any", - "alloy-eips", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rlp", - "alloy-serde", - "alloy-sol-types", - "itertools 0.14.0", - "serde", - "serde_json", - "serde_with", - "thiserror 2.0.18", -] - -[[package]] -name = "alloy-serde" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ece63b89294b8614ab3f483560c08d016930f842bf36da56bf0b764a15c11e" -dependencies = [ - "alloy-primitives", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-signer" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f447aefab0f1c0649f71edc33f590992d4e122bc35fb9cdbbf67d4421ace85" -dependencies = [ - "alloy-primitives", - "async-trait", - "auto_impl", - "either", - "elliptic-curve", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 2.0.18", -] - -[[package]] -name = "alloy-signer-local" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f721f4bf2e4812e5505aaf5de16ef3065a8e26b9139ac885862d00b5a55a659a" -dependencies = [ - "alloy-consensus", - "alloy-network", - "alloy-primitives", - "alloy-signer", - "async-trait", - "eth-keystore", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.8.5", - "thiserror 2.0.18", -] - -[[package]] -name = "alloy-sol-macro" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" -dependencies = [ - "alloy-sol-macro-expander", - "alloy-sol-macro-input", - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "alloy-sol-macro-expander" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" -dependencies = [ - "alloy-json-abi", - "alloy-sol-macro-input", - "const-hex", - "heck 0.5.0", - "indexmap 2.14.0", - "proc-macro-error2", - "proc-macro2", - "quote", - "sha3", - "syn 2.0.117", - "syn-solidity", -] - -[[package]] -name = "alloy-sol-macro-input" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" -dependencies = [ - "alloy-json-abi", - "const-hex", - "dunce", - "heck 0.5.0", - "macro-string", - "proc-macro2", - "quote", - "serde_json", - "syn 2.0.117", - "syn-solidity", -] - -[[package]] -name = "alloy-sol-type-parser" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" -dependencies = [ - "serde", - "winnow 0.7.15", -] - -[[package]] -name = "alloy-sol-types" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" -dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-macro", - "serde", -] - -[[package]] -name = "alloy-transport" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8098f965442a9feb620965ba4b4be5e2b320f4ec5a3fff6bfa9e1ff7ef42bed1" -dependencies = [ - "alloy-json-rpc", - "auto_impl", - "base64", - "derive_more 2.1.1", - "futures", - "futures-utils-wasm", - "parking_lot 0.12.5", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tower 0.5.3", - "tracing", - "url", - "wasmtimer", -] - -[[package]] -name = "alloy-transport-http" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8597d36d546e1dab822345ad563243ec3920e199322cb554ce56c8ef1a1e2e7" -dependencies = [ - "alloy-json-rpc", - "alloy-transport", - "itertools 0.14.0", - "reqwest 0.13.2", - "serde_json", - "tower 0.5.3", - "tracing", - "url", -] - -[[package]] -name = "alloy-trie" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f14b5d9b2c2173980202c6ff470d96e7c5e202c65a9f67884ad565226df7fbb" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "derive_more 2.1.1", - "nybbles", - "serde", - "smallvec", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "alloy-tx-macros" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69722eddcdf1ce096c3ab66cf8116999363f734eb36fe94a148f4f71c85da84" -dependencies = [ - "darling 0.23.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "anstream" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" - -[[package]] -name = "anstyle-parse" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" -dependencies = [ - "anstyle", - "once_cell_polyfill", - "windows-sys 0.61.2", -] - -[[package]] -name = "anyhow" -version = "1.0.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" - -[[package]] -name = "archive_sync" -version = "4.0.0" -dependencies = [ - "clap 4.6.0", - "clap_complete", - "ethrex", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-rlp 9.0.0", - "ethrex-rpc", - "ethrex-storage 9.0.0", - "eyre", - "hex", - "lazy_static", - "reqwest 0.12.28", - "serde", - "serde_json", - "tokio", - "tracing", - "tracing-subscriber 0.3.23", -] - -[[package]] -name = "ark-bls12-381" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" -dependencies = [ - "ark-ec", - "ark-ff 0.5.0", - "ark-serialize 0.5.0", - "ark-std 0.5.0", -] - -[[package]] -name = "ark-bn254" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" -dependencies = [ - "ark-ec", - "ark-ff 0.5.0", - "ark-r1cs-std", - "ark-std 0.5.0", -] - -[[package]] -name = "ark-ec" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" -dependencies = [ - "ahash", - "ark-ff 0.5.0", - "ark-poly", - "ark-serialize 0.5.0", - "ark-std 0.5.0", - "educe", - "fnv", - "hashbrown 0.15.5", - "itertools 0.13.0", - "num-bigint 0.4.6", - "num-integer", - "num-traits", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" -dependencies = [ - "ark-ff-asm 0.3.0", - "ark-ff-macros 0.3.0", - "ark-serialize 0.3.0", - "ark-std 0.3.0", - "derivative", - "num-bigint 0.4.6", - "num-traits", - "paste", - "rustc_version 0.3.3", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" -dependencies = [ - "ark-ff-asm 0.4.2", - "ark-ff-macros 0.4.2", - "ark-serialize 0.4.2", - "ark-std 0.4.0", - "derivative", - "digest 0.10.7", - "itertools 0.10.5", - "num-bigint 0.4.6", - "num-traits", - "paste", - "rustc_version 0.4.1", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" -dependencies = [ - "ark-ff-asm 0.5.0", - "ark-ff-macros 0.5.0", - "ark-serialize 0.5.0", - "ark-std 0.5.0", - "arrayvec", - "digest 0.10.7", - "educe", - "itertools 0.13.0", - "num-bigint 0.4.6", - "num-traits", - "paste", - "zeroize", -] - -[[package]] -name = "ark-ff-asm" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-asm" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-asm" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" -dependencies = [ - "quote", - "syn 2.0.117", -] - -[[package]] -name = "ark-ff-macros" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" -dependencies = [ - "num-bigint 0.4.6", - "num-traits", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" -dependencies = [ - "num-bigint 0.4.6", - "num-traits", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "ark-ff-macros" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" -dependencies = [ - "num-bigint 0.4.6", - "num-traits", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "ark-poly" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" -dependencies = [ - "ahash", - "ark-ff 0.5.0", - "ark-serialize 0.5.0", - "ark-std 0.5.0", - "educe", - "fnv", - "hashbrown 0.15.5", -] - -[[package]] -name = "ark-r1cs-std" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "941551ef1df4c7a401de7068758db6503598e6f01850bdb2cfdb614a1f9dbea1" -dependencies = [ - "ark-ec", - "ark-ff 0.5.0", - "ark-relations", - "ark-std 0.5.0", - "educe", - "num-bigint 0.4.6", - "num-integer", - "num-traits", - "tracing", -] - -[[package]] -name = "ark-relations" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec46ddc93e7af44bcab5230937635b06fb5744464dd6a7e7b083e80ebd274384" -dependencies = [ - "ark-ff 0.5.0", - "ark-std 0.5.0", - "tracing", - "tracing-subscriber 0.2.25", -] - -[[package]] -name = "ark-serialize" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" -dependencies = [ - "ark-std 0.3.0", - "digest 0.9.0", -] - -[[package]] -name = "ark-serialize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" -dependencies = [ - "ark-std 0.4.0", - "digest 0.10.7", - "num-bigint 0.4.6", -] - -[[package]] -name = "ark-serialize" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" -dependencies = [ - "ark-serialize-derive", - "ark-std 0.5.0", - "arrayvec", - "digest 0.10.7", - "num-bigint 0.4.6", -] - -[[package]] -name = "ark-serialize-derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "ark-std" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" -dependencies = [ - "num-traits", - "rand 0.8.5", -] - -[[package]] -name = "ark-std" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" -dependencies = [ - "num-traits", - "rand 0.8.5", -] - -[[package]] -name = "ark-std" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" -dependencies = [ - "num-traits", - "rand 0.8.5", -] - -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "async-trait" -version = "0.1.89" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "atomic-waker" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "aurora-engine-modexp" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "518bc5745a6264b5fd7b09dffb9667e400ee9e2bbe18555fac75e1fe9afa0df9" -dependencies = [ - "hex", - "num", -] - -[[package]] -name = "auto_impl" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "aws-lc-rs" -version = "1.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.39.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - -[[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core 0.4.5", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit 0.7.3", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tower 0.5.3", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" -dependencies = [ - "axum-core 0.5.6", - "base64", - "bytes", - "form_urlencoded", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "itoa", - "matchit 0.8.4", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "serde_core", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sha1", - "sync_wrapper", - "tokio", - "tokio-tungstenite", - "tower 0.5.3", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-core" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "sync_wrapper", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "axum-extra" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9963ff19f40c6102c76756ef0a46004c0d58957d87259fc9208ff8441c12ab96" -dependencies = [ - "axum 0.8.8", - "axum-core 0.5.6", - "bytes", - "futures-util", - "headers", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "serde_core", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "az" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be5eb007b7cacc6c660343e96f650fedf4b5a77512399eb952ca6642cf8d13f7" - -[[package]] -name = "backoff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" -dependencies = [ - "futures-core", - "getrandom 0.2.17", - "instant", - "pin-project-lite", - "rand 0.8.5", - "tokio", -] - -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "serde", - "windows-link", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - -[[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.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" -dependencies = [ - "bitflags 2.11.0", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "log", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.117", -] - -[[package]] -name = "bindgen" -version = "0.71.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" -dependencies = [ - "bitflags 2.11.0", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "proc-macro2", - "quote", - "regex", - "rustc-hash 2.1.2", - "shlex", - "syn 2.0.117", -] - -[[package]] -name = "bindgen" -version = "0.72.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" -dependencies = [ - "bitflags 2.11.0", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "proc-macro2", - "quote", - "regex", - "rustc-hash 2.1.2", - "shlex", - "syn 2.0.117", -] - -[[package]] -name = "bit-set" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" -dependencies = [ - "bit-vec", -] - -[[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - -[[package]] -name = "bitcoin-io" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" - -[[package]] -name = "bitcoin_hashes" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" -dependencies = [ - "bitcoin-io", - "hex-conservative", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" -dependencies = [ - "serde_core", -] - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "serde", - "tap", - "wyz", -] - -[[package]] -name = "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "block2" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" -dependencies = [ - "objc2", -] - -[[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 = "bls12_381" -version = "0.8.0" -source = "git+https://github.com/lambdaclass/bls12_381?branch=expose-affine-constructors#78cad0378b17fc3157b83f514be192bf46edf9a1" -dependencies = [ - "digest 0.10.7", - "ff 0.13.1", - "group 0.13.0", - "pairing 0.23.0", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "blst" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcdb4c7013139a150f9fc55d123186dbfaba0d912817466282c73ac49e71fb45" -dependencies = [ - "cc", - "glob", - "threadpool", - "zeroize", -] - -[[package]] -name = "borsh" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" -dependencies = [ - "borsh-derive", - "bytes", - "cfg_aliases", -] - -[[package]] -name = "borsh-derive" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" -dependencies = [ - "once_cell", - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "bstr" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" - -[[package]] -name = "byte-slice-cast" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" - -[[package]] -name = "bytecheck" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0caa33a2c0edca0419d15ac723dff03f1956f7978329b1e3b5fdaaaed9d3ca8b" -dependencies = [ - "bytecheck_derive", - "ptr_meta", - "rancor", - "simdutf8", -] - -[[package]] -name = "bytecheck_derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "bytemuck" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" -dependencies = [ - "serde", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" -dependencies = [ - "cc", - "pkg-config", -] - -[[package]] -name = "c-kzg" -version = "2.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6648ed1e4ea8e8a1a4a2c78e1cda29a3fd500bc622899c340d8525ea9a76b24a" -dependencies = [ - "blst", - "cc", - "glob", - "hex", - "libc", - "once_cell", - "serde", -] - -[[package]] -name = "camino" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" -dependencies = [ - "serde_core", -] - -[[package]] -name = "cargo-platform" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver 1.0.28", - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "castaway" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" -dependencies = [ - "rustversion", -] - -[[package]] -name = "cbindgen" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fce8dd7fcfcbf3a0a87d8f515194b49d6135acab73e18bd380d1d93bb1a15eb" -dependencies = [ - "clap 4.6.0", - "heck 0.4.1", - "indexmap 2.14.0", - "log", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn 2.0.117", - "tempfile", - "toml 0.8.23", -] - -[[package]] -name = "cc" -version = "1.2.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] - -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" - -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "wasm-bindgen", - "windows-link", -] - -[[package]] -name = "chrono-tz" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" -dependencies = [ - "chrono", - "chrono-tz-build", - "phf", -] - -[[package]] -name = "chrono-tz-build" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" -dependencies = [ - "parse-zoneinfo", - "phf", - "phf_codegen", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags 1.3.2", - "strsim 0.8.0", - "textwrap", - "unicode-width 0.1.14", - "vec_map", -] - -[[package]] -name = "clap" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap_builder" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim 0.11.1", -] - -[[package]] -name = "clap_complete" -version = "4.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406e68b4de5c59cfb8f750a7cbd4d31ae153788b8352167c1e5f4fc26e8c91e9" -dependencies = [ - "clap 4.6.0", -] - -[[package]] -name = "clap_derive" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "clap_lex" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" - -[[package]] -name = "clipboard-win" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" -dependencies = [ - "error-code", -] - -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - -[[package]] -name = "colorchoice" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" - -[[package]] -name = "colored" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" -dependencies = [ - "lazy_static", - "windows-sys 0.59.0", -] - -[[package]] -name = "combine" -version = "4.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" -dependencies = [ - "bytes", - "memchr", -] - -[[package]] -name = "compact_str" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" -dependencies = [ - "castaway", - "cfg-if", - "itoa", - "rustversion", - "ryu", - "static_assertions", -] - -[[package]] -name = "concat-kdf" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d72c1252426a83be2092dd5884a5f6e3b8e7180f6891b6263d2c21b92ec8816" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "console" -version = "0.15.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" -dependencies = [ - "encode_unicode", - "libc", - "once_cell", - "unicode-width 0.2.0", - "windows-sys 0.59.0", -] - -[[package]] -name = "const-hex" -version = "1.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "proptest", - "serde_core", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const_format" -version = "0.2.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" -dependencies = [ - "const_format_proc_macros", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" -dependencies = [ - "proc-macro2", - "quote", - "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.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "convert_case" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -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.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" -dependencies = [ - "crc-catalog", -] - -[[package]] -name = "crc-catalog" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" - -[[package]] -name = "crossterm" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" -dependencies = [ - "bitflags 2.11.0", - "crossterm_winapi", - "mio", - "parking_lot 0.12.5", - "rustix 0.38.44", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" -dependencies = [ - "bitflags 2.11.0", - "crossterm_winapi", - "derive_more 2.1.1", - "document-features", - "futures-core", - "mio", - "parking_lot 0.12.5", - "rustix 1.1.4", - "signal-hook", - "signal-hook-mio", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - -[[package]] -name = "crunchy" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" -dependencies = [ - "generic-array 0.14.7", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "csv" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" -dependencies = [ - "csv-core", - "itoa", - "ryu", - "serde_core", -] - -[[package]] -name = "csv-core" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" -dependencies = [ - "memchr", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "ctrlc" -version = "3.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" -dependencies = [ - "dispatch2", - "nix 0.31.2", - "windows-sys 0.61.2", -] - -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core 0.20.11", - "darling_macro 0.20.11", -] - -[[package]] -name = "darling" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" -dependencies = [ - "darling_core 0.23.0", - "darling_macro 0.23.0", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.11.1", - "syn 2.0.117", -] - -[[package]] -name = "darling_core" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "serde", - "strsim 0.11.1", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core 0.20.11", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "darling_macro" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" -dependencies = [ - "darling_core 0.23.0", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "dashmap" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" -dependencies = [ - "cfg-if", - "num_cpus", - "serde", -] - -[[package]] -name = "dashmap" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "hashbrown 0.14.5", - "lock_api", - "once_cell", - "parking_lot_core 0.9.12", -] - -[[package]] -name = "dashu" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b3e5ac1e23ff1995ef05b912e2b012a8784506987a2651552db2c73fb3d7e0" -dependencies = [ - "dashu-base", - "dashu-float", - "dashu-int", - "dashu-macros", - "dashu-ratio", - "rustversion", -] - -[[package]] -name = "dashu-base" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b80bf6b85aa68c58ffea2ddb040109943049ce3fbdf4385d0380aef08ef289" - -[[package]] -name = "dashu-float" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85078445a8dbd2e1bd21f04a816f352db8d333643f0c9b78ca7c3d1df71063e7" -dependencies = [ - "dashu-base", - "dashu-int", - "num-modular", - "num-order", - "rustversion", - "static_assertions", -] - -[[package]] -name = "dashu-int" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee99d08031ca34a4d044efbbb21dff9b8c54bb9d8c82a189187c0651ffdb9fbf" -dependencies = [ - "cfg-if", - "dashu-base", - "num-modular", - "num-order", - "rustversion", - "static_assertions", -] - -[[package]] -name = "dashu-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93381c3ef6366766f6e9ed9cf09e4ef9dec69499baf04f0c60e70d653cf0ab10" -dependencies = [ - "dashu-base", - "dashu-float", - "dashu-int", - "dashu-ratio", - "paste", - "proc-macro2", - "quote", - "rustversion", -] - -[[package]] -name = "dashu-ratio" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e33b04dd7ce1ccf8a02a69d3419e354f2bbfdf4eb911a0b7465487248764c9" -dependencies = [ - "dashu-base", - "dashu-float", - "dashu-int", - "num-modular", - "num-order", - "rustversion", -] - -[[package]] -name = "data-encoding" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" - -[[package]] -name = "datatest-stable" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833306ca7eec4d95844e65f0d7502db43888c5c1006c6c517e8cf51a27d15431" -dependencies = [ - "camino", - "fancy-regex", - "libtest-mimic", - "walkdir", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", - "serde_core", -] - -[[package]] -name = "derivative" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "derive-where" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "derive_builder" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" -dependencies = [ - "darling 0.20.11", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "derive_builder_macro" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" -dependencies = [ - "derive_builder_core", - "syn 2.0.117", -] - -[[package]] -name = "derive_more" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" -dependencies = [ - "derive_more-impl 1.0.0", -] - -[[package]] -name = "derive_more" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" -dependencies = [ - "derive_more-impl 2.1.1", -] - -[[package]] -name = "derive_more-impl" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" -dependencies = [ - "convert_case 0.6.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "unicode-xid", -] - -[[package]] -name = "derive_more-impl" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" -dependencies = [ - "convert_case 0.10.0", - "proc-macro2", - "quote", - "rustc_version 0.4.1", - "syn 2.0.117", - "unicode-xid", -] - -[[package]] -name = "deunicode" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "directories" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" -dependencies = [ - "dirs-sys 0.4.1", -] - -[[package]] -name = "dirs" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309" -dependencies = [ - "dirs-sys 0.3.7", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "dispatch2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" -dependencies = [ - "bitflags 2.11.0", - "block2", - "libc", - "objc2", -] - -[[package]] -name = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "document-features" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" -dependencies = [ - "litrs", -] - -[[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - -[[package]] -name = "downloader" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ac1e888d6830712d565b2f3a974be3200be9296bc1b03db8251a4cbf18a4a34" -dependencies = [ - "digest 0.10.7", - "futures", - "rand 0.8.5", - "reqwest 0.12.28", - "thiserror 1.0.69", - "tokio", -] - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest 0.10.7", - "elliptic-curve", - "rfc6979 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serdect", - "signature", - "spki", -] - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "git+https://github.com/sp1-patches/signatures?tag=patch-16.9-sp1-4.1.0#1880299a48fe7ef249edaa616fd411239fb5daf1" -dependencies = [ - "der", - "digest 0.10.7", - "elliptic-curve", - "rfc6979 0.4.0 (git+https://github.com/sp1-patches/signatures?tag=patch-16.9-sp1-4.1.0)", - "signature", - "spki", -] - -[[package]] -name = "educe" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" -dependencies = [ - "enum-ordinalize", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "ef_tests-blockchain" -version = "4.0.0" -dependencies = [ - "bytes", - "datatest-stable", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-guest-program", - "ethrex-prover", - "ethrex-rlp 9.0.0", - "ethrex-storage 9.0.0", - "ethrex-vm", - "hex", - "lazy_static", - "regex", - "serde", - "serde_json", - "tokio", -] - -[[package]] -name = "ef_tests-state" -version = "0.1.0" -dependencies = [ - "alloy-rlp", - "bytes", - "clap 4.6.0", - "clap_complete", - "colored", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-levm", - "ethrex-rlp 9.0.0", - "ethrex-storage 9.0.0", - "ethrex-vm", - "hex", - "itertools 0.13.0", - "rayon", - "revm", - "serde", - "serde_json", - "simd-json", - "spinoff", - "thiserror 2.0.18", - "tokio", -] - -[[package]] -name = "ef_tests-statev2" -version = "4.0.0" -dependencies = [ - "alloy-rlp", - "bytes", - "chrono", - "clap 4.6.0", - "clap_complete", - "colored", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-l2-rpc", - "ethrex-levm", - "ethrex-rlp 9.0.0", - "ethrex-storage 9.0.0", - "ethrex-vm", - "hex", - "prettytable-rs", - "rayon", - "rustc-hash 2.1.2", - "secp256k1 0.30.0", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -dependencies = [ - "serde", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest 0.10.7", - "ff 0.13.1", - "generic-array 0.14.7", - "group 0.13.0", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core 0.6.4", - "sec1", - "serdect", - "subtle", - "zeroize", -] - -[[package]] -name = "encode_unicode" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" - -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "encoding_rs_io" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83" -dependencies = [ - "encoding_rs", -] - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "enum-map" -version = "2.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" -dependencies = [ - "enum-map-derive", - "serde", -] - -[[package]] -name = "enum-map-derive" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "enum-ordinalize" -version = "4.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" -dependencies = [ - "enum-ordinalize-derive", -] - -[[package]] -name = "enum-ordinalize-derive" -version = "4.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "env_filter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" -dependencies = [ - "log", - "regex", -] - -[[package]] -name = "env_logger" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "envy" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" -dependencies = [ - "serde", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "error-code" -version = "3.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" - -[[package]] -name = "escape8259" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" - -[[package]] -name = "eth-keystore" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fda3bf123be441da5260717e0661c25a2fd9cb2b2c1d20bf2e05580047158ab" -dependencies = [ - "aes", - "ctr", - "digest 0.10.7", - "hex", - "hmac", - "pbkdf2", - "rand 0.8.5", - "scrypt", - "serde", - "serde_json", - "sha2 0.10.9", - "sha3", - "thiserror 1.0.69", - "uuid 0.8.2", -] - -[[package]] -name = "ethbloom" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c321610643004cf908ec0f5f2aa0d8f1f8e14b540562a2887a1111ff1ecbf7b" -dependencies = [ - "crunchy", - "fixed-hash", - "impl-rlp", - "impl-serde", - "tiny-keccak", -] - -[[package]] -name = "ethereum-types" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ab15ed80916029f878e0267c3a9f92b67df55e79af370bf66199059ae2b4ee3" -dependencies = [ - "ethbloom", - "fixed-hash", - "impl-rlp", - "impl-serde", - "primitive-types 0.13.1", - "uint 0.10.0", -] - -[[package]] -name = "ethrex" -version = "9.0.0" -dependencies = [ - "anyhow", - "bytes", - "clap 4.6.0", - "directories", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-config", - "ethrex-crypto", - "ethrex-dev", - "ethrex-l2", - "ethrex-l2-common", - "ethrex-l2-prover", - "ethrex-l2-rpc", - "ethrex-metrics", - "ethrex-p2p", - "ethrex-repl", - "ethrex-rlp 9.0.0", - "ethrex-rpc", - "ethrex-sdk", - "ethrex-storage 9.0.0", - "ethrex-storage-rollup", - "ethrex-vm", - "eyre", - "hex", - "itertools 0.14.0", - "lazy_static", - "local-ip-address", - "rand 0.8.5", - "rayon", - "reqwest 0.12.28", - "secp256k1 0.30.0", - "serde", - "serde_json", - "spawned-concurrency", - "spawned-rt", - "thiserror 2.0.18", - "tikv-jemallocator", - "tokio", - "tokio-util", - "tracing", - "tracing-appender", - "tracing-subscriber 0.3.23", - "tui-logger", - "url", - "vergen-git2 9.1.0", -] - -[[package]] -name = "ethrex-blockchain" -version = "9.0.0" -dependencies = [ - "bytes", - "crossbeam", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-metrics", - "ethrex-rlp 9.0.0", - "ethrex-storage 9.0.0", - "ethrex-trie 9.0.0", - "ethrex-vm", - "rayon", - "rustc-hash 2.1.2", - "thiserror 2.0.18", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "ethrex-common" -version = "1.0.0" -source = "git+https://github.com/lambdaclass/ethrex?tag=v1.0.0#a976db3e20acb441b930e1afd156586f222e8f79" -dependencies = [ - "bytes", - "crc32fast", - "ethereum-types", - "ethrex-rlp 1.0.0", - "ethrex-trie 1.0.0", - "hex", - "kzg-rs", - "lazy_static", - "once_cell", - "rayon", - "rkyv", - "secp256k1 0.30.0", - "serde", - "serde_json", - "sha2 0.10.9", - "sha3", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "url", -] - -[[package]] -name = "ethrex-common" -version = "9.0.0" -dependencies = [ - "bytes", - "crc32fast", - "ethereum-types", - "ethrex-crypto", - "ethrex-rlp 9.0.0", - "ethrex-trie 9.0.0", - "hex", - "hex-literal", - "hex-simd", - "indexmap 2.14.0", - "lazy_static", - "libc", - "lru 0.16.3", - "once_cell", - "rayon", - "rkyv", - "rustc-hash 2.1.2", - "secp256k1 0.30.0", - "serde", - "serde_json", - "sha2 0.10.9", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "ethrex-config" -version = "9.0.0" -dependencies = [ - "ethrex-common 9.0.0", - "ethrex-p2p", - "hex", - "serde", - "serde_json", -] - -[[package]] -name = "ethrex-crypto" -version = "9.0.0" -dependencies = [ - "ark-bn254", - "ark-ec", - "ark-ff 0.5.0", - "bls12_381 0.8.0", - "c-kzg", - "ethereum-types", - "ff 0.13.1", - "hex-literal", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "kzg-rs", - "malachite", - "num-bigint 0.4.6", - "p256", - "ripemd", - "secp256k1 0.30.0", - "sha2 0.10.9", - "thiserror 2.0.18", - "tiny-keccak", -] - -[[package]] -name = "ethrex-dev" -version = "9.0.0" -dependencies = [ - "bytes", - "envy", - "ethereum-types", - "ethrex-rpc", - "hex", - "jsonwebtoken", - "reqwest 0.12.28", - "serde", - "serde_json", - "sha2 0.10.9", - "thiserror 2.0.18", - "tokio", - "tracing", -] - -[[package]] -name = "ethrex-guest-program" -version = "9.0.0" -dependencies = [ - "bytes", - "ethereum-types", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-l2-common", - "ethrex-rlp 9.0.0", - "ethrex-vm", - "hex", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rkyv", - "serde", - "serde_with", - "sp1-build", - "sp1-sdk", - "substrate-bn", - "thiserror 2.0.18", -] - -[[package]] -name = "ethrex-l2" -version = "9.0.0" -dependencies = [ - "agg_mode_sdk", - "alloy", - "axum 0.8.8", - "bincode", - "bytes", - "chrono", - "clap 4.6.0", - "crossterm 0.29.0", - "envy", - "ethereum-types", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-config", - "ethrex-l2-common", - "ethrex-l2-rpc", - "ethrex-levm", - "ethrex-metrics", - "ethrex-monitor", - "ethrex-p2p", - "ethrex-rlp 9.0.0", - "ethrex-rpc", - "ethrex-sdk", - "ethrex-storage 9.0.0", - "ethrex-storage-rollup", - "ethrex-trie 9.0.0", - "ethrex-vm", - "futures", - "hex", - "jsonwebtoken", - "lazy_static", - "rand 0.8.5", - "ratatui", - "reqwest 0.12.28", - "secp256k1 0.30.0", - "serde", - "serde_json", - "serde_with", - "spawned-concurrency", - "spawned-rt", - "thiserror 2.0.18", - "tokio", - "tokio-util", - "tracing", - "tui-logger", - "vergen-git2 1.0.7", -] - -[[package]] -name = "ethrex-l2-common" -version = "9.0.0" -dependencies = [ - "bytes", - "ethereum-types", - "ethrex-common 9.0.0", - "ethrex-crypto", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "lambdaworks-crypto 0.13.0", - "rkyv", - "secp256k1 0.30.0", - "serde", - "serde_with", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "ethrex-l2-prover" -version = "9.0.0" -dependencies = [ - "anyhow", - "bincode", - "bytes", - "clap 4.6.0", - "ethereum-types", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-guest-program", - "ethrex-l2", - "ethrex-l2-common", - "ethrex-prover", - "ethrex-rlp 9.0.0", - "ethrex-sdk", - "ethrex-storage 9.0.0", - "ethrex-vm", - "hex", - "rkyv", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tokio-util", - "tracing", - "tracing-subscriber 0.3.23", - "url", -] - -[[package]] -name = "ethrex-l2-rpc" -version = "9.0.0" -dependencies = [ - "axum 0.8.8", - "bytes", - "ethereum-types", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-l2-common", - "ethrex-p2p", - "ethrex-rlp 9.0.0", - "ethrex-rpc", - "ethrex-storage 9.0.0", - "ethrex-storage-rollup", - "hex", - "reqwest 0.12.28", - "rustc-hex", - "secp256k1 0.30.0", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tower-http", - "tracing", - "tracing-subscriber 0.3.23", - "url", -] - -[[package]] -name = "ethrex-levm" -version = "9.0.0" -dependencies = [ - "bytes", - "derive_more 1.0.0", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-rlp 9.0.0", - "malachite", - "rayon", - "rustc-hash 2.1.2", - "serde", - "strum 0.27.2", - "thiserror 2.0.18", -] - -[[package]] -name = "ethrex-metrics" -version = "9.0.0" -dependencies = [ - "axum 0.8.8", - "ethrex-common 9.0.0", - "prometheus", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", - "tracing-subscriber 0.3.23", -] - -[[package]] -name = "ethrex-monitor" -version = "4.0.0" -dependencies = [ - "bytes", - "chrono", - "crossterm 0.29.0", - "ethrex-common 9.0.0", - "ethrex-config", - "ethrex-l2-common", - "ethrex-rlp 9.0.0", - "ethrex-rpc", - "ethrex-sdk", - "ethrex-storage 9.0.0", - "ethrex-storage-rollup", - "futures", - "hex", - "ratatui", - "reqwest 0.12.28", - "secp256k1 0.30.0", - "serde", - "spawned-concurrency", - "thiserror 2.0.18", - "tokio", - "tokio-util", - "tracing", - "tui-logger", -] - -[[package]] -name = "ethrex-p2p" -version = "9.0.0" -dependencies = [ - "aes", - "aes-gcm", - "bytes", - "concat-kdf", - "crossbeam", - "ctr", - "ethereum-types", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-l2-common", - "ethrex-metrics", - "ethrex-rlp 9.0.0", - "ethrex-storage 9.0.0", - "ethrex-storage-rollup", - "ethrex-trie 9.0.0", - "futures", - "hex", - "hkdf", - "hmac", - "indexmap 2.14.0", - "lazy_static", - "lru 0.16.3", - "prometheus", - "rand 0.8.5", - "rayon", - "rocksdb", - "rustc-hash 2.1.2", - "secp256k1 0.30.0", - "serde", - "sha2 0.10.9", - "snap", - "spawned-concurrency", - "spawned-rt", - "thiserror 2.0.18", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", -] - -[[package]] -name = "ethrex-prover" -version = "9.0.0" -dependencies = [ - "bincode", - "bytes", - "clap 4.6.0", - "ethrex-common 9.0.0", - "ethrex-guest-program", - "ethrex-rlp 9.0.0", - "ethrex-vm", - "rkyv", - "serde", - "serde_json", - "sp1-prover", - "sp1-sdk", - "spawned-concurrency", - "thiserror 2.0.18", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "ethrex-repl" -version = "4.0.0" -dependencies = [ - "clap 4.6.0", - "colored", - "ethereum-types", - "hex", - "jsonwebtoken", - "reqwest 0.12.28", - "rustyline", - "serde", - "serde_json", - "sha3", - "thiserror 2.0.18", - "tokio", -] - -[[package]] -name = "ethrex-rlp" -version = "1.0.0" -source = "git+https://github.com/lambdaclass/ethrex?tag=v1.0.0#a976db3e20acb441b930e1afd156586f222e8f79" -dependencies = [ - "bytes", - "ethereum-types", - "hex", - "lazy_static", - "snap", - "thiserror 2.0.18", - "tinyvec", -] - -[[package]] -name = "ethrex-rlp" -version = "9.0.0" -dependencies = [ - "bytes", - "ethereum-types", - "thiserror 2.0.18", -] - -[[package]] -name = "ethrex-rpc" -version = "9.0.0" -dependencies = [ - "axum 0.8.8", - "axum-extra", - "bytes", - "envy", - "ethereum-types", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-metrics", - "ethrex-p2p", - "ethrex-rlp 9.0.0", - "ethrex-storage 9.0.0", - "ethrex-trie 9.0.0", - "ethrex-vm", - "hex", - "hex-literal", - "jsonwebtoken", - "rand 0.8.5", - "reqwest 0.12.28", - "secp256k1 0.30.0", - "serde", - "serde_json", - "sha2 0.10.9", - "spawned-concurrency", - "spawned-rt", - "thiserror 2.0.18", - "tokio", - "tokio-util", - "tower-http", - "tracing", - "tracing-subscriber 0.3.23", - "uuid 1.23.0", -] - -[[package]] -name = "ethrex-sdk" -version = "9.0.0" -dependencies = [ - "bytes", - "ethereum-types", - "ethrex-common 9.0.0", - "ethrex-l2-common", - "ethrex-l2-rpc", - "ethrex-rlp 9.0.0", - "ethrex-rpc", - "ethrex-sdk-contract-utils", - "hex", - "itertools 0.13.0", - "lazy_static", - "reqwest 0.12.28", - "secp256k1 0.30.0", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", -] - -[[package]] -name = "ethrex-sdk-contract-utils" -version = "9.0.0" -dependencies = [ - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "ethrex-storage" -version = "1.0.0" -source = "git+https://github.com/lambdaclass/ethrex?tag=v1.0.0#a976db3e20acb441b930e1afd156586f222e8f79" -dependencies = [ - "anyhow", - "async-trait", - "bincode", - "bytes", - "ethereum-types", - "ethrex-common 1.0.0", - "ethrex-rlp 1.0.0", - "ethrex-trie 1.0.0", - "hex", - "libmdbx", - "serde", - "serde_json", - "sha3", - "thiserror 2.0.18", - "tokio", - "tracing", -] - -[[package]] -name = "ethrex-storage" -version = "9.0.0" -dependencies = [ - "anyhow", - "bytes", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-rlp 9.0.0", - "ethrex-trie 9.0.0", - "fastbloom", - "lru 0.16.3", - "rayon", - "rocksdb", - "rustc-hash 2.1.2", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", -] - -[[package]] -name = "ethrex-storage-rollup" -version = "9.0.0" -dependencies = [ - "async-trait", - "bincode", - "ethereum-types", - "ethrex-common 9.0.0", - "ethrex-l2-common", - "futures", - "rkyv", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "ethrex-trie" -version = "1.0.0" -source = "git+https://github.com/lambdaclass/ethrex?tag=v1.0.0#a976db3e20acb441b930e1afd156586f222e8f79" -dependencies = [ - "anyhow", - "bytes", - "digest 0.10.7", - "ethereum-types", - "ethrex-rlp 1.0.0", - "hex", - "lazy_static", - "libmdbx", - "serde", - "serde_json", - "sha3", - "smallvec", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "ethrex-trie" -version = "9.0.0" -dependencies = [ - "anyhow", - "bytes", - "crossbeam", - "ethereum-types", - "ethrex-crypto", - "ethrex-rlp 9.0.0", - "lazy_static", - "rayon", - "rkyv", - "rustc-hash 2.1.2", - "serde", - "thiserror 2.0.18", -] - -[[package]] -name = "ethrex-vm" -version = "9.0.0" -dependencies = [ - "bytes", - "derive_more 1.0.0", - "dyn-clone", - "ethrex-common 9.0.0", - "ethrex-crypto", - "ethrex-levm", - "ethrex-rlp 9.0.0", - "rayon", - "rustc-hash 2.1.2", - "serde", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "eventsource-stream" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" -dependencies = [ - "futures-core", - "nom", - "pin-project-lite", -] - -[[package]] -name = "eyre" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fancy-regex" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" -dependencies = [ - "bit-set", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "fastbloom" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7f34442dbe69c60fe8eaf58a8cafff81a1f278816d8ab4db255b3bef4ac3c4" -dependencies = [ - "getrandom 0.3.4", - "libm", - "rand 0.9.3", - "siphasher", -] - -[[package]] -name = "fastrand" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" - -[[package]] -name = "fastrlp" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", -] - -[[package]] -name = "fastrlp" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8dba4714ef14b8274c371879b175aa55b16b30f269663f19d576f380018dc4" -dependencies = [ - "arrayvec", - "auto_impl", - "bytes", -] - -[[package]] -name = "fd-lock" -version = "4.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce92ff622d6dadf7349484f42c93271a0d49b7cc4d466a936405bacbe10aa78" -dependencies = [ - "cfg-if", - "rustix 1.1.4", - "windows-sys 0.59.0", -] - -[[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 = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "fixed-hash" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" -dependencies = [ - "byteorder", - "rand 0.8.5", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "float-cmp" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09cf3155332e944990140d967ff5eceb70df778b34f77d8075db46e4704e6d8" -dependencies = [ - "num-traits", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" - -[[package]] -name = "futures-task" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" - -[[package]] -name = "futures-util" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "futures-utils-wasm" -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 = "gen_ops" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "304de19db7028420975a296ab0fcbbc8e69438c4ed254a1e41e2a7f37d5f0e0a" - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "generic-array" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96512db27971c2c3eece70a1e106fbe6c87760234e31e8f7e5634912fe52794a" -dependencies = [ - "serde", - "typenum", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi 5.3.0", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" -dependencies = [ - "cfg-if", - "libc", - "r-efi 6.0.0", - "wasip2", - "wasip3", -] - -[[package]] -name = "getset" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" -dependencies = [ - "proc-macro-error2", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "git2" -version = "0.20.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" -dependencies = [ - "bitflags 2.11.0", - "libc", - "libgit2-sys", - "log", - "url", -] - -[[package]] -name = "glob" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" - -[[package]] -name = "globset" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" -dependencies = [ - "aho-corasick 1.1.4", - "bstr", - "log", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "globwalk" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" -dependencies = [ - "bitflags 2.11.0", - "ignore", - "walkdir", -] - -[[package]] -name = "gmp-mpfr-sys" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfc928d8ff4ab3767a3674cf55f81186436fb6070866bb1443ffe65a640d2d6" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "grep-matcher" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36d7b71093325ab22d780b40d7df3066ae4aebb518ba719d38c697a8228a8023" -dependencies = [ - "memchr", -] - -[[package]] -name = "grep-searcher" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac63295322dc48ebb20a25348147905d816318888e64f531bfc2a2bc0577dc34" -dependencies = [ - "bstr", - "encoding_rs", - "encoding_rs_io", - "grep-matcher", - "log", - "memchr", - "memmap2", -] - -[[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 0.13.1", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "h2" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap 2.14.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "halfbrown" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2c385c6df70fd180bbb673d93039dbd2cd34e41d782600bdf6e1ca7bce39aa" -dependencies = [ - "hashbrown 0.15.5", - "serde", -] - -[[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 = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", - "serde", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.1.5", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", - "serde", - "serde_core", -] - -[[package]] -name = "hashbrown" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" - -[[package]] -name = "headers" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" -dependencies = [ - "base64", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" -dependencies = [ - "http", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hex-conservative" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" -dependencies = [ - "arrayvec", -] - -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[package]] -name = "hex-simd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7685beb53fc20efc2605f32f5d51e9ba18b8ef237961d1760169d2290d3bee" -dependencies = [ - "outref", - "vsimd", -] - -[[package]] -name = "hive_report" -version = "4.0.0" -dependencies = [ - "serde", - "serde_json", -] - -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "home" -version = "0.5.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "humansize" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" -dependencies = [ - "libm", -] - -[[package]] -name = "humantime" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" - -[[package]] -name = "hyper" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2b52f86d1d4bc0d6b4e6826d960b1b333217e07d36b882dca570a5e1c48895b" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "tokio", - "tokio-rustls", - "tower-service", - "webpki-roots", -] - -[[package]] -name = "hyper-timeout" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" -dependencies = [ - "hyper", - "hyper-util", - "pin-project-lite", - "tokio", - "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2 0.6.3", - "system-configuration", - "tokio", - "tower-service", - "tracing", - "windows-registry", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core 0.62.2", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "icu_collections" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" -dependencies = [ - "displaydoc", - "potential_utf", - "utf8_iter", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" - -[[package]] -name = "icu_properties" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" - -[[package]] -name = "icu_provider" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[package]] -name = "ignore" -version = "0.4.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a" -dependencies = [ - "crossbeam-deque", - "globset", - "log", - "memchr", - "regex-automata", - "same-file", - "walkdir", - "winapi-util", -] - -[[package]] -name = "impl-codec" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-codec" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d40b9d5e17727407e55028eafc22b2dc68781786e6d7eb8a21103f5058e3a14" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "impl-rlp" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54ed8ad1f3877f7e775b8cbf30ed1bd3209a95401817f19a0eb4402d13f8cf90" -dependencies = [ - "rlp 0.6.1", -] - -[[package]] -name = "impl-serde" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" -dependencies = [ - "serde", -] - -[[package]] -name = "impl-trait-for-tuples" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "impls" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a46645bbd70538861a90d0f26c31537cdf1e44aae99a794fb75a664b70951bc" - -[[package]] -name = "indenter" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" -dependencies = [ - "equivalent", - "hashbrown 0.17.0", - "serde", - "serde_core", -] - -[[package]] -name = "indicatif" -version = "0.17.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" -dependencies = [ - "console", - "number_prefix", - "portable-atomic", - "unicode-width 0.2.0", - "web-time", -] - -[[package]] -name = "indoc" -version = "2.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" -dependencies = [ - "rustversion", -] - -[[package]] -name = "inout" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" -dependencies = [ - "generic-array 0.14.7", -] - -[[package]] -name = "instability" -version = "0.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" -dependencies = [ - "darling 0.23.0", - "indoc", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ipnet" -version = "2.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" - -[[package]] -name = "iri-string" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "is-terminal" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" -dependencies = [ - "hermit-abi 0.5.2", - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "is_terminal_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" - -[[package]] -name = "itertools" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" -dependencies = [ - "jni-sys 0.4.1", -] - -[[package]] -name = "jni-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" -dependencies = [ - "jni-sys-macros", -] - -[[package]] -name = "jni-sys-macros" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" -dependencies = [ - "quote", - "syn 2.0.117", -] - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" -dependencies = [ - "cfg-if", - "futures-util", - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "jsonwebtoken" -version = "9.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" -dependencies = [ - "base64", - "js-sys", - "pem", - "ring", - "serde", - "serde_json", - "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 0.7.1", - "ff 0.12.1", - "group 0.12.1", - "rand_core 0.6.4", - "subtle", -] - -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if", - "ecdsa 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", - "elliptic-curve", - "once_cell", - "serdect", - "sha2 0.10.9", - "signature", -] - -[[package]] -name = "k256" -version = "0.13.4" -source = "git+https://github.com/sp1-patches/elliptic-curves?tag=patch-k256-13.4-sp1-5.0.0#f7d8998e05d8cbcbd8e543eba1030a7385011fa8" -dependencies = [ - "cfg-if", - "ecdsa 0.16.9 (git+https://github.com/sp1-patches/signatures?tag=patch-16.9-sp1-4.1.0)", - "elliptic-curve", - "hex", - "once_cell", - "sha2 0.10.9", - "signature", - "sp1-lib 5.2.4", -] - -[[package]] -name = "keccak" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" -dependencies = [ - "cpufeatures 0.2.17", -] - -[[package]] -name = "keccak-asm" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa468878266ad91431012b3e5ef1bf9b170eab22883503a318d46857afa4579a" -dependencies = [ - "digest 0.10.7", - "sha3-asm", -] - -[[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 = "lambdaworks-crypto" -version = "0.12.0" -source = "git+https://github.com/lambdaclass/lambdaworks.git?rev=5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b#5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b" -dependencies = [ - "lambdaworks-math 0.12.0", - "rand 0.8.5", - "rand_chacha 0.3.1", - "serde", - "sha2 0.10.9", - "sha3", -] - -[[package]] -name = "lambdaworks-crypto" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b1a1c1102a5a7fbbda117b79fb3a01e033459c738a3c1642269603484fd1c1" -dependencies = [ - "lambdaworks-math 0.13.0", - "rand 0.8.5", - "rand_chacha 0.3.1", - "serde", - "sha2 0.10.9", - "sha3", -] - -[[package]] -name = "lambdaworks-math" -version = "0.12.0" -source = "git+https://github.com/lambdaclass/lambdaworks.git?rev=5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b#5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b" -dependencies = [ - "getrandom 0.2.17", - "rand 0.8.5", - "serde", - "serde_json", -] - -[[package]] -name = "lambdaworks-math" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "018a95aa873eb49896a858dee0d925c33f3978d073c64b08dd4f2c9b35a017c6" -dependencies = [ - "getrandom 0.2.17", - "num-bigint 0.4.6", - "num-traits", - "rand 0.8.5", - "serde", - "serde_json", -] - -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -dependencies = [ - "spin", -] - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libc" -version = "0.2.185" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" - -[[package]] -name = "libgit2-sys" -version = "0.18.3+1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] - -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link", -] - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "libmdbx" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63a680ffe20d9f3cd4b0bf2587c01b837ac2a12976ff984838b27830366ccc92" -dependencies = [ - "anyhow", - "arrayref", - "arrayvec", - "bitflags 2.11.0", - "derive_more 1.0.0", - "impls", - "indexmap 2.14.0", - "libc", - "mdbx-sys", - "parking_lot 0.12.5", - "sealed", - "tempfile", - "thiserror 2.0.18", -] - -[[package]] -name = "libredox" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" -dependencies = [ - "libc", -] - -[[package]] -name = "librocksdb-sys" -version = "0.17.3+10.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cef2a00ee60fe526157c9023edab23943fae1ce2ab6f4abb2a807c1746835de9" -dependencies = [ - "bindgen 0.72.1", - "bzip2-sys", - "cc", - "libc", - "libz-sys", - "lz4-sys", -] - -[[package]] -name = "libsecp256k1" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79019718125edc905a079a70cfa5f3820bc76139fc91d6f9abc27ea2a887139" -dependencies = [ - "arrayref", - "base64", - "digest 0.9.0", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", - "sha2 0.9.9", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libtest-mimic" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e6ba06f0ade6e504aff834d7c34298e5155c6baca353cc6a4aaff2f9fd7f33" -dependencies = [ - "anstream", - "anstyle", - "clap 4.6.0", - "escape8259", -] - -[[package]] -name = "libz-sys" -version = "1.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "linux-raw-sys" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" - -[[package]] -name = "linux-raw-sys" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" - -[[package]] -name = "litemap" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" - -[[package]] -name = "litrs" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" - -[[package]] -name = "load_test" -version = "4.0.0" -dependencies = [ - "clap 4.6.0", - "ethereum-types", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-l2-common", - "ethrex-l2-rpc", - "ethrex-rpc", - "ethrex-sdk", - "eyre", - "futures", - "hex", - "secp256k1 0.30.0", - "tokio", - "url", -] - -[[package]] -name = "loc" -version = "4.0.0" -dependencies = [ - "clap 4.6.0", - "clap_complete", - "colored", - "prettytable", - "serde", - "serde_json", - "spinoff", - "tokei", -] - -[[package]] -name = "local-ip-address" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a59a0cb1c7f84471ad5cd38d768c2a29390d17f1ff2827cdf49bc53e8ac70b" -dependencies = [ - "libc", - "neli", - "windows-sys 0.61.2", -] - -[[package]] -name = "lock_api" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - -[[package]] -name = "lru" -version = "0.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" -dependencies = [ - "hashbrown 0.16.1", -] - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "lz4-sys" -version = "1.11.1+lz4-1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bd8c0d6c6ed0cd30b3652886bb8711dc4bb01d637a68105a3d5158039b418e6" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "macro-string" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "malachite" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec410515e231332b14cd986a475d1c3323bcfa4c7efc038bfa1d5b410b1c57e4" -dependencies = [ - "malachite-base", - "malachite-nz", - "malachite-q", -] - -[[package]] -name = "malachite-base" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c738d3789301e957a8f7519318fcbb1b92bb95863b28f6938ae5a05be6259f34" -dependencies = [ - "hashbrown 0.15.5", - "itertools 0.14.0", - "libm", - "ryu", -] - -[[package]] -name = "malachite-nz" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1707c9a1fa36ce21749b35972bfad17bbf34cf5a7c96897c0491da321e387d3b" -dependencies = [ - "itertools 0.14.0", - "libm", - "malachite-base", - "wide", -] - -[[package]] -name = "malachite-q" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d764801aa4e96bbb69b389dcd03b50075345131cd63ca2e380bca71cc37a3675" -dependencies = [ - "itertools 0.14.0", - "malachite-base", - "malachite-nz", -] - -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - -[[package]] -name = "matchit" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" - -[[package]] -name = "mdbx-sys" -version = "12.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "970abf35a0fde64c0c72971908bcd2fe0350d7da27d8dd0f02b4a00705d21c5d" -dependencies = [ - "bindgen 0.71.1", - "cc", - "libc", -] - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "memmap2" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" -dependencies = [ - "libc", -] - -[[package]] -name = "memuse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" - -[[package]] -name = "migrations" -version = "4.0.0" -dependencies = [ - "clap 4.6.0", - "ethrex-blockchain", - "ethrex-common 1.0.0", - "ethrex-common 9.0.0", - "ethrex-storage 1.0.0", - "ethrex-storage 9.0.0", - "tokio", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", -] - -[[package]] -name = "mio" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "munge" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e17401f259eba956ca16491461b6e8f72913a0a114e39736ce404410f915a0c" -dependencies = [ - "munge_macro", -] - -[[package]] -name = "munge_macro" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "native-tls" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "neli" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f9786d56d972959e1408b6a93be6af13b9c1392036c5c1fafa08a1b0c6ee87" -dependencies = [ - "bitflags 2.11.0", - "byteorder", - "derive_builder", - "getset", - "libc", - "log", - "neli-proc-macros", - "parking_lot 0.12.5", -] - -[[package]] -name = "neli-proc-macros" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d8d08c6e98f20a62417478ebf7be8e1425ec9acecc6f63e22da633f6b71609" -dependencies = [ - "either", - "proc-macro2", - "quote", - "serde", - "syn 2.0.117", -] - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "nix" -version = "0.30.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "nix" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "nohash-hasher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "ntapi" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" -dependencies = [ - "winapi", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" -dependencies = [ - "num-bigint 0.4.6", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" - -[[package]] -name = "num-format" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" -dependencies = [ - "arrayvec", - "itoa", -] - -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-modular" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f" - -[[package]] -name = "num-order" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6" -dependencies = [ - "num-modular", -] - -[[package]] -name = "num-rational" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" -dependencies = [ - "num-bigint 0.4.6", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi 0.5.2", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive 0.5.11", -] - -[[package]] -name = "num_enum" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" -dependencies = [ - "num_enum_derive 0.7.6", - "rustversion", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - -[[package]] -name = "nybbles" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d49ff0c0d00d4a502b39df9af3a525e1efeb14b9dabb5bb83335284c1309210" -dependencies = [ - "alloy-rlp", - "cfg-if", - "proptest", - "ruint", - "serde", - "smallvec", -] - -[[package]] -name = "objc2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" -dependencies = [ - "objc2-encode", -] - -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "once_cell_polyfill" -version = "1.70.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "openssl" -version = "0.10.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe4646e360ec77dff7dde40ed3d6c5fee52d156ef4a62f53973d38294dad87f" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[package]] -name = "openssl-sys" -version = "0.9.113" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2f2c0eba47118757e4c6d2bff2838f3e0523380021356e7875e858372ce644" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - -[[package]] -name = "outref" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)", - "elliptic-curve", - "primeorder", - "sha2 0.10.9", -] - -[[package]] -name = "p3-air" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05a97452c4b1cfa8626e69181d901fc8231d99ff7d87e9701a2e6b934606615" -dependencies = [ - "p3-field 0.2.3-succinct", - "p3-matrix 0.2.3-succinct", -] - -[[package]] -name = "p3-baby-bear" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7521838ecab2ddf4f7bc4ceebad06ec02414729598485c1ada516c39900820e8" -dependencies = [ - "num-bigint 0.4.6", - "p3-field 0.2.3-succinct", - "p3-mds 0.2.3-succinct", - "p3-poseidon2 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "p3-bn254-fr" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0dd4d095d254783098bd09fc5fdf33fd781a1be54608ab93cb3ed4bd723da54" -dependencies = [ - "ff 0.13.1", - "num-bigint 0.4.6", - "p3-field 0.2.3-succinct", - "p3-poseidon2 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "rand 0.8.5", - "serde", -] - -[[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 0.3.2-succinct", - "p3-poseidon2 0.3.2-succinct", - "p3-symmetric 0.3.2-succinct", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "p3-challenger" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5d18c223b7e0177f4ac91070fa3f6cc557d5ee3b279869924c3102fb1b20910" -dependencies = [ - "p3-field 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "serde", - "tracing", -] - -[[package]] -name = "p3-challenger" -version = "0.3.2-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42b725b453bbb35117a1abf0ddfd900b0676063d6e4231e0fa6bb0d76018d8ad" -dependencies = [ - "p3-field 0.3.2-succinct", - "p3-maybe-rayon 0.3.2-succinct", - "p3-symmetric 0.3.2-succinct", - "p3-util 0.3.2-succinct", - "serde", - "tracing", -] - -[[package]] -name = "p3-commit" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b38fe979d53d4f1d64158c40b3cd9ea1bd6b7bc8f085e489165c542ef914ae28" -dependencies = [ - "itertools 0.12.1", - "p3-challenger 0.2.3-succinct", - "p3-field 0.2.3-succinct", - "p3-matrix 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "serde", -] - -[[package]] -name = "p3-dft" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46414daedd796f1eefcdc1811c0484e4bced5729486b6eaba9521c572c76761a" -dependencies = [ - "p3-field 0.2.3-succinct", - "p3-matrix 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "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 0.3.2-succinct", - "p3-matrix 0.3.2-succinct", - "p3-maybe-rayon 0.3.2-succinct", - "p3-util 0.3.2-succinct", - "tracing", -] - -[[package]] -name = "p3-field" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48948a0516b349e9d1cdb95e7236a6ee010c44e68c5cc78b4b92bf1c4022a0d9" -dependencies = [ - "itertools 0.12.1", - "num-bigint 0.4.6", - "num-traits", - "p3-util 0.2.3-succinct", - "rand 0.8.5", - "serde", -] - -[[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 0.3.2-succinct", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "p3-fri" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c274dab2dcd060cdea9ab3f8f7129f5fa5f08917d6092dc2b297a31d883aa0" -dependencies = [ - "itertools 0.12.1", - "p3-challenger 0.2.3-succinct", - "p3-commit", - "p3-dft 0.2.3-succinct", - "p3-field 0.2.3-succinct", - "p3-interpolation", - "p3-matrix 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "serde", - "tracing", -] - -[[package]] -name = "p3-interpolation" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed8de7333abb0ad0a17bb78726a43749cc7fcab4763f296894e8b2933841d4d8" -dependencies = [ - "p3-field 0.2.3-succinct", - "p3-matrix 0.2.3-succinct", - "p3-util 0.2.3-succinct", -] - -[[package]] -name = "p3-keccak-air" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c7ec21317c455d39588428e4ec85b96d663ff171ddf102a10e2ca54c942dea" -dependencies = [ - "p3-air", - "p3-field 0.2.3-succinct", - "p3-matrix 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "tracing", -] - -[[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 0.3.2-succinct", - "p3-mds 0.3.2-succinct", - "p3-poseidon2 0.3.2-succinct", - "p3-symmetric 0.3.2-succinct", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "p3-matrix" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4de3f373589477cb735ea58e125898ed20935e03664b4614c7fac258b3c42f" -dependencies = [ - "itertools 0.12.1", - "p3-field 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "rand 0.8.5", - "serde", - "tracing", -] - -[[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 0.3.2-succinct", - "p3-maybe-rayon 0.3.2-succinct", - "p3-util 0.3.2-succinct", - "rand 0.8.5", - "serde", - "tracing", -] - -[[package]] -name = "p3-maybe-rayon" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3968ad1160310296eb04f91a5f4edfa38fe1d6b2b8cd6b5c64e6f9b7370979e" -dependencies = [ - "rayon", -] - -[[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.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2356b1ed0add6d5dfbf7a338ce534a6fde827374394a52cec16a0840af6e97c9" -dependencies = [ - "itertools 0.12.1", - "p3-dft 0.2.3-succinct", - "p3-field 0.2.3-succinct", - "p3-matrix 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "rand 0.8.5", -] - -[[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 0.3.2-succinct", - "p3-field 0.3.2-succinct", - "p3-matrix 0.3.2-succinct", - "p3-symmetric 0.3.2-succinct", - "p3-util 0.3.2-succinct", - "rand 0.8.5", -] - -[[package]] -name = "p3-merkle-tree" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f159e073afbee02c00d22390bf26ebb9ce03bbcd3e6dcd13c6a7a3811ab39608" -dependencies = [ - "itertools 0.12.1", - "p3-commit", - "p3-field 0.2.3-succinct", - "p3-matrix 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "serde", - "tracing", -] - -[[package]] -name = "p3-poseidon2" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da1eec7e1b6900581bedd95e76e1ef4975608dd55be9872c9d257a8a9651c3a" -dependencies = [ - "gcd", - "p3-field 0.2.3-succinct", - "p3-mds 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "rand 0.8.5", - "serde", -] - -[[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 0.3.2-succinct", - "p3-mds 0.3.2-succinct", - "p3-symmetric 0.3.2-succinct", - "rand 0.8.5", - "serde", -] - -[[package]] -name = "p3-symmetric" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb439bea1d822623b41ff4b51e3309e80d13cadf8b86d16ffd5e6efb9fdc360" -dependencies = [ - "itertools 0.12.1", - "p3-field 0.2.3-succinct", - "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 0.3.2-succinct", - "serde", -] - -[[package]] -name = "p3-uni-stark" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a86f29c32bf46fa4acb6547d2065a711e146d4faca388b56d75718c60a0097d" -dependencies = [ - "itertools 0.12.1", - "p3-air", - "p3-challenger 0.2.3-succinct", - "p3-commit", - "p3-dft 0.2.3-succinct", - "p3-field 0.2.3-succinct", - "p3-matrix 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "serde", - "tracing", -] - -[[package]] -name = "p3-util" -version = "0.2.3-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c2c2010678b9332b563eaa38364915b585c1a94b5ca61e2c7541c087ddda5c" -dependencies = [ - "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 = "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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799781ae679d79a948e13d4824a40970bfa500058d245760dd857301059810fa" -dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "const_format", - "impl-trait-for-tuples", - "parity-scale-codec-derive", - "rustversion", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "3.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.12", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.5.18", - "smallvec", - "windows-link", -] - -[[package]] -name = "parse-zoneinfo" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" -dependencies = [ - "regex", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pathdiff" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" - -[[package]] -name = "pbkdf2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "pem" -version = "3.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" -dependencies = [ - "base64", - "serde_core", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pest" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" -dependencies = [ - "memchr", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pest_meta" -version = "2.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" -dependencies = [ - "pest", - "sha2 0.10.9", -] - -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_macros", - "phf_shared", - "serde", -] - -[[package]] -name = "phf_codegen" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" -dependencies = [ - "phf_generator", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - -[[package]] -name = "pin-project" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkg-config" -version = "0.3.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" - -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "portable-atomic" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - -[[package]] -name = "potential_utf" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "prettytable" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46480520d1b77c9a3482d39939fcf96831537a250ec62d4fd8fbdf8e0302e781" -dependencies = [ - "csv", - "encode_unicode", - "is-terminal", - "lazy_static", - "term", - "unicode-width 0.1.14", -] - -[[package]] -name = "prettytable-rs" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a" -dependencies = [ - "csv", - "encode_unicode", - "is-terminal", - "lazy_static", - "term", - "unicode-width 0.1.14", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "primitive-types" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" -dependencies = [ - "fixed-hash", - "impl-codec 0.6.0", - "uint 0.9.5", -] - -[[package]] -name = "primitive-types" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" -dependencies = [ - "fixed-hash", - "impl-codec 0.7.1", - "impl-rlp", - "impl-serde", - "uint 0.10.0", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" -dependencies = [ - "toml_edit 0.25.11+spec-1.1.0", -] - -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "procfs" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" -dependencies = [ - "bitflags 2.11.0", - "hex", - "procfs-core", - "rustix 0.38.44", -] - -[[package]] -name = "procfs-core" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" -dependencies = [ - "bitflags 2.11.0", - "hex", -] - -[[package]] -name = "prometheus" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ca5326d8d0b950a9acd87e6a3f94745394f62e4dae1b1ee22b2bc0c394af43a" -dependencies = [ - "cfg-if", - "fnv", - "lazy_static", - "libc", - "memchr", - "parking_lot 0.12.5", - "procfs", - "protobuf", - "thiserror 2.0.18", -] - -[[package]] -name = "proptest" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" -dependencies = [ - "bit-set", - "bit-vec", - "bitflags 2.11.0", - "num-traits", - "rand 0.9.3", - "rand_chacha 0.9.0", - "rand_xorshift", - "regex-syntax", - "rusty-fork", - "tempfile", - "unarray", -] - -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools 0.14.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "protobuf" -version = "3.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" -dependencies = [ - "once_cell", - "protobuf-support", - "thiserror 1.0.69", -] - -[[package]] -name = "protobuf-support" -version = "3.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" -dependencies = [ - "thiserror 1.0.69", -] - -[[package]] -name = "ptr_meta" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash 2.1.2", - "rustls", - "socket2 0.6.3", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand 0.9.3", - "ring", - "rustc-hash 2.1.2", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2 0.6.3", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "r-efi" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" - -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - -[[package]] -name = "rancor" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a063ea72381527c2a0561da9c80000ef822bdd7c3241b1cc1b12100e3df081ee" -dependencies = [ - "ptr_meta", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", - "serde", -] - -[[package]] -name = "rand" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", - "serde", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", - "serde", -] - -[[package]] -name = "rand_xorshift" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" -dependencies = [ - "rand_core 0.9.5", -] - -[[package]] -name = "range-set-blaze" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8421b5d459262eabbe49048d362897ff3e3830b44eac6cfe341d6acb2f0f13d2" -dependencies = [ - "gen_ops", - "itertools 0.12.1", - "num-integer", - "num-traits", -] - -[[package]] -name = "rapidhash" -version = "4.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" -dependencies = [ - "rustversion", -] - -[[package]] -name = "ratatui" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" -dependencies = [ - "bitflags 2.11.0", - "cassowary", - "compact_str", - "crossterm 0.28.1", - "indoc", - "instability", - "itertools 0.13.0", - "lru 0.12.5", - "paste", - "strum 0.26.3", - "unicode-segmentation", - "unicode-truncate", - "unicode-width 0.2.0", -] - -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "rayon-scan" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f87cc11a0140b4b0da0ffc889885760c61b13672d80a908920b2c0df078fa14" -dependencies = [ - "rayon", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom 0.2.17", - "libredox", - "thiserror 1.0.69", -] - -[[package]] -name = "ref-cast" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick 1.1.4", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick 1.1.4", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "rend" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadadef317c2f20755a64d7fdc48f9e7178ee6b0e1f7fce33fa60f1d68a276e6" -dependencies = [ - "bytecheck", -] - -[[package]] -name = "reorgs" -version = "4.0.0" -dependencies = [ - "ethrex", - "ethrex-blockchain", - "ethrex-common 9.0.0", - "ethrex-config", - "ethrex-l2-common", - "ethrex-l2-rpc", - "ethrex-p2p", - "ethrex-rpc", - "hex", - "nix 0.30.1", - "rand 0.8.5", - "secp256k1 0.30.0", - "sha2 0.10.9", - "tokio", - "tokio-util", - "tracing", - "url", -] - -[[package]] -name = "reqwest" -version = "0.12.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "js-sys", - "log", - "mime", - "mime_guess", - "native-tls", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tokio-util", - "tower 0.5.3", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots", -] - -[[package]] -name = "reqwest" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" -dependencies = [ - "base64", - "bytes", - "futures-core", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower 0.5.3", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "reqwest-middleware" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562ceb5a604d3f7c885a792d42c199fd8af239d0a51b2fa6a78aafa092452b04" -dependencies = [ - "anyhow", - "async-trait", - "http", - "reqwest 0.12.28", - "serde", - "thiserror 1.0.69", - "tower-service", -] - -[[package]] -name = "revm" -version = "27.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6bf82101a1ad8a2b637363a37aef27f88b4efc8a6e24c72bf5f64923dc5532" -dependencies = [ - "revm-bytecode", - "revm-context", - "revm-context-interface", - "revm-database", - "revm-database-interface", - "revm-handler", - "revm-inspector", - "revm-interpreter", - "revm-precompile", - "revm-primitives", - "revm-state", -] - -[[package]] -name = "revm-bytecode" -version = "6.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c52031b73cae95d84cd1b07725808b5fd1500da3e5e24574a3b2dc13d9f16d" -dependencies = [ - "bitvec", - "phf", - "revm-primitives", - "serde", -] - -[[package]] -name = "revm-context" -version = "8.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cd508416a35a4d8a9feaf5ccd06ac6d6661cd31ee2dc0252f9f7316455d71f9" -dependencies = [ - "cfg-if", - "derive-where", - "revm-bytecode", - "revm-context-interface", - "revm-database-interface", - "revm-primitives", - "revm-state", - "serde", -] - -[[package]] -name = "revm-context-interface" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc90302642d21c8f93e0876e201f3c5f7913c4fcb66fb465b0fd7b707dfe1c79" -dependencies = [ - "alloy-eip2930", - "alloy-eip7702", - "auto_impl", - "either", - "revm-database-interface", - "revm-primitives", - "revm-state", - "serde", -] - -[[package]] -name = "revm-database" -version = "7.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a276ed142b4718dcf64bc9624f474373ed82ef20611025045c3fb23edbef9c" -dependencies = [ - "alloy-eips", - "revm-bytecode", - "revm-database-interface", - "revm-primitives", - "revm-state", - "serde", -] - -[[package]] -name = "revm-database-interface" -version = "7.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c523c77e74eeedbac5d6f7c092e3851dbe9c7fec6f418b85992bd79229db361" -dependencies = [ - "auto_impl", - "either", - "revm-primitives", - "revm-state", - "serde", -] - -[[package]] -name = "revm-handler" -version = "8.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1529c8050e663be64010e80ec92bf480315d21b1f2dbf65540028653a621b27d" -dependencies = [ - "auto_impl", - "derive-where", - "revm-bytecode", - "revm-context", - "revm-context-interface", - "revm-database-interface", - "revm-interpreter", - "revm-precompile", - "revm-primitives", - "revm-state", - "serde", -] - -[[package]] -name = "revm-inspector" -version = "8.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78db140e332489094ef314eaeb0bd1849d6d01172c113ab0eb6ea8ab9372926" -dependencies = [ - "auto_impl", - "either", - "revm-context", - "revm-database-interface", - "revm-handler", - "revm-interpreter", - "revm-primitives", - "revm-state", - "serde", - "serde_json", -] - -[[package]] -name = "revm-interpreter" -version = "24.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff9d7d9d71e8a33740b277b602165b6e3d25fff091ba3d7b5a8d373bf55f28a7" -dependencies = [ - "revm-bytecode", - "revm-context-interface", - "revm-primitives", - "serde", -] - -[[package]] -name = "revm-precompile" -version = "25.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cee3f336b83621294b4cfe84d817e3eef6f3d0fce00951973364cc7f860424d" -dependencies = [ - "ark-bls12-381", - "ark-bn254", - "ark-ec", - "ark-ff 0.5.0", - "ark-serialize 0.5.0", - "arrayref", - "aurora-engine-modexp", - "c-kzg", - "cfg-if", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libsecp256k1", - "once_cell", - "p256", - "revm-primitives", - "ripemd", - "rug", - "secp256k1 0.31.1", - "sha2 0.10.9", -] - -[[package]] -name = "revm-primitives" -version = "20.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa29d9da06fe03b249b6419b33968ecdf92ad6428e2f012dc57bcd619b5d94e" -dependencies = [ - "alloy-primitives", - "num_enum 0.7.6", - "once_cell", - "serde", -] - -[[package]] -name = "revm-state" -version = "7.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f64fbacb86008394aaebd3454f9643b7d5a782bd251135e17c5b33da592d84d" -dependencies = [ - "bitflags 2.11.0", - "revm-bytecode", - "revm-primitives", - "serde", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "git+https://github.com/sp1-patches/signatures?tag=patch-16.9-sp1-4.1.0#1880299a48fe7ef249edaa616fd411239fb5daf1" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "ripemd" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "rkyv" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a30e631b7f4a03dee9056b8ef6982e8ba371dd5bedb74d3ec86df4499132c70" -dependencies = [ - "bytecheck", - "bytes", - "hashbrown 0.16.1", - "indexmap 2.14.0", - "munge", - "ptr_meta", - "rancor", - "rend", - "rkyv_derive", - "tinyvec", - "uuid 1.23.0", -] - -[[package]] -name = "rkyv_derive" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8100bb34c0a1d0f907143db3149e6b4eea3c33b9ee8b189720168e818303986f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "rlp" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" -dependencies = [ - "bytes", - "rustc-hex", -] - -[[package]] -name = "rlp" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa24e92bb2a83198bb76d661a71df9f7076b8c420b8696e4d3d97d50d94479e3" -dependencies = [ - "bytes", - "rustc-hex", -] - -[[package]] -name = "rocksdb" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddb7af00d2b17dbd07d82c0063e25411959748ff03e8d4f96134c2ff41fce34f" -dependencies = [ - "libc", - "librocksdb-sys", -] - -[[package]] -name = "rrs-succinct" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3372685893a9f67d18e98e792d690017287fd17379a83d798d958e517d380fa9" -dependencies = [ - "downcast-rs", - "num_enum 0.5.11", - "paste", -] - -[[package]] -name = "rug" -version = "1.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f6c8f906c90b48e0c1745c9f814c3a31c5eba847043b05c3e9a934dec7c4b3" -dependencies = [ - "az", - "gmp-mpfr-sys", - "libc", - "libm", -] - -[[package]] -name = "ruint" -version = "1.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c141e807189ad38a07276942c6623032d3753c8859c146104ac2e4d68865945a" -dependencies = [ - "alloy-rlp", - "ark-ff 0.3.0", - "ark-ff 0.4.2", - "ark-ff 0.5.0", - "bytes", - "fastrlp 0.3.1", - "fastrlp 0.4.0", - "num-bigint 0.4.6", - "num-integer", - "num-traits", - "parity-scale-codec", - "primitive-types 0.12.2", - "proptest", - "rand 0.8.5", - "rand 0.9.3", - "rlp 0.5.2", - "ruint-macro", - "serde_core", - "valuable", - "zeroize", -] - -[[package]] -name = "ruint-macro" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" - -[[package]] -name = "rustc-demangle" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hash" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" -dependencies = [ - "semver 0.11.0", -] - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver 1.0.28", -] - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.11.0", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.11.0", - "errno", - "libc", - "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f9466fb2c14ea04357e91413efb882e2a6d4a406e625449bc0a5d360d53a21" -dependencies = [ - "aws-lc-rs", - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20a6af516fea4b20eccceaf166e8aa666ac996208e8a644ce3ef5aa783bc7cd4" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - -[[package]] -name = "rusty-fork" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" -dependencies = [ - "fnv", - "quick-error", - "tempfile", - "wait-timeout", -] - -[[package]] -name = "rustyline" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1e066dc922e513bda599c6ccb5f3bb2b0ea5870a579448f2622993f0a9a2f" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "clipboard-win", - "fd-lock", - "home", - "libc", - "log", - "memchr", - "nix 0.29.0", - "radix_trie", - "rustyline-derive", - "unicode-segmentation", - "unicode-width 0.2.0", - "utf8parse", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustyline-derive" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d66de233f908aebf9cc30ac75ef9103185b4b715c6f2fb7a626aa5e5ede53ab" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "ryu" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" - -[[package]] -name = "safe_arch" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "salsa20" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" -dependencies = [ - "cipher", -] - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scale-info" -version = "2.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" -dependencies = [ - "cfg-if", - "derive_more 1.0.0", - "parity-scale-codec", - "scale-info-derive", -] - -[[package]] -name = "scale-info-derive" -version = "2.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" -dependencies = [ - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "scc" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" -dependencies = [ - "sdd", -] - -[[package]] -name = "schannel" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "scrypt" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f9e24d2b632954ded8ab2ef9fea0a0c769ea56ea98bddbafbad22caeeadf45d" -dependencies = [ - "hmac", - "pbkdf2", - "salsa20", - "sha2 0.10.9", -] - -[[package]] -name = "sdd" -version = "3.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" - -[[package]] -name = "sealed" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array 0.14.7", - "pkcs8", - "serdect", - "subtle", - "zeroize", -] - -[[package]] -name = "secp256k1" -version = "0.30.0" -source = "git+https://github.com/sp1-patches/rust-secp256k1?tag=patch-0.30.0-sp1-5.0.0#04d87db04bcc2dc5dd8e1ab3f046cc655440d07a" -dependencies = [ - "bitcoin_hashes", - "cfg-if", - "k256 0.13.4 (git+https://github.com/sp1-patches/elliptic-curves?tag=patch-k256-13.4-sp1-5.0.0)", - "rand 0.8.5", - "secp256k1-sys 0.10.0", - "serde", -] - -[[package]] -name = "secp256k1" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" -dependencies = [ - "bitcoin_hashes", - "rand 0.9.3", - "secp256k1-sys 0.11.0", -] - -[[package]] -name = "secp256k1-sys" -version = "0.10.0" -source = "git+https://github.com/sp1-patches/rust-secp256k1?tag=patch-0.30.0-sp1-5.0.0#04d87db04bcc2dc5dd8e1ab3f046cc655440d07a" -dependencies = [ - "cc", -] - -[[package]] -name = "secp256k1-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb913707158fadaf0d8702c2db0e857de66eb003ccfdda5924b5f5ac98efb38" -dependencies = [ - "cc", -] - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "semver-parser" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9900206b54a3527fdc7b8a938bffd94a568bac4f4aa8113b209df75a09c0dec2" -dependencies = [ - "pest", -] - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "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_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "indexmap 2.14.0", - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_path_to_error" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" -dependencies = [ - "itoa", - "serde", - "serde_core", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_with" -version = "3.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" -dependencies = [ - "base64", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.14.0", - "schemars 0.9.0", - "schemars 1.2.1", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" -dependencies = [ - "darling 0.23.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serdect" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" -dependencies = [ - "base16ct", - "serde", -] - -[[package]] -name = "serial_test" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" -dependencies = [ - "futures-executor", - "futures-util", - "log", - "once_cell", - "parking_lot 0.12.5", - "scc", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures 0.2.17", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - -[[package]] -name = "sha3" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" -dependencies = [ - "digest 0.10.7", - "keccak", -] - -[[package]] -name = "sha3-asm" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cbb88c189d6352cc8ae96a39d19c7ecad8f7330b29461187f2587fdc2988d5" -dependencies = [ - "cc", - "cfg-if", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" -dependencies = [ - "libc", - "mio", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest 0.10.7", - "rand_core 0.6.4", -] - -[[package]] -name = "simd-json" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c962f626b54771990066e5435ec8331d1462576cd2d1e62f24076ae014f92112" -dependencies = [ - "getrandom 0.3.4", - "halfbrown", - "ref-cast", - "serde", - "serde_json", - "simdutf8", - "value-trait", -] - -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - -[[package]] -name = "simple_asn1" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" -dependencies = [ - "num-bigint 0.4.6", - "num-traits", - "thiserror 2.0.18", - "time", -] - -[[package]] -name = "siphasher" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" - -[[package]] -name = "size" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fed904c7fb2856d868b92464fc8fa597fce366edea1a9cbfaa8cb5fe080bd6d" - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[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 0.3.2-succinct", - "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 0.3.2-succinct", - "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 0.3.2-succinct", - "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 0.3.2-succinct", -] - -[[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 0.3.2-succinct", -] - -[[package]] -name = "slug" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" -dependencies = [ - "deunicode", - "wasm-bindgen", -] - -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -dependencies = [ - "serde", -] - -[[package]] -name = "snap" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" - -[[package]] -name = "snowbridge-amcl" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460a9ed63cdf03c1b9847e8a12a5f5ba19c4efd5869e4a737e05be25d7c427e5" -dependencies = [ - "parity-scale-codec", - "scale-info", -] - -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socket2" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" -dependencies = [ - "libc", - "windows-sys 0.61.2", -] - -[[package]] -name = "sp1-build" -version = "5.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d96525859507f33edf4389c16470d21f6515d33ae7a2c73d5fb0aea36f5aba" -dependencies = [ - "anyhow", - "cargo_metadata", - "chrono", - "clap 4.6.0", - "dirs 5.0.1", - "sp1-prover", -] - -[[package]] -name = "sp1-core-executor" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2363566d0d4213d0ffd93cfcc1a5e413e2af8682213d3e65b90ac0af5623e3" -dependencies = [ - "bincode", - "bytemuck", - "clap 4.6.0", - "elf", - "enum-map", - "eyre", - "hashbrown 0.14.5", - "hex", - "itertools 0.13.0", - "nohash-hasher", - "num", - "p3-baby-bear", - "p3-field 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "rand 0.8.5", - "range-set-blaze", - "rrs-succinct", - "serde", - "serde_json", - "sp1-curves", - "sp1-primitives 5.2.4", - "sp1-stark", - "strum 0.26.3", - "strum_macros 0.26.4", - "subenum", - "thiserror 1.0.69", - "tiny-keccak", - "tracing", - "typenum", - "vec_map", -] - -[[package]] -name = "sp1-core-machine" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bd3ff75c100e24b89a7b513e082ec3e040c4c9f1cd779b6ba475c5bdc1aa7ad" -dependencies = [ - "bincode", - "cbindgen", - "cc", - "cfg-if", - "elliptic-curve", - "generic-array 1.1.0", - "glob", - "hashbrown 0.14.5", - "hex", - "itertools 0.13.0", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "num", - "num_cpus", - "p256", - "p3-air", - "p3-baby-bear", - "p3-challenger 0.2.3-succinct", - "p3-field 0.2.3-succinct", - "p3-keccak-air", - "p3-matrix 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-poseidon2 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "p3-uni-stark", - "p3-util 0.2.3-succinct", - "pathdiff", - "rand 0.8.5", - "rayon", - "rayon-scan", - "serde", - "serde_json", - "size", - "snowbridge-amcl", - "sp1-core-executor", - "sp1-curves", - "sp1-derive", - "sp1-primitives 5.2.4", - "sp1-stark", - "static_assertions", - "strum 0.26.3", - "strum_macros 0.26.4", - "tempfile", - "thiserror 1.0.69", - "tracing", - "tracing-forest", - "tracing-subscriber 0.3.23", - "typenum", - "web-time", -] - -[[package]] -name = "sp1-cuda" -version = "5.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d56209b50707201184746b749d3e791f3d33411539f508a563405de1fb8694" -dependencies = [ - "bincode", - "ctrlc", - "prost", - "serde", - "sp1-core-machine", - "sp1-prover", - "tokio", - "tracing", - "twirp-rs", -] - -[[package]] -name = "sp1-curves" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a5dc6007e0c1f35afe334e45531e17b8b347fdf73f6e7786ef5c1bc2218e30" -dependencies = [ - "cfg-if", - "dashu", - "elliptic-curve", - "generic-array 1.1.0", - "itertools 0.13.0", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "num", - "p256", - "p3-field 0.2.3-succinct", - "serde", - "snowbridge-amcl", - "sp1-primitives 5.2.4", - "sp1-stark", - "typenum", -] - -[[package]] -name = "sp1-derive" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a1ed8d5acbb6cea056401791e79ca3cba7c7d5e17d0d44cd60e117f16b11ca" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "sp1-lib" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73b8ff343f2405d5935440e56b7aba5cee6d87303f0051974cbd6f5de502f57" -dependencies = [ - "bincode", - "elliptic-curve", - "serde", - "sp1-primitives 5.2.4", -] - -[[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 6.1.0", -] - -[[package]] -name = "sp1-primitives" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e69a03098f827102c54c31a5e57280eb45b2c085de433b3f702e4f9e3ec1641" -dependencies = [ - "bincode", - "blake3", - "cfg-if", - "hex", - "lazy_static", - "num-bigint 0.4.6", - "p3-baby-bear", - "p3-field 0.2.3-succinct", - "p3-poseidon2 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "serde", - "sha2 0.10.9", -] - -[[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-prover" -version = "5.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fa3bb2b42cd36c1045472900dcc348a61475774734a5d8cdd4acaf929396ff" -dependencies = [ - "anyhow", - "bincode", - "clap 4.6.0", - "dirs 5.0.1", - "downloader", - "enum-map", - "eyre", - "hashbrown 0.14.5", - "hex", - "itertools 0.13.0", - "lru 0.12.5", - "num-bigint 0.4.6", - "p3-baby-bear", - "p3-bn254-fr 0.2.3-succinct", - "p3-challenger 0.2.3-succinct", - "p3-commit", - "p3-field 0.2.3-succinct", - "p3-matrix 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "rayon", - "serde", - "serde_json", - "serial_test", - "sha2 0.10.9", - "sp1-core-executor", - "sp1-core-machine", - "sp1-primitives 5.2.4", - "sp1-recursion-circuit", - "sp1-recursion-compiler", - "sp1-recursion-core", - "sp1-recursion-gnark-ffi", - "sp1-stark", - "thiserror 1.0.69", - "tracing", - "tracing-appender", - "tracing-subscriber 0.3.23", -] - -[[package]] -name = "sp1-recursion-circuit" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c4a3739e84f154becfc7d2a57d23c825ac83313feec64569b86090395c33fab" -dependencies = [ - "hashbrown 0.14.5", - "itertools 0.13.0", - "num-traits", - "p3-air", - "p3-baby-bear", - "p3-bn254-fr 0.2.3-succinct", - "p3-challenger 0.2.3-succinct", - "p3-commit", - "p3-dft 0.2.3-succinct", - "p3-field 0.2.3-succinct", - "p3-fri", - "p3-matrix 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "p3-uni-stark", - "p3-util 0.2.3-succinct", - "rand 0.8.5", - "rayon", - "serde", - "sp1-core-executor", - "sp1-core-machine", - "sp1-derive", - "sp1-primitives 5.2.4", - "sp1-recursion-compiler", - "sp1-recursion-core", - "sp1-recursion-gnark-ffi", - "sp1-stark", - "tracing", -] - -[[package]] -name = "sp1-recursion-compiler" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06aa784cfdc5c979da22ad6c36fe393e9005b6b57702fa9bdd041f112ead5ec5" -dependencies = [ - "backtrace", - "itertools 0.13.0", - "p3-baby-bear", - "p3-bn254-fr 0.2.3-succinct", - "p3-field 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "serde", - "sp1-core-machine", - "sp1-primitives 5.2.4", - "sp1-recursion-core", - "sp1-recursion-derive", - "sp1-stark", - "tracing", - "vec_map", -] - -[[package]] -name = "sp1-recursion-core" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be0db07b18f95f4e04f63f7f12a6547efd10601e2ce180aaf7868aa1bd98257" -dependencies = [ - "backtrace", - "cbindgen", - "cc", - "cfg-if", - "ff 0.13.1", - "glob", - "hashbrown 0.14.5", - "itertools 0.13.0", - "num_cpus", - "p3-air", - "p3-baby-bear", - "p3-bn254-fr 0.2.3-succinct", - "p3-challenger 0.2.3-succinct", - "p3-commit", - "p3-dft 0.2.3-succinct", - "p3-field 0.2.3-succinct", - "p3-fri", - "p3-matrix 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-merkle-tree", - "p3-poseidon2 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "p3-util 0.2.3-succinct", - "pathdiff", - "rand 0.8.5", - "serde", - "sp1-core-machine", - "sp1-derive", - "sp1-primitives 5.2.4", - "sp1-stark", - "static_assertions", - "thiserror 1.0.69", - "tracing", - "vec_map", - "zkhash", -] - -[[package]] -name = "sp1-recursion-derive" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b190465c0c0377f3cacfac2d0ac8a630adf8e1bfac8416be593753bfa4f668e" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "sp1-recursion-gnark-ffi" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "933ef703fb1c7a25e987a76ad705e60bb53730469766363b771baf3082a50fa0" -dependencies = [ - "anyhow", - "bincode", - "bindgen 0.70.1", - "cc", - "cfg-if", - "hex", - "num-bigint 0.4.6", - "p3-baby-bear", - "p3-field 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "serde", - "serde_json", - "sha2 0.10.9", - "sp1-core-machine", - "sp1-recursion-compiler", - "sp1-stark", - "tempfile", - "tracing", -] - -[[package]] -name = "sp1-sdk" -version = "5.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6f73c5efb1f55c0b6dca8a9427124eff4e36bd57108a96a7eb5a6034cf61a1" -dependencies = [ - "alloy-primitives", - "anyhow", - "async-trait", - "backoff", - "bincode", - "cfg-if", - "dirs 5.0.1", - "eventsource-stream", - "futures", - "hashbrown 0.14.5", - "hex", - "indicatif", - "itertools 0.13.0", - "k256 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", - "p3-baby-bear", - "p3-field 0.2.3-succinct", - "p3-fri", - "prost", - "reqwest 0.12.28", - "reqwest-middleware", - "serde", - "serde_json", - "sp1-build", - "sp1-core-executor", - "sp1-core-machine", - "sp1-cuda", - "sp1-primitives 5.2.4", - "sp1-prover", - "sp1-stark", - "strum 0.26.3", - "strum_macros 0.26.4", - "tempfile", - "thiserror 1.0.69", - "tokio", - "tonic", - "tracing", - "twirp-rs", -] - -[[package]] -name = "sp1-stark" -version = "5.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e99d1cc89ba28fc95736afb1e6ad22b9eb689e95a1dbb29cf0e9d1fa4fc2a5c" -dependencies = [ - "arrayref", - "hashbrown 0.14.5", - "itertools 0.13.0", - "num-bigint 0.4.6", - "num-traits", - "p3-air", - "p3-baby-bear", - "p3-challenger 0.2.3-succinct", - "p3-commit", - "p3-dft 0.2.3-succinct", - "p3-field 0.2.3-succinct", - "p3-fri", - "p3-matrix 0.2.3-succinct", - "p3-maybe-rayon 0.2.3-succinct", - "p3-merkle-tree", - "p3-poseidon2 0.2.3-succinct", - "p3-symmetric 0.2.3-succinct", - "p3-uni-stark", - "p3-util 0.2.3-succinct", - "rayon-scan", - "serde", - "sp1-derive", - "sp1-primitives 5.2.4", - "strum 0.26.3", - "sysinfo", - "tracing", -] - -[[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 6.1.0", - "subtle", -] - -[[package]] -name = "spawned-concurrency" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc21166874e8cd7584ea795c223303160461f0bb1b571bc23e92ca2abb7c5149" -dependencies = [ - "futures", - "pin-project-lite", - "spawned-macros", - "spawned-rt", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "spawned-macros" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d64742b41741dfebd5b5ba4dbc4cbc5cc91f4a2cf8107191007d64295682973" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "spawned-rt" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e270e6606a118708120671f2d171316762fa832cab73699c714c23aaafe6eb" -dependencies = [ - "ctrlc", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "tracing-subscriber 0.3.23", -] - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spinoff" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20aa2ed67fbb202e7b716ff8bfc6571dd9301617767380197d701c31124e88f6" -dependencies = [ - "colored", - "once_cell", - "paste", -] - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "strum" -version = "0.26.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" -dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" -dependencies = [ - "strum_macros 0.27.2", -] - -[[package]] -name = "strum_macros" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", - "syn 2.0.117", -] - -[[package]] -name = "strum_macros" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "subenum" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3d08fe7078c57309d5c3d938e50eba95ba1d33b9c3a101a8465fc6861a5416" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "substrate-bn" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" -dependencies = [ - "byteorder", - "crunchy", - "lazy_static", - "rand 0.8.5", - "rustc-hex", -] - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn-solidity" -version = "1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" -dependencies = [ - "paste", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sysinfo" -version = "0.30.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" -dependencies = [ - "cfg-if", - "core-foundation-sys", - "libc", - "ntapi", - "once_cell", - "rayon", - "windows", -] - -[[package]] -name = "system-configuration" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.9.4", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - -[[package]] -name = "tempfile" -version = "3.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" -dependencies = [ - "fastrand", - "getrandom 0.4.2", - "once_cell", - "rustix 1.1.4", - "windows-sys 0.61.2", -] - -[[package]] -name = "tera" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8004bca281f2d32df3bacd59bc67b312cb4c70cea46cbd79dbe8ac5ed206722" -dependencies = [ - "chrono", - "chrono-tz", - "globwalk", - "humansize", - "lazy_static", - "percent-encoding", - "pest", - "pest_derive", - "rand 0.8.5", - "regex", - "serde", - "serde_json", - "slug", - "unicode-segmentation", -] - -[[package]] -name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "term_size" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width 0.1.14", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "tikv-jemalloc-sys" -version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "tikv-jemallocator" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a" -dependencies = [ - "libc", - "tikv-jemalloc-sys", -] - -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - -[[package]] -name = "tinystr" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokei" -version = "12.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a41f915e075a8a98ad64a5f7be6b7cc1710fc835c5f07e4a3efcaeb013291c00" -dependencies = [ - "aho-corasick 0.7.20", - "clap 2.34.0", - "crossbeam-channel", - "dashmap 4.0.2", - "dirs 3.0.2", - "encoding_rs_io", - "env_logger", - "grep-searcher", - "ignore", - "log", - "num-format", - "once_cell", - "parking_lot 0.11.2", - "rayon", - "regex", - "serde", - "serde_json", - "tera", - "term_size", - "toml 0.5.11", -] - -[[package]] -name = "tokio" -version = "1.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" -dependencies = [ - "bytes", - "libc", - "mio", - "parking_lot 0.12.5", - "pin-project-lite", - "signal-hook-registry", - "socket2 0.6.3", - "tokio-macros", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-macros" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-stream" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", - "tokio-util", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - -[[package]] -name = "tokio-util" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "futures-util", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "toml" -version = "0.8.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime 0.6.11", - "toml_edit 0.22.27", -] - -[[package]] -name = "toml_datetime" -version = "0.6.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "1.1.1+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.14.0", - "toml_datetime 0.6.11", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" -dependencies = [ - "indexmap 2.14.0", - "serde", - "serde_spanned", - "toml_datetime 0.6.11", - "toml_write", - "winnow 0.7.15", -] - -[[package]] -name = "toml_edit" -version = "0.25.11+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" -dependencies = [ - "indexmap 2.14.0", - "toml_datetime 1.1.1+spec-1.1.0", - "toml_parser", - "winnow 1.0.1", -] - -[[package]] -name = "toml_parser" -version = "1.1.2+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" -dependencies = [ - "winnow 1.0.1", -] - -[[package]] -name = "toml_write" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" - -[[package]] -name = "tonic" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" -dependencies = [ - "async-stream", - "async-trait", - "axum 0.7.9", - "base64", - "bytes", - "h2", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-timeout", - "hyper-util", - "percent-encoding", - "pin-project", - "prost", - "rustls-native-certs", - "rustls-pemfile", - "socket2 0.5.10", - "tokio", - "tokio-rustls", - "tokio-stream", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" -dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand 0.8.5", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags 2.11.0", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower 0.5.3", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[package]] -name = "tracing" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" -dependencies = [ - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-appender" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" -dependencies = [ - "crossbeam-channel", - "thiserror 2.0.18", - "time", - "tracing-subscriber 0.3.23", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-forest" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee40835db14ddd1e3ba414292272eddde9dad04d3d4b65509656414d1c42592f" -dependencies = [ - "ansi_term", - "smallvec", - "thiserror 1.0.69", - "tracing", - "tracing-subscriber 0.3.23", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" -dependencies = [ - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "tui-logger" -version = "0.17.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382b7ea88082dbe2236ed1e942552b1bfc59e98fdc5d0599f11a627aae9ee2be" -dependencies = [ - "chrono", - "env_filter", - "lazy_static", - "log", - "parking_lot 0.12.5", - "ratatui", - "tracing", - "tracing-subscriber 0.3.23", - "unicode-segmentation", -] - -[[package]] -name = "tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.3", - "sha1", - "thiserror 2.0.18", - "utf-8", -] - -[[package]] -name = "twirp-rs" -version = "0.13.0-succinct" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27dfcc06b8d9262bc2d4b8d1847c56af9971a52dd8a0076876de9db763227d0d" -dependencies = [ - "async-trait", - "axum 0.7.9", - "futures", - "http", - "http-body-util", - "hyper", - "prost", - "reqwest 0.12.28", - "serde", - "serde_json", - "thiserror 1.0.69", - "tokio", - "tower 0.5.3", - "url", -] - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "ucd-trie" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" - -[[package]] -name = "uint" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "uint" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - -[[package]] -name = "unarray" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" - -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - -[[package]] -name = "unicode-ident" -version = "1.0.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" - -[[package]] -name = "unicode-segmentation" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" - -[[package]] -name = "unicode-truncate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" -dependencies = [ - "itertools 0.13.0", - "unicode-segmentation", - "unicode-width 0.1.14", -] - -[[package]] -name = "unicode-width" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" - -[[package]] -name = "unicode-width" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "url" -version = "2.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", - "serde", - "serde_derive", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "utf8parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" - -[[package]] -name = "uuid" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" -dependencies = [ - "getrandom 0.2.17", - "serde", -] - -[[package]] -name = "uuid" -version = "1.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" -dependencies = [ - "getrandom 0.4.2", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "value-trait" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0508fce11ad19e0aab49ce20b6bec7f8f82902ded31df1c9fc61b90f0eb396b8" -dependencies = [ - "float-cmp", - "halfbrown", - "itoa", - "ryu", -] - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -dependencies = [ - "serde", -] - -[[package]] -name = "vergen" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" -dependencies = [ - "anyhow", - "derive_builder", - "rustc_version 0.4.1", - "rustversion", - "vergen-lib 9.1.0", -] - -[[package]] -name = "vergen-git2" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f6ee511ec45098eabade8a0750e76eec671e7fb2d9360c563911336bea9cac1" -dependencies = [ - "anyhow", - "derive_builder", - "git2", - "rustversion", - "time", - "vergen", - "vergen-lib 0.1.6", -] - -[[package]] -name = "vergen-git2" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51ab55ddf1188c8d679f349775362b0fa9e90bd7a4ac69838b2a087623f0d57" -dependencies = [ - "anyhow", - "derive_builder", - "git2", - "rustversion", - "time", - "vergen", - "vergen-lib 9.1.0", -] - -[[package]] -name = "vergen-lib" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", -] - -[[package]] -name = "vergen-lib" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "vsimd" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" - -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - -[[package]] -name = "walkdir" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" -dependencies = [ - "same-file", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wasm-bindgen" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.118" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.14.0", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasm-streams" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" -dependencies = [ - "futures-util", - "js-sys", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags 2.11.0", - "hashbrown 0.15.5", - "indexmap 2.14.0", - "semver 1.0.28", -] - -[[package]] -name = "wasmtimer" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.12.5", - "pin-utils", - "slab", - "wasm-bindgen", -] - -[[package]] -name = "web-sys" -version = "0.3.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "webpki-roots" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "wide" -version = "0.7.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" -dependencies = [ - "bytemuck", - "safe_arch", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core 0.52.0", - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", -] - -[[package]] -name = "windows-sys" -version = "0.61.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - -[[package]] -name = "windows_i686_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.7.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" -dependencies = [ - "memchr", -] - -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap 2.14.0", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "indexmap 2.14.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.14.0", - "log", - "semver 1.0.28", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "writeable" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" - -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - -[[package]] -name = "yoke" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" -dependencies = [ - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zerocopy" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerofrom" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "synstructure", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "zerotrie" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", -] - -[[package]] -name = "zerovec" -version = "0.11.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[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 0.7.1", - "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 = "zmij" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/tooling/Cargo.toml b/tooling/Cargo.toml deleted file mode 100644 index 0f8a310280e..00000000000 --- a/tooling/Cargo.toml +++ /dev/null @@ -1,108 +0,0 @@ -[workspace] -members = [ - "archive_sync", - "ef_tests/blockchain", - "ef_tests/state", - "ef_tests/state_v2", - "hive_report", - "load_test", - "loc", - "migrations", - "monitor", - "reorgs", -] -exclude = ["ef_tests/state"] -resolver = "2" - -[workspace.package] -version = "4.0.0" -edition = "2024" -authors = ["LambdaClass"] -documentation = "https://docs.ethrex.xyz" -license = "MIT OR Apache-2.0" - -[workspace.dependencies] -ethrex-blockchain = { path = "../crates/blockchain" } -ethrex-common = { path = "../crates/common" } -ethrex-crypto = { path = "../crates/common/crypto" } -ethrex-config = { path = "../crates/common/config" } -ethrex-p2p = { path = "../crates/networking/p2p" } -ethrex-rpc = { path = "../crates/networking/rpc" } -ethrex-storage = { path = "../crates/storage" } -ethrex-vm = { path = "../crates/vm", default-features = false } -ethrex-levm = { path = "../crates/vm/levm" } -ethrex-trie = { path = "../crates/common/trie" } -ethrex-rlp = { path = "../crates/common/rlp" } -ethrex-l2 = { path = "../crates/l2" } -ethrex-l2-common = { path = "../crates/l2/common" } -ethrex-sdk = { path = "../crates/l2/sdk" } -ethrex-l2-prover = { path = "../crates/l2/prover" } -ethrex-prover = { path = "../crates/prover", default-features = false } -ethrex-storage-rollup = { path = "../crates/l2/storage" } -ethrex = { path = "../cmd/ethrex" } -ethrex-l2-rpc = { path = "../crates/l2/networking/rpc" } - -tracing = { version = "0.1", features = ["log"] } -tracing-subscriber = { version = "0.3.0", features = ["env-filter"] } - -async-trait = "0.1.88" -ethereum-types = { version = "0.15.1", features = ["serialize"] } -serde = { version = "1.0.203", features = ["derive"] } -serde_with = "3.11.0" -serde_json = "1.0.117" -bytes = { version = "1.6.0", features = ["serde"] } -tokio = { version = "1.41.1", default-features = false } -thiserror = "2.0.9" -hex = "0.4.3" -hex-literal = "0.4.1" -crc32fast = "1.4.2" -lazy_static = "1.5.0" -sha2 = "0.10.8" -sha3 = "0.10.8" -tokio-util = { version = "0.7.15", features = ["rt"] } -jsonwebtoken = "9.3.0" -rand = "0.8.5" -reqwest = { version = "0.12.7", features = ["socks", "json"] } -snap = "1.1.1" -secp256k1 = { version = "0.30.0", default-features = false, features = [ - "global-context", - "recovery", - "rand", -] } -axum = { version = "0.8.1" } -clap = { version = "4.3", features = ["derive", "env"] } -clap_complete = "4.5.17" -eyre = "0.6.12" -rustc-hex = "2.1.0" -url = { version = "2.5.4", features = ["serde"] } -kzg-rs = "0.2.6" -libsql = "0.9.10" -futures = "0.3.31" -spawned-concurrency = "0.5.0" -spawned-rt = "0.5.0" -lambdaworks-crypto = "0.13.0" -tui-logger = { version = "0.17.3", features = ["tracing-support"] } -crossbeam = "0.8.4" -rayon = "1.10.0" -rkyv = { version = "0.8.10", features = ["std", "unaligned"] } -tempfile = "3.8" -uuid = { version = "1.18.1", features = ["v4"] } -tower-http = { version = "0.6.2", features = ["cors"] } -indexmap = { version = "2.11.4" } - -rocksdb = "0.24.0" - -[profile.dev] -debug = 2 - -[profile.release] -opt-level = 3 -lto = "thin" -codegen-units = 1 - -[profile.release-with-debug] -inherits = "release" -debug = 2 - -[patch.crates-io] -secp256k1 = { git = "https://github.com/sp1-patches/rust-secp256k1", tag = "patch-0.30.0-sp1-5.0.0" } diff --git a/tooling/archive_sync/Cargo.toml b/tooling/archive_sync/Cargo.toml deleted file mode 100644 index f7031ce4639..00000000000 --- a/tooling/archive_sync/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "archive_sync" -version.workspace = true -edition.workspace = true -authors.workspace = true -documentation.workspace = true -license.workspace = true - -[dependencies] -lazy_static.workspace = true -ethrex-common.workspace = true -ethrex-crypto.workspace = true -ethrex-storage.workspace = true -ethrex-rlp.workspace = true -ethrex-rpc.workspace = true -tokio.workspace = true -tracing.workspace = true -tracing-subscriber = { version = "0.3", features = ["fmt"] } -clap = { workspace = true, features = ["string"] } -clap_complete.workspace = true -eyre.workspace = true -reqwest.workspace = true -serde.workspace = true -serde_json.workspace = true -hex.workspace = true -ethrex.workspace = true diff --git a/tooling/archive_sync/README.md b/tooling/archive_sync/README.md deleted file mode 100644 index b98cdb717ca..00000000000 --- a/tooling/archive_sync/README.md +++ /dev/null @@ -1,88 +0,0 @@ -# Archive Sync - -Archive sync can be used to download the whole state of a particular block from an active archive node. - -Note that this involves using IPC to communicate with the archive node so both the archive node and ethrex must be on the same computer/server. - -We also rely on geth's debug api for this, so it is not guaranteed to work for other non-geth-compatible implementations. - -## Usage - -### Step 1: Launch an archive node - -You can do so by running: - -```bash - geth --NETWORK --http.addr 0.0.0.0 --ipcpath IPC_PATH --syncmode full --gcmode archive --state.scheme=hash -``` - -You may get the following error if you have been using geth as non-archive before: - -```bash -Failed to register the Ethereum service: incompatible state scheme, stored: hash, provided: path -``` - -This should be solved by either clearing the DB or fully uninstalling and reinstalling geth - -You should also launch a consensus node. You can read more about this on the main [README](../../README.md) - -You may skip this step if you already have an archive node up and running. - -### Step 2: Run the `archive_sync` executable - -This executable takes 3 arguments: - -* The IPC path of the archive node: - - This will be the `IPC_PATH` we used when launching geth + the path to geth's data. For example, in a geth installed by brew on mac, the ipc file will be located at: /Users/USERNAME/Library/Ethereum/NETWORK/IPC_PATH. This can vary depending on your system so I recommend looking for this kind of log line when you startup geth which will give you the full path: - - ```bash - INFO [06-19|11:37:09.316] IPC endpoint opened url=/Users/USER/Library/Ethereum/IPC_PATH - ``` - -* The number of the block you want to sync to: - - Our most common use case for this tool will be to do fullsync on networks like mainnet and sepolia, but starting from the first post-merge blocks, so we will commonly be using the blocks right after the merge transition blocks for this tool which should be: - - - Sepolia Next Block After Merge: 1450410 - - Mainnet Next Block After Merge: 15537395 - -* (Optional with flag --datadir): - - The path to the DB directory, if none is set the default will be used - -With these arguments you can run the following command from this directory: - -```bash - cargo run --release BLOCK_NUMBER --ipc_path IPC_PATH -``` - -And adding `--datadir DATADIR` if you want to use a custom directory - -While archive sync is faster than the alternatives (snap, full) it can still take a long time on later blocks of large chains - -## Usage without an active archive node connection - -We can avoid relying on an active archive node connection once we have already performed the first sync by writing the state dump to a directory. Note that this will still require an active archive node for the first step. - -### Step 1: Run the `archive_sync` executable as usual with `--output_dir` flag - -```bash - cargo run --release BLOCK_NUMBER --ipc_path IPC_PATH --output_dir STATE_DUMP_DIR -``` - -If we don't need the node to be synced (for example if we plan to move the state dump to another server after the sync) we can also add the flag `--no_sync` to skip the state sync and only write the state data to files. - -### Step 2: Run the `archive_sync` executable with `--input_dir` instead of `--ipc_path` - -```bash - cargo run --release BLOCK_NUMBER --input_dir STATE_DUMP_DIR -``` - -## Resuming archive sync after a crash or manual stop - -In order to safely resume an archive sync process the `--checkpoint` flag can be used to provide a checkpoint file which will be periodically updated during the sync. This file can then be passed on to a second run to resume the sync from the latest checkpoint. It can be used with any supported flag combination. The checkpoint will not store the block number so please make sure you target the same block to avoid state inconsistencies. The tool will fail if the input flags are not compatible with the checkpoint data (ie running with `--ipc_path` and then using the same checkpoint with `--input_dir`). It will also warn and request for user approval if the new run is a downgrade from the previous run which generated the checkpoint (ie, `--no_sync` flag being added or `--output_dir` flag removed) to ensure no checkpoint data is mistakenly lost. For example, you may use this flag like this: - -```bash - cargo run --release BLOCK_NUMBER --ipc_path IPC_PATH --checkpoint CHECKPOINT_FILE --output_dir OUTPUT_DIRECTORY -``` diff --git a/tooling/archive_sync/src/main.rs b/tooling/archive_sync/src/main.rs deleted file mode 100644 index 39cfde5a904..00000000000 --- a/tooling/archive_sync/src/main.rs +++ /dev/null @@ -1,755 +0,0 @@ -lazy_static::lazy_static! { - static ref CLIENT: reqwest::Client = reqwest::Client::new(); -} - -use clap::{ArgGroup, Parser}; -use ethrex::initializers::open_store; -use ethrex::utils::{default_datadir, init_datadir}; -use ethrex_common::types::{BlockHash, Code}; -use ethrex_common::utils::keccak; -use ethrex_common::{Address, serde_utils}; -use ethrex_common::{BigEndianHash, Bytes, H256, U256, types::BlockNumber}; -use ethrex_common::{ - constants::{EMPTY_KECCACK_HASH, EMPTY_TRIE_HASH}, - types::{AccountState, Block}, -}; -use ethrex_rlp::decode::RLPDecode; -use ethrex_rlp::encode::RLPEncode; -use ethrex_rpc::utils::RpcResponse; -use ethrex_storage::Store; -use serde::{Deserialize, Serialize}; -use serde_json::{Value, json}; -use std::collections::HashMap; -use std::fs::File; -use std::io::{self, Read, Write}; -use std::path::PathBuf; -use std::time::Instant; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; -use tokio::net::UnixStream; -use tokio::task::JoinSet; -use tracing::{debug, info}; -use tracing_subscriber::FmtSubscriber; - -/// Max account dumps to ask for in a single request. The current value matches geth's maximum output. -const MAX_ACCOUNTS: usize = 256; -/// Amount of blocks before the target block to request hashes for. These may be needed to execute the next block after the target block. -const BLOCK_HASH_LOOKUP_DEPTH: u64 = 128; -/// Amount of state dumps to process before updating checkpoint -const DUMPS_BEFORE_CHECKPOINT: usize = 10; - -#[derive(Deserialize, Debug, Serialize)] -struct Dump { - #[serde(rename = "root")] - state_root: H256, - accounts: HashMap, - #[serde(default)] - next: Option, -} - -#[derive(Deserialize, Debug, Serialize)] -#[serde(rename_all = "camelCase")] -struct DumpAccount { - #[serde(with = "serde_utils::u256::dec_str")] - balance: U256, - nonce: u64, - #[serde(rename = "root")] - storage_root: H256, - code_hash: H256, - #[serde(default, with = "serde_utils::bytes")] - code: Bytes, - #[serde(default)] - storage: HashMap, - address: Option
, - #[serde(rename = "key")] - hashed_address: Option, -} - -pub async fn archive_sync( - archive_ipc_path: Option, - block_number: BlockNumber, - output_dir: Option, - input_dir: Option, - no_sync: bool, - checkpoint: Option, - store: Store, -) -> eyre::Result<()> { - let sync_start: Instant = Instant::now(); - // Load checkpoint (if we have one) - let prev_checkpoint = load_checkpoint( - &checkpoint, - archive_ipc_path.is_some(), - input_dir.is_some(), - output_dir.is_some(), - no_sync, - )?; - let mut dump_reader = if let Some(ipc_path) = archive_ipc_path { - DumpReader::new_from_ipc(&ipc_path, block_number, &prev_checkpoint).await? - } else { - DumpReader::new_from_dir(input_dir.unwrap(), &prev_checkpoint)? - }; - let dump_writer = output_dir - .map(|dir| DumpDirWriter::new(dir, &prev_checkpoint)) - .transpose()?; - let mut dump_processor = if no_sync { - DumpProcessor::new_no_sync(dump_writer) - } else { - DumpProcessor::new_sync(dump_writer, store, &prev_checkpoint) - }; - let mut should_continue = true; - let mut dumps_since_checkpoint = 0; - // Fetch and process dumps until we have the full block state - while should_continue { - let dump = dump_reader.read_dump().await?; - should_continue = dump_processor.process_dump(dump).await?; - // Write checkpoint every `DUMPS_BEFORE_CHECKPOINT` dumps if we have one - if let Some(checkpoint_filename) = checkpoint.as_ref() { - dumps_since_checkpoint += 1; - if dumps_since_checkpoint >= DUMPS_BEFORE_CHECKPOINT || !should_continue { - dumps_since_checkpoint = 0; - let checkpoint = CheckPoint { - processing: dump_processor.get_checkpoint(), - reading: dump_reader.get_checkpoint(), - }; - let checkpoint_file = File::create(checkpoint_filename)?; - serde_json::to_writer(checkpoint_file, &checkpoint)?; - } - } - } - // Fetch the block itself so we can mark it as canonical - let rlp_block = dump_reader.read_rlp_block().await?; - // Fetch the block hashes of the previous `BLOCK_HASH_LOOKUP_DEPTH` blocks - // as we might need them to execute the next blocks after archive sync - let block_hashes = dump_reader.read_block_hashes().await?; - // Process both as part of a FCU - dump_processor - .process_rlp_block_and_block_hashes(rlp_block, block_hashes) - .await?; - let sync_time = mseconds_to_readable(sync_start.elapsed().as_millis()); - info!("Archive Sync complete in {sync_time}"); - Ok(()) -} - -/// Adds all dump accounts to the trie on top of the current root, returns the next root -/// This could be improved in the future to use an in_memory trie with async db writes -async fn process_dump(dump: Dump, store: Store, current_root: H256) -> eyre::Result { - let mut storage_tasks = JoinSet::new(); - let mut state_trie = store.open_direct_state_trie(current_root)?; - for (address, dump_account) in dump.accounts.into_iter() { - let hashed_address = dump_account - .hashed_address - .unwrap_or_else(|| keccak(address)); - // Add account to state trie - // Maybe we can validate the dump account here? or while deserializing - state_trie.insert( - hashed_address.0.to_vec(), - dump_account.get_account_state().encode_to_vec(), - )?; - // Add code to DB if it is not empty - if dump_account.code_hash != *EMPTY_KECCACK_HASH { - store - .add_account_code(Code::from_bytecode( - dump_account.code.clone(), - ðrex_crypto::NativeCrypto, - )) - .await?; - } - // Process storage trie if it is not empty - if dump_account.storage_root != *EMPTY_TRIE_HASH { - storage_tasks.spawn(process_dump_storage( - dump_account.storage, - store.clone(), - hashed_address, - dump_account.storage_root, - )); - } - } - for res in storage_tasks.join_all().await { - res?; - } - Ok(state_trie.hash(ðrex_crypto::NativeCrypto)?) -} - -async fn process_dump_storage( - dump_storage: HashMap, - store: Store, - hashed_address: H256, - storage_root: H256, -) -> eyre::Result<()> { - let mut trie = store.open_direct_storage_trie(hashed_address, *EMPTY_TRIE_HASH)?; - for (key, val) in dump_storage { - // The key we receive is the preimage of the one stored in the trie - trie.insert(keccak(key.0).0.to_vec(), val.encode_to_vec())?; - } - if trie.hash(ðrex_crypto::NativeCrypto)? != storage_root { - Err(eyre::ErrReport::msg( - "Storage root doesn't match the one in the account during archive sync", - )) - } else { - Ok(()) - } -} - -async fn send_ipc_json_request(stream: &mut UnixStream, request: &Value) -> eyre::Result { - stream.write_all(request.to_string().as_bytes()).await?; - stream.write_all(b"\n").await?; - stream.flush().await?; - let mut response = Vec::new(); - while stream.read_buf(&mut response).await? != 0 { - if response.ends_with(b"\n") { - break; - } - } - let response: RpcResponse = serde_json::from_slice(&response)?; - match response { - RpcResponse::Success(success_res) => Ok(success_res.result), - RpcResponse::Error(error_res) => Err(eyre::ErrReport::msg(error_res.error.message)), - } -} - -fn hash_next(hash: H256) -> H256 { - H256::from_uint(&(hash.into_uint() + 1)) -} - -impl DumpAccount { - fn get_account_state(&self) -> AccountState { - AccountState { - nonce: self.nonce, - balance: self.balance, - storage_root: self.storage_root, - code_hash: self.code_hash, - } - } -} - -fn mseconds_to_readable(mut mseconds: u128) -> String { - const DAY: u128 = 24 * HOUR; - const HOUR: u128 = 60 * MINUTE; - const MINUTE: u128 = 60 * SECOND; - const SECOND: u128 = 1000 * MSECOND; - const MSECOND: u128 = 1; - let mut res = String::new(); - let mut apply_time_unit = |unit_in_ms: u128, unit_str: &str| { - if mseconds > unit_in_ms { - let amount_of_unit = mseconds / unit_in_ms; - res.push_str(&format!("{amount_of_unit}{unit_str}")); - mseconds -= unit_in_ms * amount_of_unit - } - }; - apply_time_unit(DAY, "d"); - apply_time_unit(HOUR, "h"); - apply_time_unit(MINUTE, "m"); - apply_time_unit(SECOND, "s"); - apply_time_unit(MSECOND, "ms"); - - res -} - -/// Struct in charge of processing incoming state data -/// Depending on its optional fields processing can refer to either writing the state into files -/// and/or rebuilding the block's state in the DB -struct DumpProcessor { - state_root: Option, - // Current Trie Root + Store. Set to None if state sync is disabled - sync_state: Option<(H256, Store)>, - writer: Option, -} - -impl DumpProcessor { - /// Create a new DumpProcessor that will rebuild a Block's state based on incoming state dumps - /// And which may write incoming data into files if writer is set - fn new_sync( - writer: Option, - store: Store, - prev_checkpoint: &Option, - ) -> Self { - Self { - state_root: None, - sync_state: Some(( - prev_checkpoint - .as_ref() - .and_then(|check_point| check_point.processing.current_root) - .unwrap_or(*EMPTY_TRIE_HASH), - store, - )), - writer, - } - } - - /// Create a new DumpProcessor which may write incoming data into files if writer is set - fn new_no_sync(writer: Option) -> Self { - Self { - state_root: None, - sync_state: None, - writer, - } - } - - /// Process incoming state dump by either writing it to a file and/or using it to rebuild the partial state - /// Will fail if the incoming dump's state root differs from the previously processed dump - async fn process_dump(&mut self, dump: Dump) -> eyre::Result { - // Sanity check - if *self.state_root.get_or_insert(dump.state_root) != dump.state_root { - return Err(eyre::ErrReport::msg( - "Archive node yielded different state roots for the same block dump", - )); - } - let should_continue = dump.next.is_some(); - // Write dump if we have an output - if let Some(writer) = self.writer.as_mut() { - writer.write_dump(&dump)?; - } - // Process dump - if let Some((current_root, store)) = self.sync_state.as_mut() { - let instant = Instant::now(); - *current_root = process_dump(dump, store.clone(), *current_root).await?; - info!( - "Processed Dump of {MAX_ACCOUNTS} accounts in {}", - mseconds_to_readable(instant.elapsed().as_millis()) - ); - } - Ok(should_continue) - } - - /// Process the incoming RLP-encoded Block by either writing it to a file and/or adding it as head of the canonical chain. - /// In the later case, the rebuilt state root will be chacked againts the block's state root - /// Processes the incoming list of block hashes by either writing them to a file and/or marking - /// them as part of the canonical chain. This will be necessary in order to execute blocks after the target block - async fn process_rlp_block_and_block_hashes( - &mut self, - rlp_block: Vec, - block_hashes: Vec<(BlockNumber, BlockHash)>, - ) -> eyre::Result<()> { - if let Some(writer) = self.writer.as_mut() { - writer.write_rlp_block(&rlp_block)?; - writer.write_hashes_file(&block_hashes)?; - } - if let Some((current_root, store)) = self.sync_state.as_ref() { - let block = Block::decode(&rlp_block)?; - let block_number = block.header.number; - let block_hash = block.hash(); - - if *current_root != block.header.state_root { - return Err(eyre::ErrReport::msg( - "State root doesn't match the one in the header after archive sync", - )); - } - - store.add_block(block).await?; - store - .forkchoice_update(block_hashes, block_number, block_hash, None, None) - .await?; - info!("Head of local chain is now block {block_number} with hash {block_hash}"); - } - Ok(()) - } - - fn get_checkpoint(&self) -> ProcessingCheckpoint { - ProcessingCheckpoint { - current_root: self - .sync_state - .as_ref() - .map(|(current_root, _)| *current_root), - current_file: self.writer.as_ref().map(|writer| writer.current_file), - } - } -} - -/// Struct in charge of writing state data into files on a given directory -struct DumpDirWriter { - dirname: String, - current_file: usize, -} - -impl DumpDirWriter { - /// Create a new DumpDirWriter which will write state data to files the given directory - /// It will create the directory if it doesn't exist yet - fn new(dirname: String, prev_checkpoint: &Option) -> eyre::Result { - if !std::path::Path::new(&dirname).exists() { - std::fs::create_dir(&dirname)?; - } - Ok(Self { - dirname, - current_file: prev_checkpoint - .as_ref() - .and_then(|checkpoint| checkpoint.processing.current_file) - .unwrap_or_default(), - }) - } - - /// Writes the incoming dump into a json file named `dump_n.json` at the directory set - /// in the struct's creation. Where n represents the order at which each dump was received - fn write_dump(&mut self, dump: &Dump) -> eyre::Result<()> { - let dump_file = File::create( - std::path::Path::new(&self.dirname).join(format!("dump_{}.json", self.current_file)), - )?; - serde_json::to_writer(dump_file, dump)?; - self.current_file += 1; - Ok(()) - } - - /// Writes the incoming RLP-encoded block into file named `block.rlp` - /// at the directory set in the struct's creation - fn write_rlp_block(&mut self, rlp: &[u8]) -> eyre::Result<()> { - let mut block_file: File = - File::create(std::path::Path::new(&self.dirname).join("block.rlp"))?; - block_file.write_all(rlp)?; - Ok(()) - } - - /// Writes the incoming block hahses into a json file named `block_hashes.json` - /// at the directory set in the struct's creation - fn write_hashes_file( - &mut self, - block_hashes: &Vec<(BlockNumber, BlockHash)>, - ) -> eyre::Result<()> { - let block_hashes_file = - File::create(std::path::Path::new(&self.dirname).join("block_hashes.json"))?; - serde_json::to_writer(block_hashes_file, block_hashes)?; - Ok(()) - } -} - -/// Struct in charge of fetching state data -/// This data may come from either IPC comunication with an active archive node -/// or a directory of files obtained from a previous archive-sync execution using --output-dir flag -enum DumpReader { - Dir(DumpDirReader), - Ipc(DumpIpcReader), -} - -/// Struct in charge of reading state data from a directory of files obtained -/// from a previous archive-sync execution using --output-dir flag -struct DumpDirReader { - dirname: String, - current_file: usize, -} - -/// Struct in charge of fetching state data from an IPC connection with an active archive node -struct DumpIpcReader { - stream: UnixStream, - block_number: BlockNumber, - start: H256, -} - -impl DumpReader { - /// Create a new DumpReader that will read state data from the given directory - fn new_from_dir(dirname: String, prev_checkpoint: &Option) -> eyre::Result { - let mut dir_reader = DumpDirReader::new(dirname)?; - if let Some(current) = prev_checkpoint - .as_ref() - .and_then(|checkpoint| checkpoint.reading.current_file) - { - dir_reader.current_file = current - } - Ok(Self::Dir(dir_reader)) - } - - /// Create a new DumpReader that will read state data from an archive node given the path to its IPC file - async fn new_from_ipc( - archive_ipc_path: &str, - block_number: BlockNumber, - prev_checkpoint: &Option, - ) -> eyre::Result { - let mut ipc_reader = DumpIpcReader::new(archive_ipc_path, block_number).await?; - if let Some(start) = prev_checkpoint - .as_ref() - .and_then(|checkpoint| checkpoint.reading.start_hash) - { - ipc_reader.start = start - } - Ok(Self::Ipc(ipc_reader)) - } - - /// Read the next state dump, either from a file or from an active IPC connection - async fn read_dump(&mut self) -> eyre::Result { - match self { - DumpReader::Dir(dump_dir_reader) => dump_dir_reader.read_dump(), - DumpReader::Ipc(dump_ipc_reader) => dump_ipc_reader.read_dump().await, - } - } - - /// Read the target RLP-encoded block, either from a file or from an active IPC connection - async fn read_rlp_block(&mut self) -> eyre::Result> { - match self { - DumpReader::Dir(dump_dir_reader) => dump_dir_reader.read_rlp_block(), - DumpReader::Ipc(dump_ipc_reader) => dump_ipc_reader.read_rlp_block().await, - } - } - - /// Read hashes of the `BLOCK_HASH_LOOKUP_DEPTH` blocks before the target block, - /// either from a file or from an active IPC connection - async fn read_block_hashes(&mut self) -> eyre::Result> { - match self { - DumpReader::Dir(dump_dir_reader) => dump_dir_reader.read_block_hashes(), - DumpReader::Ipc(dump_ipc_reader) => dump_ipc_reader.read_block_hashes().await, - } - } - - fn get_checkpoint(&self) -> ReadingCheckpoint { - let mut checkpoint = ReadingCheckpoint::default(); - match self { - DumpReader::Dir(dir_reader) => checkpoint.current_file = Some(dir_reader.current_file), - DumpReader::Ipc(ipc_reader) => checkpoint.start_hash = Some(ipc_reader.start), - } - checkpoint - } -} - -impl DumpDirReader { - /// Create a new DumpDirReader that will read state data from the given directory - fn new(dirname: String) -> eyre::Result { - if !std::path::Path::new(&dirname).exists() { - return Err(eyre::Error::msg("Input directory doesn't exist")); - } - Ok(Self { - dirname, - current_file: 0, - }) - } - - /// Read the next dump file from the directory set at creation - fn read_dump(&mut self) -> eyre::Result { - let dump_file = File::open( - std::path::Path::new(&self.dirname).join(format!("dump_{}.json", self.current_file)), - )?; - self.current_file += 1; - Ok(serde_json::from_reader(dump_file)?) - } - - /// Read the rlp block file from the directory set at creation - fn read_rlp_block(&mut self) -> eyre::Result> { - let mut block_file = File::open(std::path::Path::new(&self.dirname).join("block.rlp"))?; - let mut buffer = Vec::::new(); - block_file.read_to_end(&mut buffer)?; - Ok(buffer) - } - - /// Read the block hashes file from the directory set at creation - fn read_block_hashes(&mut self) -> eyre::Result> { - let hashes_file = - File::open(std::path::Path::new(&self.dirname).join("block_hashes.json"))?; - Ok(serde_json::from_reader(hashes_file)?) - } -} - -impl DumpIpcReader { - /// Create a new DumpIpcReader that will fetch incoming data by connecting to an active archive node - /// given the path to its IPC file - async fn new(archive_ipc_path: &str, block_number: BlockNumber) -> eyre::Result { - let stream = UnixStream::connect(archive_ipc_path).await?; - Ok(Self { - stream, - block_number, - start: H256::zero(), - }) - } - - /// Fetches the nex state dump from the archive node it is currently connected to via IPC - async fn read_dump(&mut self) -> eyre::Result { - // [debug_accountRange](https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-debug#debugaccountrange) - let request = &json!({ - "id": 1, - "jsonrpc": "2.0", - "method": "debug_accountRange", - "params": [format!("{:#x}", self.block_number), format!("{:#x}", self.start), MAX_ACCOUNTS, false, false, false] - }); - let response = send_ipc_json_request(&mut self.stream, request).await?; - let dump: Dump = serde_json::from_value(response)?; - // Find the next hash - let last_key = dump - .accounts - .iter() - .map(|(addr, acc)| acc.hashed_address.unwrap_or_else(|| keccak(addr))) - .max() - .unwrap_or_default(); - self.start = hash_next(last_key); - Ok(dump) - } - - /// Fetches the RLP-encoded target blocks from the archive node it is currently connected to via IPC - async fn read_rlp_block(&mut self) -> eyre::Result> { - // Request block so we can store it and mark it as canonical - let request = &json!({ - "id": 1, - "jsonrpc": "2.0", - "method": "debug_getRawBlock", - "params": [format!("{:#x}", self.block_number)] - }); - let response = send_ipc_json_request(&mut self.stream, request).await?; - let rlp_block_str: String = serde_json::from_value(response)?; - let rlp_block = hex::decode(rlp_block_str.trim_start_matches("0x"))?; - Ok(rlp_block) - } - - /// Fetch the block hashes for the `BLOCK_HASH_LOOKUP_DEPTH` blocks before the current one - /// from the archive node it is currently connected to via IPC - async fn read_block_hashes(&mut self) -> eyre::Result> { - let mut res = Vec::new(); - for offset in 1..BLOCK_HASH_LOOKUP_DEPTH { - let Some(block_number) = self.block_number.checked_sub(offset) else { - break; - }; - let request = &json!({ - "id": 1, - "jsonrpc": "2.0", - "method": "debug_dbAncient", - "params": ["hashes", block_number] - }); - let response = send_ipc_json_request(&mut self.stream, request).await?; - let block_hash: BlockHash = serde_json::from_value(response)?; - res.push((block_number, block_hash)); - } - Ok(res) - } -} - -#[derive(Deserialize, Debug, Serialize, Default)] -struct CheckPoint { - processing: ProcessingCheckpoint, - reading: ReadingCheckpoint, -} - -#[derive(Deserialize, Debug, Serialize, Default)] -struct ProcessingCheckpoint { - current_root: Option, - current_file: Option, -} - -#[derive(Deserialize, Debug, Serialize, Default)] -struct ReadingCheckpoint { - start_hash: Option, - current_file: Option, -} - -/// Loads previous checkpoint (if it exists) -fn load_checkpoint( - checkpoint: &Option, - ipc_input: bool, - file_input: bool, - file_output: bool, - no_sync: bool, -) -> Result, eyre::Error> { - let prev_checkpoint: Option = match checkpoint { - Some(checkpoint_filename) if std::path::Path::new(&checkpoint_filename).exists() => { - Some(serde_json::from_reader(File::open(checkpoint_filename)?)?) - } - _ => None, - }; - // Validate current flags match pre-existing state - if let Some(checkpoint) = &prev_checkpoint { - info!("Checkpoint detected, resuming archive sync"); - debug!("Previous checkpoint: {checkpoint:?}"); - // Check inconsistencies - if ipc_input && checkpoint.reading.start_hash.is_none() - || file_input && checkpoint.reading.current_file.is_none() - { - return Err(eyre::Error::msg("Input type doesn't match checkpoint data")); - } - if file_output && checkpoint.processing.current_file.is_none() { - return Err(eyre::Error::msg( - "Output directory received but no current file found in checkpoint", - )); - } - if !no_sync && checkpoint.processing.current_root.is_none() { - return Err(eyre::Error::msg( - "Checkpoint file doesn't contain currnet root, try running with --no_sync", - )); - } - // Warn and request user approval before resuming a sync process with --no_sync flag - if no_sync && checkpoint.processing.current_root.is_some() { - println!( - "Previous archive sync started a state sync, are you sure you want to continue with --no_sync? Please type `confirm`" - ); - io::stdout().flush()?; - let mut input = String::new(); - io::stdin().read_line(&mut input)?; - if !input.trim().eq_ignore_ascii_case("confirm") { - return Err(eyre::Error::msg("Archive sync cancelled")); - } - } - // Warn and request user approval before resuming a sync process that wrote state to files without --output_dir - if !file_output && checkpoint.processing.current_file.is_some() { - println!( - "Previous archive sync downloaded state to files, are you sure you want to continue without an output_dir? Please type `confirm`" - ); - io::stdout().flush()?; - let mut input = String::new(); - io::stdin().read_line(&mut input)?; - if !input.trim().eq_ignore_ascii_case("confirm") { - return Err(eyre::Error::msg("Archive sync cancelled")); - } - } - } - Ok(prev_checkpoint) -} - -#[derive(Parser)] -#[clap(group = ArgGroup::new("input").required(true).args(&["ipc_path", "input_dir"]).multiple(false))] -struct Args { - #[arg( - required = true, - value_name = "NUMBER", - help = "Block number to sync to" - )] - block_number: BlockNumber, - #[arg( - long = "datadir", - value_name = "DATABASE_DIRECTORY", - default_value = default_datadir().into_os_string(), - help = "Receives the name of the directory where the Database is located.", - long_help = "If the datadir is the word `memory`, ethrex will use the `InMemory Engine`.", - env = "ETHREX_DATADIR" - )] - pub datadir: PathBuf, - #[arg( - long = "ipc_path", - value_name = "IPC_PATH", - help = "Path to the ipc of the archive node." - )] - ipc_path: Option, - #[arg( - long = "input_dir", - value_name = "INPUT_DIRECTORY", - help = "Receives the name of the directory where the State Dump will be read from." - )] - pub input_dir: Option, - #[arg( - long = "output_dir", - value_name = "OUTPUT_DIRECTORY", - help = "Receives the name of the directory where the State Dump will be written to." - )] - pub output_dir: Option, - #[arg( - long = "no_sync", - value_name = "NO_SYNC", - help = "If enabled, the node will not process the incoming state. Only usable if --output_dir is set", - requires = "output_dir" - )] - pub no_sync: bool, - #[arg( - long = "checkpoint", - value_name = "CHECKPOINT_FILENAME", - help = "Receives the name of the file where the checkpoint is/will be located", - long_help = "Receives the name of the file where the checkpoint is/will be located. This checkpoint will be used to resume a previous archive sync process if aborted" - )] - pub checkpoint: Option, -} - -#[tokio::main] -pub async fn main() -> eyre::Result<()> { - let args = Args::parse(); - tracing::subscriber::set_global_default(FmtSubscriber::new()) - .expect("setting default subscriber failed"); - init_datadir(&args.datadir); - let store = open_store(&args.datadir).expect("Failed to open Store"); - archive_sync( - args.ipc_path, - args.block_number, - args.output_dir, - args.input_dir, - args.no_sync, - args.checkpoint, - store, - ) - .await -} diff --git a/tooling/ef_tests/blockchain/.fixtures_url b/tooling/ef_tests/blockchain/.fixtures_url deleted file mode 100644 index ea7c838993b..00000000000 --- a/tooling/ef_tests/blockchain/.fixtures_url +++ /dev/null @@ -1 +0,0 @@ -https://github.com/ethereum/execution-spec-tests/releases/download/v5.3.0/fixtures_develop.tar.gz diff --git a/tooling/ef_tests/blockchain/.fixtures_url_amsterdam b/tooling/ef_tests/blockchain/.fixtures_url_amsterdam deleted file mode 100644 index 2290401371e..00000000000 --- a/tooling/ef_tests/blockchain/.fixtures_url_amsterdam +++ /dev/null @@ -1 +0,0 @@ -https://github.com/ethereum/execution-spec-tests/releases/download/bal%40v5.6.1/fixtures_bal.tar.gz diff --git a/tooling/ef_tests/blockchain/.fixtures_url_zkevm b/tooling/ef_tests/blockchain/.fixtures_url_zkevm deleted file mode 100644 index 7b92a046c93..00000000000 --- a/tooling/ef_tests/blockchain/.fixtures_url_zkevm +++ /dev/null @@ -1 +0,0 @@ -https://github.com/ethereum/execution-spec-tests/releases/download/zkevm%40v0.3.0/fixtures_zkevm.tar.gz diff --git a/tooling/ef_tests/blockchain/Cargo.toml b/tooling/ef_tests/blockchain/Cargo.toml deleted file mode 100644 index 8625171e524..00000000000 --- a/tooling/ef_tests/blockchain/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "ef_tests-blockchain" -version.workspace = true -edition.workspace = true -authors.workspace = true -documentation.workspace = true -license.workspace = true - -[dependencies] -ethrex-blockchain.workspace = true -ethrex-common.workspace = true -ethrex-crypto.workspace = true -ethrex-storage.workspace = true -ethrex-rlp.workspace = true -ethrex-vm.workspace = true -ethrex-prover = { workspace = true, default-features = false } -ethrex-guest-program = { path = "../../../crates/guest-program", default-features = false } - -serde.workspace = true -serde_json.workspace = true -bytes.workspace = true -hex.workspace = true -lazy_static.workspace = true -tokio = { workspace = true, features = ["full"] } -datatest-stable = "0.2.9" -regex = "1.11.1" - -[lib] -path = "./lib.rs" - -[features] -default = ["c-kzg"] -c-kzg = ["ethrex-blockchain/c-kzg"] -sp1 = ["ethrex-guest-program/sp1-build-elf", "ethrex-prover/sp1"] -stateless = [] -l2 = ["ethrex-guest-program/l2", "ethrex-prover/l2"] - -[[test]] -name = "all" -harness = false diff --git a/tooling/ef_tests/blockchain/Makefile b/tooling/ef_tests/blockchain/Makefile deleted file mode 100644 index 214a0fa40ca..00000000000 --- a/tooling/ef_tests/blockchain/Makefile +++ /dev/null @@ -1,79 +0,0 @@ -.PHONY: download-test-vectors clean-vectors test test-levm test-sp1 test-stateless amsterdam-vectors zkevm-vectors - -VECTORS_ROOT := vectors -FIXTURES_FILE := .fixtures_url -SPECTEST_ARTIFACT := tests.tar.gz -SPECTEST_VECTORS_DIR := $(VECTORS_ROOT)/eest -SPECTEST_URL := $(shell cat $(FIXTURES_FILE)) - -LEGACYTEST_ARTIFACT := legacy-tests.tar.gz -LEGACYTEST_VECTORS_DIR := $(VECTORS_ROOT)/legacy -LEGACYTEST_COMMIT := 1f581b8ccdc4c63acf5f2c5c1b155c690c32a8eb -LEGACYTEST_URL := https://github.com/ethereum/legacytests/archive/$(LEGACYTEST_COMMIT).tar.gz -LEGACYTEST_ARCHIVE_ROOT := legacytests-$(LEGACYTEST_COMMIT)/Cancun/BlockchainTests - -AMSTERDAM_FIXTURES_FILE := .fixtures_url_amsterdam -AMSTERDAM_ARTIFACT := amsterdam-tests.tar.gz -AMSTERDAM_URL := $(shell cat $(AMSTERDAM_FIXTURES_FILE)) - -ZKEVM_FIXTURES_FILE := .fixtures_url_zkevm -ZKEVM_ARTIFACT := zkevm-tests.tar.gz -ZKEVM_URL := $(shell cat $(ZKEVM_FIXTURES_FILE)) - -VECTORS_TARGETS := $(SPECTEST_VECTORS_DIR) $(LEGACYTEST_VECTORS_DIR) - -$(SPECTEST_ARTIFACT): $(FIXTURES_FILE) - $(MAKE) clean-vectors - curl -L -o $(SPECTEST_ARTIFACT) $(SPECTEST_URL) - -$(SPECTEST_VECTORS_DIR): $(SPECTEST_ARTIFACT) - rm -rf $(SPECTEST_VECTORS_DIR) - mkdir -p $(SPECTEST_VECTORS_DIR) - tar -xzf $(SPECTEST_ARTIFACT) --strip-components=2 -C $(SPECTEST_VECTORS_DIR) fixtures/blockchain_tests - -$(LEGACYTEST_ARTIFACT): - curl -L -o $(LEGACYTEST_ARTIFACT) $(LEGACYTEST_URL) - -$(LEGACYTEST_VECTORS_DIR): $(LEGACYTEST_ARTIFACT) - rm -rf $(LEGACYTEST_VECTORS_DIR) - mkdir -p $(LEGACYTEST_VECTORS_DIR) - tar -xzf $(LEGACYTEST_ARTIFACT) --strip-components=3 -C $(LEGACYTEST_VECTORS_DIR) \ - $(LEGACYTEST_ARCHIVE_ROOT)/GeneralStateTests \ - $(LEGACYTEST_ARCHIVE_ROOT)/ValidBlocks - -$(AMSTERDAM_ARTIFACT): $(AMSTERDAM_FIXTURES_FILE) - curl -L -o $(AMSTERDAM_ARTIFACT) $(AMSTERDAM_URL) - -amsterdam-vectors: $(AMSTERDAM_ARTIFACT) $(SPECTEST_VECTORS_DIR) - tar -xzf $(AMSTERDAM_ARTIFACT) --strip-components=2 -C $(SPECTEST_VECTORS_DIR) fixtures/blockchain_tests/for_amsterdam - -$(ZKEVM_ARTIFACT): $(ZKEVM_FIXTURES_FILE) - curl -L -o $(ZKEVM_ARTIFACT) $(ZKEVM_URL) - -zkevm-vectors: $(ZKEVM_ARTIFACT) $(SPECTEST_VECTORS_DIR) - tar -xzf $(ZKEVM_ARTIFACT) --strip-components=2 -C $(SPECTEST_VECTORS_DIR) fixtures/blockchain_tests/for_amsterdam/amsterdam/eip8025_optional_proofs - -help: ## 📚 Show help for each of the Makefile recipes - @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' - -download-test-vectors: $(VECTORS_TARGETS) amsterdam-vectors zkevm-vectors ## 📥 Download test vectors - -clean-vectors: ## 🗑️ Clean test vectors - rm -rf $(VECTORS_ROOT) - rm -f $(SPECTEST_ARTIFACT) $(LEGACYTEST_ARTIFACT) $(AMSTERDAM_ARTIFACT) $(ZKEVM_ARTIFACT) - -test-levm: $(VECTORS_TARGETS) amsterdam-vectors zkevm-vectors ## 🧪 Run blockchain tests with LEVM - cargo test --profile release-with-debug - -test-sp1: $(VECTORS_TARGETS) amsterdam-vectors zkevm-vectors - cargo test --profile release-with-debug --features sp1 - -test-stateless: $(VECTORS_TARGETS) amsterdam-vectors zkevm-vectors - cargo test --profile release-with-debug --features stateless - -test-stateless-zkevm: $(VECTORS_TARGETS) amsterdam-vectors zkevm-vectors - cargo test --profile release-with-debug --features stateless -- eip8025_optional_proofs - -test: ## 🧪 Run blockchain tests with LEVM both with state and stateless - $(MAKE) test-levm - $(MAKE) test-stateless diff --git a/tooling/ef_tests/blockchain/README.md b/tooling/ef_tests/blockchain/README.md deleted file mode 100644 index f93597c060b..00000000000 --- a/tooling/ef_tests/blockchain/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Blockchain Tests -The blockchain tests test block validation and the consensus rules of the Ethereum blockchain. Tests are usually run for multiple forks. -Some [docs](https://ethereum.github.io/execution-spec-tests/main/consuming_tests/blockchain_test/). - -## Running the tests - -```bash -make test -``` - -## Running the tests for either levm or revm - -```bash -make test-levm -``` -or -```bash -make test-revm -``` diff --git a/tooling/ef_tests/blockchain/deserialize.rs b/tooling/ef_tests/blockchain/deserialize.rs deleted file mode 100644 index 126752bab22..00000000000 --- a/tooling/ef_tests/blockchain/deserialize.rs +++ /dev/null @@ -1,134 +0,0 @@ -use crate::types::{BlockChainExpectedException, BlockExpectedException}; -use serde::{Deserialize, Deserializer}; - -pub const SENDER_NOT_EOA_REGEX: &str = "Sender account .* shouldn't be a contract"; -pub const PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS_REGEX: &str = - "Priority fee .* is greater than max fee per gas .*"; - -pub fn deserialize_block_expected_exception<'de, D>( - deserializer: D, -) -> Result>, D::Error> -where - D: Deserializer<'de>, -{ - let option: Option = Option::deserialize(deserializer)?; - - if let Some(value) = option { - let exceptions = value - .split('|') - .map(|s| match s.trim() { - "TransactionException.INITCODE_SIZE_EXCEEDED" => { - BlockChainExpectedException::TxtException("Initcode size exceeded".to_string()) - } - "TransactionException.NONCE_IS_MAX" => { - BlockChainExpectedException::TxtException("Nonce is max".to_string()) - } - "TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED" => { - BlockChainExpectedException::TxtException("Blob count exceeded".to_string()) - } - "TransactionException.TYPE_3_TX_ZERO_BLOBS" => { - BlockChainExpectedException::TxtException( - "Type 3 transaction without blobs".to_string(), - ) - } - "TransactionException.TYPE_3_TX_CONTRACT_CREATION" => { - BlockChainExpectedException::RLPException - } - "TransactionException.TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH" => { - BlockChainExpectedException::TxtException( - "Invalid blob versioned hash".to_string(), - ) - } - "TransactionException.INTRINSIC_GAS_TOO_LOW" => { - BlockChainExpectedException::TxtException("Transaction gas limit lower than the minimum gas cost to execute the transaction".to_string()) - } - "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS" => { - BlockChainExpectedException::TxtException( - "Insufficient account funds".to_string(), - ) - } - "TransactionException.SENDER_NOT_EOA" => { - BlockChainExpectedException::TxtException(SENDER_NOT_EOA_REGEX.to_string()) - } - "TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS" => { - BlockChainExpectedException::TxtException( - PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS_REGEX.to_string(), - ) - } - "TransactionException.GAS_ALLOWANCE_EXCEEDED" => { - BlockChainExpectedException::TxtException("Gas allowance exceeded".to_string()) - } - "TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS" => { - BlockChainExpectedException::TxtException( - "Insufficient max fee per gas".to_string(), - ) - } - "TransactionException.RLP_INVALID_VALUE" => { - BlockChainExpectedException::TxtException("RLP invalid value".to_string()) - } - "TransactionException.GASLIMIT_PRICE_PRODUCT_OVERFLOW" => { - BlockChainExpectedException::TxtException( - "Gas limit price product overflow".to_string(), - ) - } - "TransactionException.TYPE_3_TX_PRE_FORK" => { - BlockChainExpectedException::TxtException( - "Type 3 transactions are not supported before the Cancun fork".to_string(), - ) - } - "TransactionException.TYPE_4_TX_CONTRACT_CREATION" => { - BlockChainExpectedException::RLPException - } - "TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS" => { - BlockChainExpectedException::TxtException( - "Insufficient max fee per blob gas".to_string(), - ) - } - "TransactionException.GAS_LIMIT_EXCEEDS_MAXIMUM" => { - BlockChainExpectedException::TxtException( - "Transaction gas limit exceeds maximum.".to_string(), - ) - } - "BlockException.RLP_STRUCTURES_ENCODING" => { - BlockChainExpectedException::RLPException - } - "BlockException.INCORRECT_BLOB_GAS_USED" => { - BlockChainExpectedException::BlockException( - BlockExpectedException::IncorrectBlobGasUsed, - ) - } - "BlockException.BLOB_GAS_USED_ABOVE_LIMIT" => { - BlockChainExpectedException::BlockException( - BlockExpectedException::BlobGasUsedAboveLimit, - ) - } - "BlockException.INCORRECT_EXCESS_BLOB_GAS" => { - BlockChainExpectedException::BlockException( - BlockExpectedException::IncorrectExcessBlobGas, - ) - } - "BlockException.INCORRECT_BLOCK_FORMAT" => { - BlockChainExpectedException::BlockException( - BlockExpectedException::IncorrectBlockFormat, - ) - } - "BlockException.INVALID_REQUESTS" => BlockChainExpectedException::BlockException( - BlockExpectedException::InvalidRequest, - ), - "BlockException.SYSTEM_CONTRACT_CALL_FAILED" => { - BlockChainExpectedException::BlockException( - BlockExpectedException::SystemContractCallFailed, - ) - } - "BlockException.RLP_BLOCK_LIMIT_EXCEEDED" => BlockChainExpectedException::BlockException( - BlockExpectedException::RlpBlockLimitExceeded, - ), - _ => BlockChainExpectedException::Other, - }) - .collect(); - - Ok(Some(exceptions)) - } else { - Ok(None) - } -} diff --git a/tooling/ef_tests/blockchain/fork.rs b/tooling/ef_tests/blockchain/fork.rs deleted file mode 100644 index 99d1b03769b..00000000000 --- a/tooling/ef_tests/blockchain/fork.rs +++ /dev/null @@ -1,178 +0,0 @@ -use ethrex_common::{H160, types::ChainConfig}; -use lazy_static::lazy_static; -use serde::Deserialize; -use std::str::FromStr; - -// Chain config for different forks as defined on https://ethereum.github.io/execution-spec-tests/v3.0.0/consuming_tests/common_types/#fork -lazy_static! { - pub static ref MERGE_CONFIG: ChainConfig = ChainConfig { - chain_id: 1_u64, - homestead_block: Some(0), - dao_fork_block: Some(0), - dao_fork_support: true, - eip150_block: Some(0), - eip155_block: Some(0), - eip158_block: Some(0), - byzantium_block: Some(0), - constantinople_block: Some(0), - petersburg_block: Some(0), - istanbul_block: Some(0), - muir_glacier_block: Some(0), - berlin_block: Some(0), - london_block: Some(0), - arrow_glacier_block: Some(0), - gray_glacier_block: Some(0), - merge_netsplit_block: Some(0), - terminal_total_difficulty: Some(0), - ..Default::default() - }; - pub static ref MERGE_TO_SHANGHAI_AT_15K_CONFIG: ChainConfig = ChainConfig { - shanghai_time: Some(0x3a98), - ..*MERGE_CONFIG - }; - pub static ref SHANGHAI_CONFIG: ChainConfig = ChainConfig { - shanghai_time: Some(0), - ..*MERGE_CONFIG - }; - pub static ref SHANGHAI_TO_CANCUN_AT_15K_CONFIG: ChainConfig = ChainConfig { - cancun_time: Some(0x3a98), - ..*SHANGHAI_CONFIG - }; - pub static ref CANCUN_CONFIG: ChainConfig = ChainConfig { - cancun_time: Some(0), - ..*SHANGHAI_CONFIG - }; - pub static ref CANCUN_TO_PRAGUE_AT_15K_CONFIG: ChainConfig = ChainConfig { - prague_time: Some(0x3a98), - // Mainnet address - deposit_contract_address: H160::from_str("0x00000000219ab540356cbb839cbe05303d7705fa") - .unwrap(), - ..*CANCUN_CONFIG - }; - pub static ref PRAGUE_CONFIG: ChainConfig = ChainConfig { - prague_time: Some(0), - ..*CANCUN_TO_PRAGUE_AT_15K_CONFIG - }; - - pub static ref PRAGUE_TO_OSAKA_AT_15K_CONFIG: ChainConfig = ChainConfig { - osaka_time: Some(0x3a98), - ..*PRAGUE_CONFIG - - }; - - pub static ref OSAKA_CONFIG: ChainConfig = ChainConfig { - osaka_time: Some(0), - ..*PRAGUE_CONFIG - }; - - pub static ref OSAKA_TO_BPO1_AT_15K_CONFIG: ChainConfig = ChainConfig { - bpo1_time: Some(0x3a98), - ..*OSAKA_CONFIG - }; - - pub static ref BPO1_TO_BPO2_AT_15K_CONFIG: ChainConfig = ChainConfig { - bpo1_time: Some(0), - bpo2_time: Some(0x3a98), - ..*OSAKA_CONFIG - }; - - pub static ref BPO2_TO_BPO3_AT_15K_CONFIG: ChainConfig = ChainConfig { - bpo2_time: Some(0), - bpo3_time: Some(0x3a98), - ..*OSAKA_CONFIG - }; - pub static ref BPO3_TO_BPO4_AT_15K_CONFIG: ChainConfig = ChainConfig { - bpo3_time: Some(0), - bpo4_time: Some(0x3a98), - ..*OSAKA_CONFIG - }; - pub static ref BPO4_TO_BPO5_AT_15K_CONFIG: ChainConfig = ChainConfig { - bpo4_time: Some(0), - bpo5_time: Some(0x3a98), - ..*OSAKA_CONFIG - }; - - pub static ref BPO2_CONFIG: ChainConfig = ChainConfig { - bpo1_time: Some(0), - bpo2_time: Some(0), - ..*OSAKA_CONFIG - }; - - pub static ref BPO2_TO_AMSTERDAM_AT_15K_CONFIG: ChainConfig = ChainConfig { - amsterdam_time: Some(0x3a98), - ..*BPO2_CONFIG - }; - - pub static ref AMSTERDAM_CONFIG: ChainConfig = ChainConfig { - amsterdam_time: Some(0), - ..*BPO2_TO_AMSTERDAM_AT_15K_CONFIG - }; - -} - -/// Most of the fork variants are just for parsing the tests -/// It's important for the pre-merge forks to be before Paris because we make a comparison for executing post-merge forks only. -#[derive(Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord)] -pub enum Fork { - Constantinople, - EIP150, - EIP158, - EIP158ToByzantiumAt5, - ArrowGlacierToParisAtDiffC0000, - BerlinToLondonAt5, - ByzantiumToConstantinopleFixAt5, - FrontierToHomesteadAt5, - HomesteadToDaoAt5, - HomesteadToEIP150At5, - Frontier, - Homestead, - ConstantinopleFix, - Istanbul, - Byzantium, - London, - Berlin, - #[serde(alias = "Paris")] - Merge, - #[serde(alias = "ParisToShanghaiAtTime15k")] - MergeToShanghaiAtTime15k, - Shanghai, - ShanghaiToCancunAtTime15k, - Cancun, - CancunToPragueAtTime15k, - Prague, - PragueToOsakaAtTime15k, - Osaka, - OsakaToBPO1AtTime15k, - BPO1ToBPO2AtTime15k, - BPO2ToBPO3AtTime15k, - BPO3ToBPO4AtTime15k, - BPO4ToBPO5AtTime15k, - BPO2ToAmsterdamAtTime15k, - Amsterdam, -} - -impl Fork { - pub fn chain_config(&self) -> &ChainConfig { - match self { - Fork::Merge => &MERGE_CONFIG, - Fork::MergeToShanghaiAtTime15k => &MERGE_TO_SHANGHAI_AT_15K_CONFIG, - Fork::Shanghai => &SHANGHAI_CONFIG, - Fork::ShanghaiToCancunAtTime15k => &SHANGHAI_TO_CANCUN_AT_15K_CONFIG, - Fork::Cancun => &CANCUN_CONFIG, - Fork::CancunToPragueAtTime15k => &CANCUN_TO_PRAGUE_AT_15K_CONFIG, - Fork::Prague => &PRAGUE_CONFIG, - Fork::PragueToOsakaAtTime15k => &PRAGUE_TO_OSAKA_AT_15K_CONFIG, - Fork::Osaka => &OSAKA_CONFIG, - Fork::OsakaToBPO1AtTime15k => &OSAKA_TO_BPO1_AT_15K_CONFIG, - Fork::BPO1ToBPO2AtTime15k => &BPO1_TO_BPO2_AT_15K_CONFIG, - Fork::BPO2ToBPO3AtTime15k => &BPO2_TO_BPO3_AT_15K_CONFIG, - Fork::BPO3ToBPO4AtTime15k => &BPO3_TO_BPO4_AT_15K_CONFIG, - Fork::BPO4ToBPO5AtTime15k => &BPO4_TO_BPO5_AT_15K_CONFIG, - Fork::BPO2ToAmsterdamAtTime15k => &BPO2_TO_AMSTERDAM_AT_15K_CONFIG, - Fork::Amsterdam => &AMSTERDAM_CONFIG, - _ => { - panic!("Ethrex doesn't support pre-Merge forks: {self:?}") - } - } - } -} diff --git a/tooling/ef_tests/blockchain/lib.rs b/tooling/ef_tests/blockchain/lib.rs deleted file mode 100644 index e65671eb0a0..00000000000 --- a/tooling/ef_tests/blockchain/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod deserialize; -pub mod fork; -pub mod test_runner; -pub mod types; diff --git a/tooling/ef_tests/blockchain/test_runner.rs b/tooling/ef_tests/blockchain/test_runner.rs deleted file mode 100644 index 85fac6dd9b7..00000000000 --- a/tooling/ef_tests/blockchain/test_runner.rs +++ /dev/null @@ -1,582 +0,0 @@ -use std::{collections::HashMap, path::Path}; - -use crate::{ - fork::Fork, - types::{BlockChainExpectedException, BlockExpectedException, BlockWithRLP, TestUnit}, -}; -use ethrex_blockchain::{ - Blockchain, BlockchainOptions, - error::{ChainError, InvalidBlockError}, - fork_choice::apply_fork_choice, -}; -#[cfg(feature = "stateless")] -use ethrex_common::types::block_execution_witness::RpcExecutionWitness; -use ethrex_common::{ - constants::EMPTY_KECCACK_HASH, - types::{ - Account as CoreAccount, Block as CoreBlock, BlockHeader as CoreBlockHeader, - InvalidBlockHeaderError, block_access_list::BlockAccessList, - }, -}; -use ethrex_guest_program::input::ProgramInput; -#[cfg(feature = "sp1")] -use ethrex_prover::Sp1Backend; -use ethrex_prover::{BackendType, ExecBackend, ProverBackend}; -use ethrex_rlp::decode::RLPDecode; -use ethrex_storage::{EngineType, Store}; -use ethrex_vm::EvmError; -use regex::Regex; - -pub fn parse_and_execute( - path: &Path, - skipped_tests: Option<&[&str]>, - stateless_backend: Option, -) -> datatest_stable::Result<()> { - let rt = tokio::runtime::Runtime::new().unwrap(); - let tests = parse_tests(path); - - let mut failures = Vec::new(); - - for (test_key, test) in tests { - let should_skip_test = test.network < Fork::Merge - || skipped_tests - .map(|skipped| skipped.iter().any(|s| test_key.contains(s))) - .unwrap_or(false); - - if should_skip_test { - continue; - } - - let result = rt.block_on(run_ef_test(&test_key, &test, stateless_backend)); - - if let Err(e) = result { - eprintln!("Test {test_key} failed: {e:?}"); - failures.push(format!("{test_key}: {e:?}")); - } - } - - if failures.is_empty() { - Ok(()) - } else { - // \n doesn't print new lines on terminal, so this alternative is for making it readable - Err(failures.join(" ------- ").into()) - } -} - -pub async fn run_ef_test( - test_key: &str, - test: &TestUnit, - stateless_backend: Option, -) -> Result<(), String> { - // check that the decoded genesis block header matches the deserialized one - let genesis_rlp = test.genesis_rlp.clone(); - let decoded_block = match CoreBlock::decode(&genesis_rlp) { - Ok(block) => block, - Err(e) => return Err(format!("Failed to decode genesis RLP: {e}")), - }; - let genesis_block_header = CoreBlockHeader::from(test.genesis_block_header.clone()); - if decoded_block.header != genesis_block_header { - return Err("Decoded genesis header does not match expected header".to_string()); - } - - let store = build_store_for_test(test).await; - - // Check world_state - check_prestate_against_db(test_key, test, &store); - - // Blockchain EF tests are meant for L1. - let blockchain = Blockchain::new(store.clone(), BlockchainOptions::default()); - - // Early return if the exception is in the rlp decoding of the block - for bf in &test.blocks { - if bf.expect_exception.is_some() && exception_in_rlp_decoding(bf) { - return Ok(()); - } - } - - run(test_key, test, &blockchain, &store).await?; - - // For Amsterdam tests, exercise the parallel BAL execution path as a correctness check. - // Two-pass approach: pass 1 collects the BAL produced by sequential execution, pass 2 - // re-executes using that BAL to drive parallel (BAL-warmed) execution and verifies the - // same final state is reached. - if test.network == Fork::Amsterdam { - run_two_pass_parallel(test_key, test).await?; - } - - // Run stateless if backend was specified for this. - // TODO: See if we can run stateless without needing a previous run. We can't easily do it for now. #4142 - if let Some(backend) = stateless_backend { - // If the fixture provides an executionWitness (zkevm format), use it directly - // instead of regenerating the witness from blockchain execution. - #[cfg(feature = "stateless")] - { - let has_fixture_witness = test.blocks.iter().any(|bf| { - bf.block() - .and_then(|b| b.execution_witness.as_ref()) - .is_some() - }); - if has_fixture_witness { - run_stateless_from_fixture(test, test_key, backend).await?; - return Ok(()); - } - } - re_run_stateless(blockchain, test, test_key, backend).await?; - }; - - Ok(()) -} - -// Helper: run the EF test blocks and verify poststate -async fn run( - test_key: &str, - test: &TestUnit, - blockchain: &Blockchain, - store: &Store, -) -> Result<(), String> { - // Execute all blocks in test - for block_fixture in test.blocks.iter() { - let expects_exception = block_fixture.expect_exception.is_some(); - - // Won't panic because test has been validated - let block: CoreBlock = block_fixture.block().unwrap().clone().into(); - let hash = block.hash(); - - // Attempt to add the block as the head of the chain - let chain_result = blockchain.add_block_pipeline(block.clone(), None); - - match chain_result { - Err(error) => { - if !expects_exception { - return Err(format!( - "Transaction execution unexpectedly failed on test: {test_key}, with error {error:?}", - )); - } - let expected_exception = block_fixture.expect_exception.clone().unwrap(); - if !exception_is_expected(expected_exception.clone(), &error) { - eprintln!( - "Warning: Returned exception {error:?} does not match expected {expected_exception:?}", - ); - } - // Expected exception matched — stop processing further blocks of this test. - break; - } - Ok(_) => { - if expects_exception { - return Err(format!( - "Expected transaction execution to fail in test: {test_key} with error: {:?}", - block_fixture.expect_exception.clone() - )); - } - // Advance fork choice to the new head - apply_fork_choice(store, hash, hash, hash).await.unwrap(); - } - } - } - - // Final post-state verification - check_poststate_against_db(test_key, test, store).await; - Ok(()) -} - -/// Two-pass parallel execution check for Amsterdam tests. -/// -/// Pass 1 (sequential): runs every block with `add_block_pipeline_bal` to collect the -/// BAL that each block produces. Pass 2 (parallel): creates a fresh chain and re-runs every -/// block passing the corresponding BAL so the BAL-warmed parallel path is exercised. The final -/// post-state of pass 2 must match the expected post-state. -async fn run_two_pass_parallel(test_key: &str, test: &TestUnit) -> Result<(), String> { - // ---- Pass 1: sequential, collect BALs ---- - let store1 = build_store_for_test(test).await; - let blockchain1 = Blockchain::new(store1.clone(), BlockchainOptions::default()); - - let mut bals: Vec = Vec::with_capacity(test.blocks.len()); - - for block_fixture in test.blocks.iter() { - // Skip fixtures that expect an exception — the normal run() already verified them. - if block_fixture.expect_exception.is_some() { - return Ok(()); - } - - let block: CoreBlock = block_fixture.block().unwrap().clone().into(); - let hash = block.hash(); - - let produced_bal = blockchain1 - .add_block_pipeline_bal(block, None) - .map_err(|e| format!("Two-pass pass-1 failed for test {test_key}: {e:?}"))?; - - apply_fork_choice(&store1, hash, hash, hash) - .await - .map_err(|e| { - format!("Two-pass pass-1 fork choice failed for test {test_key}: {e:?}") - })?; - - // If execution produced no BAL (non-Amsterdam block in a transition test), skip pass 2. - match produced_bal { - Some(bal) => bals.push(bal), - None => return Ok(()), - } - } - - // ---- Pass 2: parallel (BAL-driven), verify post-state ---- - let store2 = build_store_for_test(test).await; - let blockchain2 = Blockchain::new(store2.clone(), BlockchainOptions::default()); - - for (block_fixture, bal) in test.blocks.iter().zip(bals.iter()) { - let block: CoreBlock = block_fixture.block().unwrap().clone().into(); - let hash = block.hash(); - - blockchain2 - .add_block_pipeline(block, Some(bal)) - .map_err(|e| format!("Two-pass pass-2 (parallel) failed for test {test_key}: {e:?}"))?; - - apply_fork_choice(&store2, hash, hash, hash) - .await - .map_err(|e| { - format!("Two-pass pass-2 fork choice failed for test {test_key}: {e:?}") - })?; - } - - // Verify post-state matches expected - check_poststate_against_db(test_key, test, &store2).await; - Ok(()) -} - -fn exception_is_expected( - expected_exceptions: Vec, - returned_error: &ChainError, -) -> bool { - expected_exceptions.iter().any(|exception| { - if let ( - BlockChainExpectedException::TxtException(expected_error_msg), - ChainError::EvmError(EvmError::Transaction(error_msg)) - | ChainError::InvalidBlock(InvalidBlockError::InvalidTransaction(error_msg)), - ) = (exception, returned_error) - { - return (expected_error_msg.to_lowercase() == error_msg.to_lowercase()) - || match_expected_regex(expected_error_msg, error_msg); - } - matches!( - (exception, &returned_error), - ( - BlockChainExpectedException::BlockException( - BlockExpectedException::IncorrectBlobGasUsed - ), - ChainError::InvalidBlock(InvalidBlockError::BlobGasUsedMismatch) - ) | ( - BlockChainExpectedException::BlockException( - BlockExpectedException::BlobGasUsedAboveLimit - ), - ChainError::InvalidBlock(InvalidBlockError::InvalidHeader( - InvalidBlockHeaderError::GasUsedGreaterThanGasLimit - )) - ) | ( - BlockChainExpectedException::BlockException( - BlockExpectedException::IncorrectExcessBlobGas - ), - ChainError::InvalidBlock(InvalidBlockError::InvalidHeader( - InvalidBlockHeaderError::ExcessBlobGasIncorrect - )) - ) | ( - BlockChainExpectedException::BlockException( - BlockExpectedException::IncorrectBlockFormat - ), - ChainError::InvalidBlock(_) - ) | ( - BlockChainExpectedException::BlockException(BlockExpectedException::InvalidRequest), - ChainError::InvalidBlock(InvalidBlockError::RequestsHashMismatch) - ) | ( - BlockChainExpectedException::BlockException( - BlockExpectedException::SystemContractCallFailed - ), - ChainError::EvmError(EvmError::SystemContractCallFailed(_)) - ) | ( - BlockChainExpectedException::BlockException( - BlockExpectedException::RlpBlockLimitExceeded - ), - ChainError::InvalidBlock(InvalidBlockError::MaximumRlpSizeExceeded(_, _)) - ) | ( - BlockChainExpectedException::Other, - _ //TODO: Decide whether to support more specific errors. - ), - ) - }) -} - -fn match_expected_regex(expected_error_regex: &str, error_msg: &str) -> bool { - let Ok(regex) = Regex::new(expected_error_regex) else { - return false; - }; - regex.is_match(error_msg) -} - -/// Tests the rlp decoding of a block -fn exception_in_rlp_decoding(block_fixture: &BlockWithRLP) -> bool { - // NOTE: There is a test which validates that an EIP-7702 transaction is not allowed to - // have the "to" field set to null (create). - // This test expects an exception to be thrown AFTER the Block RLP decoding, when the - // transaction is validated. This would imply allowing the "to" field of the - // EIP-7702 transaction to be null and validating it on the `prepare_execution` LEVM hook. - // - // Instead, this approach is taken, which allows for the exception to be thrown on - // RLPDecoding, so the data type EIP7702Transaction correctly describes the requirement of - // "to" field to be an Address - // For more information, please read: - // - https://eips.ethereum.org/EIPS/eip-7702 - // - https://github.com/lambdaclass/ethrex/pull/2425 - // - // There is another test which validates the same exact thing, but for an EIP-4844 tx. - // That test also allows for a "BlockException.RLP_..." error to happen, and that's what is being - // caught. - - // Decoding_exception_cases = [ - // "BlockException.RLP_", - // "TransactionException.TYPE_4_TX_CONTRACT_CREATION", ]; - - let expects_rlp_exception = block_fixture - .expect_exception - .as_ref() - .unwrap_or(&Vec::new()) - .iter() - .any(|case| matches!(case, BlockChainExpectedException::RLPException)); - - match CoreBlock::decode(block_fixture.rlp.as_ref()) { - Ok(_) => { - assert!(!expects_rlp_exception); - false - } - Err(_) => { - assert!(expects_rlp_exception); - true - } - } -} - -pub fn parse_tests(path: &Path) -> HashMap { - let mut all_tests = HashMap::new(); - - if path.is_file() { - let file_tests = parse_json_file(path); - all_tests.extend(file_tests); - } else if path.is_dir() { - for entry in std::fs::read_dir(path).expect("Failed to read directory") { - let entry = entry.expect("Failed to get DirEntry"); - let path = entry.path(); - if path.is_dir() { - let sub_tests = parse_tests(&path); // recursion - all_tests.extend(sub_tests); - } else if path.extension().and_then(|s| s.to_str()) == Some("json") { - let file_tests = parse_json_file(&path); - all_tests.extend(file_tests); - } - } - } else { - panic!("Invalid path: not a file or directory"); - } - - all_tests -} - -fn parse_json_file(path: &Path) -> HashMap { - let s = std::fs::read_to_string(path).expect("Unable to read file"); - serde_json::from_str(&s).expect("Unable to parse JSON") -} - -/// Creates a new in-memory store and adds the genesis state. -pub async fn build_store_for_test(test: &TestUnit) -> Store { - let mut store = - Store::new("store.db", EngineType::InMemory).expect("Failed to build DB for testing"); - let genesis = test.get_genesis(); - store - .add_initial_state(genesis) - .await - .expect("Failed to add genesis state"); - store -} - -/// Checks db is correct after setting up initial state -/// Panics if any comparison fails -fn check_prestate_against_db(test_key: &str, test: &TestUnit, db: &Store) { - let block_number = test.genesis_block_header.number.low_u64(); - let db_block_header = db.get_block_header(block_number).unwrap().unwrap(); - let computed_genesis_block_hash = db_block_header.hash(); - // Check genesis block hash - assert_eq!(test.genesis_block_header.hash, computed_genesis_block_hash); - // Check genesis state root - let test_state_root = test.genesis_block_header.state_root; - assert_eq!( - test_state_root, db_block_header.state_root, - "Mismatched genesis state root for database, test: {test_key}" - ); - assert!(db.has_state_root(test_state_root).unwrap()); -} - -/// Checks that all accounts in the post-state are present and have the correct values in the DB -/// Panics if any comparison fails -/// Tests that previously failed the validation stage shouldn't be executed with this function. -async fn check_poststate_against_db(test_key: &str, test: &TestUnit, db: &Store) { - let latest_block_number = db.get_latest_block_number().await.unwrap(); - if let Some(post_state) = &test.post_state { - for (addr, account) in post_state { - let expected_account: CoreAccount = account.clone().into(); - // Check info - let db_account_info = db - .get_account_info(latest_block_number, *addr) - .await - .expect("Failed to read from DB") - .unwrap_or_else(|| { - panic!("Account info for address {addr} not found in DB, test:{test_key}") - }); - assert_eq!( - db_account_info, expected_account.info, - "Mismatched account info for address {addr} test:{test_key}" - ); - // Check code - let code_hash = expected_account.info.code_hash; - if code_hash != *EMPTY_KECCACK_HASH { - // We don't want to get account code if there's no code. - let db_account_code = db - .get_account_code(code_hash) - .expect("Failed to read from DB") - .unwrap_or_else(|| { - panic!( - "Account code for code hash {code_hash} not found in DB test:{test_key}" - ) - }); - assert_eq!( - db_account_code, expected_account.code, - "Mismatched account code for code hash {code_hash} test:{test_key}" - ); - } - // Check storage - for (key, value) in expected_account.storage { - let db_storage_value = db - .get_storage_at(latest_block_number, *addr, key) - .expect("Failed to read from DB") - .unwrap_or_else(|| { - panic!("Storage missing for address {addr} key {key} in DB test:{test_key}") - }); - assert_eq!( - db_storage_value, value, - "Mismatched storage value for address {addr}, key {key} test:{test_key}" - ); - } - } - } - // Check lastblockhash is in store - let last_block_number = db.get_latest_block_number().await.unwrap(); - let last_block_header = db.get_block_header(last_block_number).unwrap().unwrap(); - let last_block_hash = last_block_header.hash(); - assert_eq!( - test.lastblockhash, last_block_hash, - "Last block number does not match" - ); - - // State root was already validated by `add_block`. -} - -async fn re_run_stateless( - blockchain: Blockchain, - test: &TestUnit, - test_key: &str, - backend_type: BackendType, -) -> Result<(), String> { - let blocks = test - .blocks - .iter() - .map(|block_fixture| block_fixture.block().unwrap().clone().into()) - .collect::>(); - - let test_should_fail = test.blocks.iter().any(|t| t.expect_exception.is_some()); - - let witness = blockchain.generate_witness_for_blocks(&blocks).await; - if test_should_fail { - // The normal run() already verified this test fails correctly. - // The stateless prover proves valid block execution, not invalid block rejection. - return Ok(()); - } else if let Err(err) = witness { - return Err(format!( - "Failed to create witness for a test that should not fail: {err}" - )); - } - // At this point witness is guaranteed to be Ok - let execution_witness = witness.unwrap(); - - let program_input = ProgramInput::new(blocks, execution_witness); - - let execute_result = match backend_type { - BackendType::Exec => ExecBackend::new().execute(program_input), - #[cfg(feature = "sp1")] - BackendType::SP1 => Sp1Backend::new().execute(program_input), - }; - - if let Err(e) = execute_result { - if !test_should_fail { - return Err(format!( - "Expected test: {test_key} to succeed but failed with {e}" - )); - } - } else if test_should_fail { - return Err(format!("Expected test: {test_key} to fail but succeeded")); - } - Ok(()) -} - -/// Run stateless execution using the execution witness provided directly in the -/// zkevm fixture, instead of generating one from blockchain execution. -/// -/// Each block in the fixture has its own `executionWitness` containing the state -/// trie nodes, codes, and ancestor headers needed for that specific block. -/// Following the spec, we execute each block -/// independently with its own witness. -#[cfg(feature = "stateless")] -async fn run_stateless_from_fixture( - test: &TestUnit, - test_key: &str, - backend_type: BackendType, -) -> Result<(), String> { - let chain_config = test.network.chain_config(); - - for block_fixture in test.blocks.iter() { - // Skip blocks that expect exceptions — those are already validated by the normal path. - if block_fixture.expect_exception.is_some() { - continue; - } - - let Some(block_data) = block_fixture.block() else { - continue; - }; - - let Some(witness_json) = block_data.execution_witness.as_ref() else { - continue; - }; - - let block: CoreBlock = block_data.clone().into(); - let block_number = block.header.number; - - let rpc_witness: RpcExecutionWitness = serde_json::from_value(witness_json.clone()) - .map_err(|e| { - format!("Failed to parse executionWitness for block {block_number}: {e}") - })?; - - let execution_witness = rpc_witness - .into_execution_witness(*chain_config, block_number) - .map_err(|e| format!("Witness conversion failed for block {block_number}: {e}"))?; - - let program_input = ProgramInput::new(vec![block], execution_witness); - - let execute_result = match backend_type { - BackendType::Exec => ExecBackend::new().execute(program_input), - #[cfg(feature = "sp1")] - BackendType::SP1 => Sp1Backend::new().execute(program_input), - }; - - if let Err(e) = execute_result { - return Err(format!( - "Stateless execution from fixture failed for {test_key} block {block_number}: {e}" - )); - } - } - - Ok(()) -} diff --git a/tooling/ef_tests/blockchain/tests/all.rs b/tooling/ef_tests/blockchain/tests/all.rs deleted file mode 100644 index f2443a3c919..00000000000 --- a/tooling/ef_tests/blockchain/tests/all.rs +++ /dev/null @@ -1,53 +0,0 @@ -use ef_tests_blockchain::test_runner::parse_and_execute; -use ethrex_prover::backend::BackendType; -use std::path::Path; - -// Enable only one of `sp1` or `stateless` at a time. -#[cfg(all(feature = "sp1", feature = "stateless"))] -compile_error!("Only one of `sp1` and `stateless` can be enabled at a time."); - -const TEST_FOLDER: &str = "vectors/"; - -// Base skips shared by all runs. -const SKIPPED_BASE: &[&str] = &[ - // Skip because they take too long to run, but they pass - "static_Call50000_sha256", - "CALLBlake2f_MaxRounds", - "loopMul", - // Skip because it tries to deserialize number > U256::MAX - "ValueOverflowParis", - // Skip because it's a "Create" Blob Transaction, which doesn't actually exist. It never reaches the EVM because we can't even parse it as an actual Transaction. - "createBlobhashTx", -]; - -// Extra skips added only for prover backends. -#[cfg(feature = "sp1")] -const EXTRA_SKIPS: &[&str] = &[ - // I believe these tests fail because of how much stress they put into the zkVM, they probably cause an OOM though this should be checked - "static_Call50000", - "Return50000", - "static_Call1MB1024Calldepth", -]; -#[cfg(not(feature = "sp1"))] -const EXTRA_SKIPS: &[&str] = &[]; - -// Select backend -#[cfg(feature = "stateless")] -const BACKEND: Option = Some(BackendType::Exec); -#[cfg(feature = "sp1")] -const BACKEND: Option = Some(BackendType::SP1); -#[cfg(not(any(feature = "sp1", feature = "stateless")))] -const BACKEND: Option = None; - -fn blockchain_runner(path: &Path) -> datatest_stable::Result<()> { - // Compose the final skip list - let skips: Vec<&'static str> = SKIPPED_BASE - .iter() - .copied() - .chain(EXTRA_SKIPS.iter().copied()) - .collect(); - - parse_and_execute(path, Some(&skips), BACKEND) -} - -datatest_stable::harness!(blockchain_runner, TEST_FOLDER, r".*"); diff --git a/tooling/ef_tests/blockchain/types.rs b/tooling/ef_tests/blockchain/types.rs deleted file mode 100644 index eacf14c0db9..00000000000 --- a/tooling/ef_tests/blockchain/types.rs +++ /dev/null @@ -1,669 +0,0 @@ -use bytes::Bytes; -use ethrex_common::types::{ - Account as ethrexAccount, AccountInfo, Block as CoreBlock, BlockBody, Code, EIP1559Transaction, - EIP2930Transaction, EIP4844Transaction, EIP7702Transaction, LegacyTransaction, - Transaction as ethrexTransaction, TxKind, code_hash, -}; -use ethrex_common::types::{Genesis, GenesisAccount, Withdrawal}; -use ethrex_common::{Address, Bloom, H64, H256, U256, types::BlockHeader}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -use crate::deserialize::deserialize_block_expected_exception; -use crate::fork::Fork; - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct TestUnit { - #[serde(default, rename = "_info")] - pub info: Info, - pub blocks: Vec, - pub genesis_block_header: Header, - #[serde(rename = "genesisRLP", with = "ethrex_common::serde_utils::bytes")] - pub genesis_rlp: Bytes, - pub lastblockhash: H256, - pub network: Fork, - pub post_state: Option>, - pub post_state_hash: Option, - pub pre: HashMap, - pub seal_engine: serde_json::Value, - pub config: Option, -} - -/// General information about the test. Matches the `_info` field in the `.json` file. -#[derive(Debug, Deserialize, Clone, Default)] -pub struct Info { - #[serde(default)] - pub comment: Option, - #[serde(rename = "filling-rpc-server", default)] - pub filling_rpc_server: Option, - #[serde(rename = "filling-tool-version", default)] - pub filling_tool_version: Option, - #[serde(rename = "generatedTestHash", default)] - pub generated_test_hash: Option, - #[serde(default)] - pub labels: Option>, - #[serde(default)] - pub lllcversion: Option, - #[serde(default)] - pub solidity: Option, - #[serde(default)] - pub source: Option, - #[serde(rename = "sourceHash", default)] - pub source_hash: Option, - // These fields are implemented in the new version of the test vectors (Prague). - #[serde(rename = "hash", default)] - pub hash: Option, - #[serde(rename = "filling-transition-tool", default)] - pub filling_transition_tool: Option, - #[serde(default)] - pub description: Option, - #[serde(default)] - pub url: Option, - #[serde(rename = "fixture_format", default)] - pub fixture_format: Option, - #[serde(rename = "reference-spec", default)] - pub reference_spec: Option, - #[serde(rename = "reference-spec-version", default)] - pub reference_spec_version: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct FixtureConfig { - pub blob_schedule: Option, -} - -#[derive(Debug, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct ForkBlobSchedule { - #[serde(default, with = "ethrex_common::serde_utils::u32::hex_str")] - pub target: u32, - #[serde(default, with = "ethrex_common::serde_utils::u32::hex_str")] - pub max: u32, - #[serde(default, with = "ethrex_common::serde_utils::u64::hex_str")] - pub base_fee_update_fraction: u64, -} - -#[derive(Debug, Deserialize, Clone)] -#[serde(rename_all = "PascalCase")] -pub struct BlobSchedule { - pub cancun: Option, - pub prague: Option, - pub osaka: Option, - #[serde(rename = "BPO1")] - pub bpo1: Option, - #[serde(rename = "BPO2")] - pub bpo2: Option, - #[serde(rename = "BPO3")] - pub bpo3: Option, - #[serde(rename = "BPO4")] - pub bpo4: Option, - #[serde(rename = "BPO5")] - pub bpo5: Option, - pub amsterdam: Option, -} - -impl From for ethrex_common::types::BlobSchedule { - fn from(val: BlobSchedule) -> Self { - let mut blob_schedule = ethrex_common::types::BlobSchedule::default(); - if let Some(cancun_schedule) = val.cancun { - blob_schedule.cancun = cancun_schedule.into() - } - if let Some(prague_schedule) = val.prague { - blob_schedule.prague = prague_schedule.into() - } - if let Some(osaka_schedule) = val.osaka { - blob_schedule.osaka = osaka_schedule.into() - } - if let Some(bpo1_schedule) = val.bpo1 { - blob_schedule.bpo1 = bpo1_schedule.into() - } - if let Some(bpo2_schedule) = val.bpo2 { - blob_schedule.bpo2 = bpo2_schedule.into() - } - if let Some(bpo3_schedule) = val.bpo3 { - blob_schedule.bpo3 = Some(bpo3_schedule.into()) - } - if let Some(bpo4_schedule) = val.bpo4 { - blob_schedule.bpo4 = Some(bpo4_schedule.into()) - } - if let Some(bpo5_schedule) = val.bpo5 { - blob_schedule.bpo5 = Some(bpo5_schedule.into()) - } - if let Some(amsterdam_schedule) = val.amsterdam { - blob_schedule.amsterdam = Some(amsterdam_schedule.into()) - } - blob_schedule - } -} - -impl From for ethrex_common::types::ForkBlobSchedule { - fn from(val: ForkBlobSchedule) -> Self { - ethrex_common::types::ForkBlobSchedule { - target: val.target, - max: val.max, - base_fee_update_fraction: val.base_fee_update_fraction, - } - } -} - -impl TestUnit { - /// Checks whether a test has a block where the inner_block is none. - /// These tests only check for failures in decoding invalid rlp and expect an exception. - pub fn is_rlp_only_test(&self) -> bool { - let mut is_rlp_only = false; - for block in self.blocks.iter() { - is_rlp_only = is_rlp_only || block.block().is_none(); - } - is_rlp_only - } - - pub fn get_genesis(&self) -> Genesis { - let mut config = *self.network.chain_config(); - // Overwrite default blob schedule with test's blob schedule - if let Some(test_config) = &self.config - && let Some(ref schedule) = test_config.blob_schedule - { - config.blob_schedule = schedule.clone().into(); - } - Genesis { - config, - alloc: self - .pre - .clone() - .into_iter() - .map(|(key, val)| (key, val.into())) - .collect(), - coinbase: self.genesis_block_header.coinbase, - difficulty: self.genesis_block_header.difficulty, - extra_data: self.genesis_block_header.extra_data.clone(), - gas_limit: self.genesis_block_header.gas_limit.try_into().unwrap(), - nonce: self.genesis_block_header.nonce.to_low_u64_be(), - mix_hash: self.genesis_block_header.mix_hash, - timestamp: self.genesis_block_header.timestamp.try_into().unwrap(), - base_fee_per_gas: self - .genesis_block_header - .base_fee_per_gas - .map(|v| v.try_into().unwrap()), - blob_gas_used: self - .genesis_block_header - .blob_gas_used - .map(|v| v.try_into().unwrap()), - excess_blob_gas: self - .genesis_block_header - .excess_blob_gas - .map(|v| v.try_into().unwrap()), - requests_hash: self.genesis_block_header.requests_hash, - block_access_list_hash: self.genesis_block_header.block_access_list_hash, - slot_number: self - .genesis_block_header - .slot_number - .map(|v| v.try_into().unwrap()), - } - } -} - -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] -pub struct Account { - pub balance: U256, - #[serde(with = "ethrex_common::serde_utils::bytes")] - pub code: Bytes, - pub nonce: U256, - pub storage: HashMap, -} - -#[derive(Debug, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct Env { - pub current_coinbase: Address, - pub current_difficulty: U256, - pub current_gas_limit: U256, - pub current_number: U256, - pub current_timestamp: U256, - pub current_base_fee: Option, - pub previous_hash: Option, - pub current_random: Option, - pub current_beacon_root: Option, - pub current_withdrawals_root: Option, - pub parent_blob_gas_used: Option, - pub parent_excess_blob_gas: Option, - pub current_excess_blob_gas: Option, -} - -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase", deny_unknown_fields)] -pub struct AccessListItem { - pub address: Address, - pub storage_keys: Vec, -} -pub type AccessList = Vec; - -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct AuthorizationListItem { - pub chain_id: U256, - pub address: Address, - pub nonce: U256, - pub v: U256, - pub r: U256, - pub s: U256, - pub signer: Option
, -} -pub type AuthorizationList = Vec; - -#[derive(Debug, PartialEq, Eq, Clone, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct Header { - pub bloom: Bloom, - pub coinbase: Address, - pub difficulty: U256, - #[serde(with = "ethrex_common::serde_utils::bytes")] - pub extra_data: Bytes, - pub gas_limit: U256, - pub gas_used: U256, - pub hash: H256, - pub mix_hash: H256, - pub nonce: H64, - pub number: U256, - pub parent_hash: H256, - pub receipt_trie: H256, - pub state_root: H256, - pub timestamp: U256, - pub transactions_trie: H256, - pub uncle_hash: H256, - pub base_fee_per_gas: Option, - pub withdrawals_root: Option, - pub blob_gas_used: Option, - pub excess_blob_gas: Option, - pub parent_beacon_block_root: Option, - pub requests_hash: Option, - // Amsterdam fork fields (EIP-7928) - pub block_access_list_hash: Option, - pub slot_number: Option, -} - -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct BlockWithRLP { - #[serde(with = "ethrex_common::serde_utils::bytes")] - pub rlp: Bytes, - #[serde(flatten)] - inner: Option, - #[serde( - rename = "expectException", - default, - deserialize_with = "deserialize_block_expected_exception" - )] - pub expect_exception: Option>, -} - -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(untagged)] -pub enum BlockInner { - Block(Block), - DecodedRLP(DecodedRLPBlock), -} - -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -pub struct DecodedRLPBlock { - rlp_decoded: Block, -} - -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct Block { - pub block_header: Header, - #[serde(default)] - pub transactions: Vec, - #[serde(default)] - pub uncle_headers: Vec
, - pub withdrawals: Option>, - /// Execution witness from zkevm fixtures (standard format: state/codes/headers). - #[serde(default, rename = "executionWitness")] - pub execution_witness: Option, - /// Reference-encoded stateless input bytes from zkevm fixtures. - #[serde(default, rename = "statelessInputBytes")] - pub stateless_input_bytes: Option, - /// Expected stateless output bytes from zkevm fixtures. - #[serde(default, rename = "statelessOutputBytes")] - pub stateless_output_bytes: Option, - /// Block access list from zkevm fixtures. - #[serde(default, rename = "blockAccessList")] - pub block_access_list_data: Option, - /// Transaction receipts from zkevm fixtures. - #[serde(default, rename = "receipts")] - pub receipts: Option, -} - -impl BlockWithRLP { - pub fn block(&self) -> Option<&Block> { - match self.inner { - Some(BlockInner::Block(ref block)) => Some(block), - Some(BlockInner::DecodedRLP(ref decoded)) => Some(&decoded.rlp_decoded), - None => None, - } - } -} -impl From for CoreBlock { - fn from(val: Block) -> Self { - CoreBlock::new( - val.block_header.into(), - BlockBody { - transactions: val.transactions.iter().map(|t| t.clone().into()).collect(), - ommers: val.uncle_headers.iter().map(|h| h.clone().into()).collect(), - withdrawals: val.withdrawals, - }, - ) - } -} - -#[derive(Debug, PartialEq, Eq, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct Transaction { - #[serde(rename = "type")] - pub transaction_type: Option, - #[serde(with = "ethrex_common::serde_utils::bytes")] - pub data: Bytes, - pub gas_limit: U256, - pub gas_price: Option, - pub nonce: U256, - pub r: U256, - pub s: U256, - pub v: U256, - pub value: U256, - pub chain_id: Option, - pub access_list: Option, - pub authorization_list: Option, - pub max_fee_per_gas: Option, - pub max_fee_per_blob_gas: Option, - pub max_priority_fee_per_gas: Option, - pub blob_versioned_hashes: Option>, - pub hash: Option, - pub sender: Option
, - pub to: TxKind, -} - -// Conversions between EFtests & ethrex types -impl From
for BlockHeader { - fn from(val: Header) -> Self { - BlockHeader { - parent_hash: val.parent_hash, - ommers_hash: val.uncle_hash, - coinbase: val.coinbase, - state_root: val.state_root, - transactions_root: val.transactions_trie, - receipts_root: val.receipt_trie, - logs_bloom: val.bloom, - difficulty: val.difficulty, - number: val.number.try_into().unwrap(), - gas_limit: val.gas_limit.try_into().unwrap(), - gas_used: val.gas_used.try_into().unwrap(), - timestamp: val.timestamp.try_into().unwrap(), - extra_data: val.extra_data, - prev_randao: val.mix_hash, - nonce: val.nonce.to_low_u64_be(), - base_fee_per_gas: val.base_fee_per_gas.map(|v| v.try_into().unwrap()), - withdrawals_root: val.withdrawals_root, - blob_gas_used: val.blob_gas_used.map(|x| x.try_into().unwrap()), - excess_blob_gas: val.excess_blob_gas.map(|x| x.try_into().unwrap()), - parent_beacon_block_root: val.parent_beacon_block_root, - requests_hash: val.requests_hash, - block_access_list_hash: val.block_access_list_hash, - slot_number: val.slot_number.map(|x| x.try_into().unwrap()), - ..Default::default() - } - } -} - -impl From for ethrexTransaction { - fn from(val: Transaction) -> Self { - match val.transaction_type { - Some(tx_type) => match tx_type.try_into().unwrap() { - 0 => ethrexTransaction::LegacyTransaction(val.into()), - 1 => ethrexTransaction::EIP2930Transaction(val.into()), - 2 => ethrexTransaction::EIP1559Transaction(val.into()), - 3 => ethrexTransaction::EIP4844Transaction(val.into()), - 4 => ethrexTransaction::EIP7702Transaction(val.into()), - _ => unimplemented!(), - }, - None => ethrexTransaction::LegacyTransaction(val.into()), - } - } -} - -impl From for EIP1559Transaction { - fn from(val: Transaction) -> Self { - EIP1559Transaction { - // Note: gas_price is not used in this conversion as it is not part of EIP1559Transaction, this could be a problem - chain_id: val - .chain_id - .map(|id| id.try_into().unwrap()) - .unwrap_or(1 /*mainnet*/), // TODO: Consider converting this into Option - nonce: val.nonce.try_into().unwrap(), - max_priority_fee_per_gas: val - .max_priority_fee_per_gas - .unwrap_or_default() - .try_into() - .unwrap(), // TODO: Consider converting this into Option - max_fee_per_gas: val - .max_fee_per_gas - .unwrap_or(val.gas_price.unwrap_or_default()) - .try_into() - .unwrap(), // TODO: Consider converting this into Option - gas_limit: val.gas_limit.try_into().unwrap(), - // to: match val.to { - // zero if zero == H160::zero() => TxKind::Create, - // _ => TxKind::Call(val.to), - // }, - to: val.to, - value: val.value, - data: val.data, - access_list: val - .access_list - .unwrap_or_default() - .into_iter() - .map(|item| (item.address, item.storage_keys)) - .collect(), - signature_y_parity: !val.v.is_zero(), - signature_r: val.r, - signature_s: val.s, - ..Default::default() - } - } -} - -impl From for EIP4844Transaction { - fn from(val: Transaction) -> Self { - EIP4844Transaction { - chain_id: val - .chain_id - .map(|id: U256| id.try_into().unwrap()) - .unwrap_or(1), // TODO: Consider converting this into Option - nonce: val.nonce.try_into().unwrap(), - max_priority_fee_per_gas: val - .max_priority_fee_per_gas - .unwrap_or_default() - .try_into() - .unwrap(), // TODO: Consider converting this into Option - max_fee_per_gas: val - .max_fee_per_gas - .unwrap_or(val.gas_price.unwrap_or_default()) - .try_into() - .unwrap(), - gas: val.gas_limit.try_into().unwrap(), - to: match val.to { - TxKind::Call(address) => address, - TxKind::Create => panic!("EIP4844Transaction cannot be contract creation"), - }, - value: val.value, - data: val.data, - access_list: val - .access_list - .unwrap_or_default() - .into_iter() - .map(|a| (a.address, a.storage_keys)) - .collect(), - max_fee_per_blob_gas: val.max_fee_per_blob_gas.unwrap(), - blob_versioned_hashes: val.blob_versioned_hashes.unwrap_or_default(), - signature_y_parity: !val.v.is_zero(), - signature_r: val.r, - signature_s: val.s, - ..Default::default() - } - } -} - -impl From for EIP7702Transaction { - fn from(val: Transaction) -> Self { - EIP7702Transaction { - chain_id: val - .chain_id - .map(|id: U256| id.try_into().unwrap()) - .unwrap_or(1), // TODO: Consider converting this into Option - nonce: val.nonce.try_into().unwrap(), - max_priority_fee_per_gas: val - .max_priority_fee_per_gas - .unwrap_or_default() - .try_into() - .unwrap(), // TODO: Consider converting this into Option - max_fee_per_gas: val - .max_fee_per_gas - .unwrap_or(val.gas_price.unwrap_or_default()) - .try_into() - .unwrap(), - gas_limit: val.gas_limit.try_into().unwrap(), - to: match val.to { - TxKind::Call(address) => address, - TxKind::Create => panic!("EIP7702Transaction cannot be contract creation"), - }, - value: val.value, - data: val.data, - access_list: val - .access_list - .unwrap_or_default() - .into_iter() - .map(|a| (a.address, a.storage_keys)) - .collect(), - authorization_list: val - .authorization_list - .unwrap_or_default() - .into_iter() - .map(|a| ethrex_common::types::AuthorizationTuple { - chain_id: a.chain_id, - address: a.address, - nonce: a.nonce.try_into().unwrap(), - y_parity: a.v, - r_signature: a.r, - s_signature: a.s, - }) - .collect(), - signature_y_parity: !val.v.is_zero(), - signature_r: val.r, - signature_s: val.s, - ..Default::default() - } - } -} - -impl From for LegacyTransaction { - fn from(val: Transaction) -> Self { - LegacyTransaction { - nonce: val.nonce.try_into().unwrap(), - gas_price: val.gas_price.unwrap_or_default(), // TODO: Consider converting this into Option - gas: val.gas_limit.try_into().unwrap(), - // to: match val.to { - // zero if zero == H160::zero() => TxKind::Create, - // _ => TxKind::Call(val.to), - // }, - to: val.to, - value: val.value, - data: val.data, - v: val.v, - r: val.r, - s: val.s, - ..Default::default() - } - } -} - -impl From for EIP2930Transaction { - fn from(val: Transaction) -> Self { - EIP2930Transaction { - chain_id: val - .chain_id - .map(|id: U256| id.try_into().unwrap()) - .unwrap_or(1), - nonce: val.nonce.try_into().unwrap(), - gas_price: val.gas_price.unwrap_or_default(), - gas_limit: val.gas_limit.try_into().unwrap(), - // to: match val.to { - // zero if zero == H160::zero() => TxKind::Create, - // _ => TxKind::Call(val.to), - // }, - to: val.to, - value: val.value, - data: val.data, - access_list: val - .access_list - .unwrap_or_default() - .into_iter() - .map(|a| (a.address, a.storage_keys)) - .collect(), - signature_y_parity: !val.v.is_zero(), - signature_r: val.r, - signature_s: val.s, - ..Default::default() - } - } -} - -impl From for ethrexAccount { - fn from(val: Account) -> Self { - ethrexAccount { - info: AccountInfo { - code_hash: code_hash(&val.code, ðrex_crypto::NativeCrypto), - balance: val.balance, - nonce: val.nonce.try_into().unwrap(), - }, - code: Code::from_bytecode(val.code, ðrex_crypto::NativeCrypto), - storage: val - .storage - .into_iter() - .map(|(k, v)| (H256(k.to_big_endian()), v)) - .collect(), - } - } -} - -impl From for GenesisAccount { - fn from(val: Account) -> Self { - GenesisAccount { - code: val.code, - storage: val.storage.into_iter().collect(), - balance: val.balance, - nonce: val.nonce.try_into().unwrap(), - } - } -} - -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] -pub enum BlockChainExpectedException { - TxtException(String), - BlockException(BlockExpectedException), - RLPException, - Other, -} - -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] -pub enum BlockExpectedException { - RLPStructuresEncoding, - IncorrectBlobGasUsed, - BlobGasUsedAboveLimit, - IncorrectExcessBlobGas, - IncorrectBlockFormat, - InvalidRequest, - SystemContractCallFailed, - RlpBlockLimitExceeded, - Other, //TODO: Implement exceptions -} diff --git a/tooling/ef_tests/run-ef-tests.py b/tooling/ef_tests/run-ef-tests.py deleted file mode 100755 index a4cf36d5b1a..00000000000 --- a/tooling/ef_tests/run-ef-tests.py +++ /dev/null @@ -1,842 +0,0 @@ -#!/usr/bin/env python3 -"""EF Blockchain Test Runner & Analyzer for ethrex. - -Runs EF blockchain tests, parses cargo test output, categorizes failures, -and produces LLM-friendly reports for efficient iteration on EVM fixes. - -Usage: - python run-ef-tests.py # Run all tests, show report - python run-ef-tests.py --from-file out.log # Parse saved output - python run-ef-tests.py --filter eip7702 # Run only matching tests - python run-ef-tests.py --get-json # Find & print JSON for a test - python run-ef-tests.py --summary-only # Counts + category table only - python run-ef-tests.py --json-output # Machine-readable JSON output - python run-ef-tests.py --save-output f.log # Save raw cargo output - python run-ef-tests.py --count-by-eip # Break down failures by EIP - python run-ef-tests.py --list-categories # Show failure category definitions - python run-ef-tests.py --state --forks Amsterdam # Run state tests for Amsterdam fork - python run-ef-tests.py --state --summary-only # State tests, summary only -""" - -import argparse -import json -import re -import subprocess -import sys -from dataclasses import dataclass, field -from datetime import datetime -from enum import Enum, auto -from pathlib import Path -from typing import Optional - - -# --------------------------------------------------------------------------- -# Failure categories - derived from test_runner.rs error messages -# --------------------------------------------------------------------------- - -class FailureCategory(Enum): - RLPDecodingError = auto() - GenesisHeaderMismatch = auto() - UnexpectedExecutionFailure = auto() - ExpectedExceptionNotRaised = auto() - GenesisStateRootMismatch = auto() - AccountInfoNotFound = auto() - AccountInfoMismatch = auto() - AccountCodeNotFound = auto() - AccountCodeMismatch = auto() - StorageNotFound = auto() - StorageMismatch = auto() - LastBlockHashMismatch = auto() - WitnessCreationFailed = auto() - StatelessExecutionFailed = auto() - StatelessUnexpectedSuccess = auto() - Unknown = auto() - - -# Ordered list: more specific patterns first. -CATEGORY_PATTERNS: list[tuple[FailureCategory, re.Pattern]] = [ - (FailureCategory.RLPDecodingError, - re.compile(r"Failed to decode genesis RLP")), - (FailureCategory.GenesisHeaderMismatch, - re.compile(r"Decoded genesis header does not match")), - (FailureCategory.UnexpectedExecutionFailure, - re.compile(r"Transaction execution unexpectedly failed")), - (FailureCategory.ExpectedExceptionNotRaised, - re.compile(r"Expected transaction execution to fail")), - (FailureCategory.GenesisStateRootMismatch, - re.compile(r"Mismatched genesis state root")), - (FailureCategory.AccountInfoNotFound, - re.compile(r"Account info for address .* not found")), - (FailureCategory.AccountInfoMismatch, - re.compile(r"Mismatched account info")), - (FailureCategory.AccountCodeNotFound, - re.compile(r"Account code for code hash .* not found")), - (FailureCategory.AccountCodeMismatch, - re.compile(r"Mismatched account code")), - (FailureCategory.StorageNotFound, - re.compile(r"Storage missing for address")), - (FailureCategory.StorageMismatch, - re.compile(r"Mismatched storage value")), - (FailureCategory.LastBlockHashMismatch, - re.compile(r"Last block number does not match")), - (FailureCategory.WitnessCreationFailed, - re.compile(r"Failed to create witness")), - (FailureCategory.StatelessExecutionFailed, - re.compile(r"to succeed but failed")), - (FailureCategory.StatelessUnexpectedSuccess, - re.compile(r"to fail but succeeded")), -] - -CATEGORY_DESCRIPTIONS: dict[FailureCategory, str] = { - FailureCategory.RLPDecodingError: "Failed to decode genesis block RLP (test_runner.rs L73)", - FailureCategory.GenesisHeaderMismatch: "Decoded genesis header != expected header (test_runner.rs L77)", - FailureCategory.UnexpectedExecutionFailure: "Block execution failed when it should have succeeded (test_runner.rs L128)", - FailureCategory.ExpectedExceptionNotRaised: "Block execution succeeded when it should have failed (test_runner.rs L142)", - FailureCategory.GenesisStateRootMismatch: "Genesis state root in DB != expected (test_runner.rs L322)", - FailureCategory.AccountInfoNotFound: "Post-state account not found in DB (test_runner.rs L341)", - FailureCategory.AccountInfoMismatch: "Post-state account info differs from DB (test_runner.rs L345)", - FailureCategory.AccountCodeNotFound: "Account code not found for expected code hash (test_runner.rs L356)", - FailureCategory.AccountCodeMismatch: "Account code in DB differs from expected (test_runner.rs L360)", - FailureCategory.StorageNotFound: "Storage slot missing from DB (test_runner.rs L369)", - FailureCategory.StorageMismatch: "Storage value in DB differs from expected (test_runner.rs L373)", - FailureCategory.LastBlockHashMismatch: "Last block hash after execution != expected (test_runner.rs L383)", - FailureCategory.WitnessCreationFailed: "Failed to create execution witness (test_runner.rs L412)", - FailureCategory.StatelessExecutionFailed: "Stateless execution failed when expected to succeed (test_runner.rs L429)", - FailureCategory.StatelessUnexpectedSuccess: "Stateless execution succeeded when expected to fail (test_runner.rs L433)", - FailureCategory.Unknown: "Unrecognized error pattern", -} - - -# --------------------------------------------------------------------------- -# Data types -# --------------------------------------------------------------------------- - -@dataclass -class TestFailure: - file_path: str # vectors/eest/... path from test name - test_key: str # full test key (from JSON) - error_text: str # raw error message - category: str = "Unknown" # the actual error kind (e.g. InvalidBlock(BlockAccessListHashMismatch)) - - -@dataclass -class TestResults: - passed: int = 0 - failed: int = 0 - ignored: int = 0 - duration_secs: float = 0.0 - failures: list[TestFailure] = field(default_factory=list) - raw_output: str = "" - timestamp: str = "" - - -# --------------------------------------------------------------------------- -# Categorization -# --------------------------------------------------------------------------- - -def clean_error_text(text: str) -> str: - """Strip debug-format artifacts like escaped quotes.""" - return text.replace('\\"', '"').strip('"').strip("\\").strip() - - -# Regex to extract the inner error from "with error " -RE_INNER_ERROR = re.compile(r"with error\s+(.+?)(?:\s*$|\")") - - -def categorize_error(error_text: str) -> str: - """Extract the actual error kind from the error message. - - For "unexpectedly failed ... with error X" -> returns X (the useful part). - For panics like "Mismatched account info" -> returns that message directly. - """ - # First try to extract inner error from execution failures - m = RE_INNER_ERROR.search(error_text) - if m: - return m.group(1).strip() - - # Fall back to pattern matching for panics / other messages - for category, pattern in CATEGORY_PATTERNS: - if pattern.search(error_text): - return category.name - return "Unknown" - - -# --------------------------------------------------------------------------- -# Parsing -# --------------------------------------------------------------------------- - -# Matches: test blockchain_runner::eest/path/file.json ... ok -# datatest_stable uses "::" separator (not brackets) -# The path may be followed by variable whitespace before "..." -RE_TEST_LINE = re.compile( - r"^test\s+blockchain_runner::(.+?\.json)\s+\.\.\.\s+(ok|FAILED|ignored)" -) - -# Matches the summary line: test result: FAILED. X passed; Y failed; Z ignored; ... -RE_SUMMARY = re.compile( - r"test result:.*?(\d+)\s+passed;\s+(\d+)\s+failed;\s+(\d+)\s+ignored" -) - -# Matches duration: finished in Xs -RE_DURATION = re.compile(r"finished in\s+([\d.]+)s") - -# Matches failure block header: -# ---- blockchain_runner::eest/path/file.json ---- -RE_FAILURE_HEADER = re.compile( - r"^----\s+blockchain_runner::(.+?\.json)\s+----$" -) - -# Extract test key from error text patterns like "test:key_name" or "test_key: error" -RE_TEST_KEY_IN_ERROR = re.compile(r"test:(\S+)") - -# Separator used when test_runner.rs joins multiple errors -ERROR_SEPARATOR = " ------- " - -# Regex to strip ANSI escape sequences -RE_ANSI = re.compile(r"\x1b\[[0-9;]*m") - -# State test patterns -RE_STATE_SUMMARY = re.compile(r"Summary:\s*(\d+)/(\d+)") -RE_STATE_DIR_RESULT = re.compile(r"(state_tests/\S+|GeneralStateTests/\S+|LegacyTests/\S+):\s*(\d+)/(\d+)\s*\(([\d.]+)%\)") -RE_STATE_DURATION = re.compile(r"real\s+(\d+)m([\d.]+)s") - - -def parse_output(raw: str) -> TestResults: - """Parse cargo test output into structured results.""" - results = TestResults( - raw_output=raw, - timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), - ) - - lines = raw.split("\n") - - # --- Pass 1: Count results from test lines --- - failed_paths: set[str] = set() - for line in lines: - m = RE_TEST_LINE.match(line.strip()) - if m: - path, status = m.group(1), m.group(2) - if status == "FAILED": - failed_paths.add(path) - - # --- Pass 2: Extract summary line --- - # Keep the result line with the highest total (avoids doc-test "0 passed; 0 failed") - best_total = -1 - for line in lines: - m = RE_SUMMARY.search(line) - if m: - p, f, ig = int(m.group(1)), int(m.group(2)), int(m.group(3)) - if p + f + ig > best_total: - best_total = p + f + ig - results.passed = p - results.failed = f - results.ignored = ig - m = RE_DURATION.search(line) - if m: - results.duration_secs = float(m.group(1)) - - # --- Pass 3: Extract failure blocks --- - # Failure details appear in stderr between the header markers. - i = 0 - while i < len(lines): - header_match = RE_FAILURE_HEADER.match(lines[i].strip()) - if header_match: - file_path = header_match.group(1) - i += 1 - # Collect all lines until the next header or "failures:" section - block_lines: list[str] = [] - while i < len(lines): - stripped = lines[i].strip() - if RE_FAILURE_HEADER.match(stripped): - break - if stripped == "failures:": - break - if stripped.startswith("note:"): - i += 1 - continue - block_lines.append(lines[i]) - i += 1 - - error_block = "\n".join(block_lines).strip() - if not error_block: - continue - - # Handle two failure modes: - # 1. Returned errors joined by separator - # 2. Panics with "panicked at" messages - - if ERROR_SEPARATOR in error_block: - # Mode 1: Multiple errors joined by separator - parts = error_block.split(ERROR_SEPARATOR) - for part in parts: - part = part.strip().strip('"') - if not part: - continue - # Format: "test_key: error_message" or just error text - test_key = "" - error_text = part - # Try to extract test key from "key: error" format - colon_idx = part.find(": ") - if colon_idx > 0 and not part[:colon_idx].startswith("Failed"): - test_key = part[:colon_idx].strip().strip('"') - error_text = part[colon_idx + 2:].strip().strip('"') - - error_text = clean_error_text(error_text) - category = categorize_error(error_text) - results.failures.append(TestFailure( - file_path=file_path, - test_key=test_key or file_path, - error_text=error_text, - category=category, - )) - else: - # Mode 2: Panic or single error - clean = error_block.strip().strip('"') - test_key = "" - error_text = clean - - # Try "key: error" format first - colon_idx = clean.find(": ") - if colon_idx > 0 and not clean[:colon_idx].startswith("Failed"): - test_key = clean[:colon_idx].strip().strip('"') - error_text = clean[colon_idx + 2:].strip().strip('"') - - # Fallback: extract test key from "test:key_name" in error - if not test_key: - key_match = RE_TEST_KEY_IN_ERROR.search(error_text) - if key_match: - test_key = key_match.group(1) - - error_text = clean_error_text(error_text) - category = categorize_error(error_text) - results.failures.append(TestFailure( - file_path=file_path, - test_key=test_key or file_path, - error_text=error_text, - category=category, - )) - else: - i += 1 - - return results - - -def parse_state_output(raw: str) -> TestResults: - """Parse state test runner output into structured results.""" - results = TestResults( - raw_output=raw, - timestamp=datetime.now().strftime("%Y-%m-%d %H:%M:%S"), - ) - - # Strip ANSI codes for reliable matching - clean = RE_ANSI.sub("", raw) - lines = clean.split("\n") - - # Extract summary line (first match of "Summary: N/M") - for line in lines: - m = RE_STATE_SUMMARY.search(line) - if m: - results.passed = int(m.group(1)) - total = int(m.group(2)) - results.failed = total - results.passed - break - - # Extract duration from `time` output: real XmY.Zs - for line in lines: - m = RE_STATE_DURATION.search(line) - if m: - minutes = int(m.group(1)) - seconds = float(m.group(2)) - results.duration_secs = minutes * 60 + seconds - - # Parse "Failed tests:" section for individual failure blocks - in_failed_section = False - i = 0 - while i < len(lines): - stripped = lines[i].strip() - - if stripped == "Failed tests:": - in_failed_section = True - i += 1 - continue - - if not in_failed_section: - i += 1 - continue - - # Each failure block starts with "Test:" - if stripped == "Test:": - # Collect block: Test name, Test path, description, then fork/vector/error lines - test_name = "" - test_path = "" - test_description = "" - i += 1 - - # Read test metadata lines - while i < len(lines): - stripped = lines[i].strip() - if stripped.startswith("Test name:"): - test_name = stripped[len("Test name:"):].strip() - elif stripped.startswith("Test path:"): - test_path = stripped[len("Test path:"):].strip() - elif stripped.startswith("Test description:"): - test_description = stripped[len("Test description:"):].strip() - elif stripped.startswith("Fork:") or stripped == "Test:" or stripped == "": - if stripped.startswith("Fork:") or stripped == "Test:": - break - # Skip blank lines within the metadata - elif stripped.startswith("Note:") or stripped.startswith("- http") or stripped.startswith("- Test"): - pass # Skip note/link lines - i += 1 - - # Now parse Fork/Failed Vector/Error blocks - while i < len(lines): - stripped = lines[i].strip() - if stripped == "Test:" or stripped == "": - # Check if this blank line is followed by another Test: block - if stripped == "": - # Look ahead for Test: or end - j = i + 1 - while j < len(lines) and lines[j].strip() == "": - j += 1 - if j >= len(lines) or lines[j].strip() == "Test:": - break - # Otherwise it might be within the block, keep going - i += 1 - continue - break - - if stripped.startswith("Fork:"): - current_fork = stripped[len("Fork:"):].strip() - i += 1 - # Read Failed Vector / Error pairs - while i < len(lines): - stripped = lines[i].strip() - if stripped.startswith("Fork:") or stripped == "Test:" or (stripped == "" and _is_block_end(lines, i)): - break - if stripped.startswith("Failed Vector:"): - vector_text = stripped - error_text = "" - category = "" - i += 1 - # Read Error and other detail lines - while i < len(lines): - stripped = lines[i].strip() - if stripped.startswith("Error:"): - error_text = stripped[len("Error:"):].strip() - category = _categorize_state_error(error_text) - elif (stripped.startswith("Failed Vector:") or - stripped.startswith("Fork:") or - stripped == "Test:" or - (stripped == "" and _is_block_end(lines, i))): - break - # Other detail lines (execution result mismatch, gas mismatch, etc.) - skip - i += 1 - - results.failures.append(TestFailure( - file_path=test_path, - test_key=test_name, - error_text=error_text or vector_text, - category=category or "Unknown", - )) - continue - i += 1 - continue - i += 1 - continue - i += 1 - - return results - - -def _is_block_end(lines: list[str], i: int) -> bool: - """Check if a blank line signals the end of a failure block.""" - j = i + 1 - while j < len(lines) and lines[j].strip() == "": - j += 1 - if j >= len(lines): - return True - next_line = lines[j].strip() - return next_line == "Test:" or next_line == "" - - -def _categorize_state_error(error_text: str) -> str: - """Extract a short category from state test error text.""" - # Common patterns in state test errors - if "Post-state root mismatch" in error_text or "post-state" in error_text.lower(): - return "Post-state root mismatch" - if "Logs mismatch" in error_text or "logs mismatch" in error_text.lower(): - return "Logs mismatch" - if "Gas used mismatch" in error_text or "gas used" in error_text.lower(): - return "Gas used mismatch" - if "Gas refunded mismatch" in error_text or "gas refunded" in error_text.lower(): - return "Gas refunded mismatch" - if "Execution result mismatch" in error_text: - return "Execution result mismatch" - if "execution failed when it was not expected" in error_text.lower(): - return "Unexpected execution failure" - if "Exception does not match" in error_text: - return "Exception mismatch" - if "Failed to ensure pre-state" in error_text: - return "Pre-state validation failure" - if "Failed to ensure post-state" in error_text: - return "Post-state validation failure" - if "VM initialization failed" in error_text: - return "VM initialization failure" - # For more specific errors, use the first significant phrase - if error_text: - # Truncate long errors to a reasonable category name - short = error_text[:80] - return short - return "Unknown" - - -# --------------------------------------------------------------------------- -# Test execution -# --------------------------------------------------------------------------- - -SCRIPT_DIR = Path(__file__).resolve().parent - - -def run_tests(filter_pattern: Optional[str] = None) -> str: - """Run EF blockchain tests and return combined stdout+stderr.""" - if filter_pattern: - cmd = [ - "cargo", "test", - "--profile", "release-with-debug", - "--", filter_pattern, - ] - else: - cmd = ["make", "test-levm"] - - blockchain_dir = SCRIPT_DIR / "blockchain" - print(f"Running: {' '.join(cmd)}", file=sys.stderr) - print(f"Working directory: {blockchain_dir}", file=sys.stderr) - - proc = subprocess.run( - cmd, - cwd=str(blockchain_dir), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - ) - return proc.stdout - - -def run_state_tests(forks: Optional[str] = None) -> str: - """Run EF state tests and return combined stdout+stderr.""" - flags = "--summary" - if forks: - flags = f"--forks {forks} --summary" - cmd = ["make", "run-evm-ef-tests", f"flags={flags}"] - state_dir = SCRIPT_DIR / "state" - print(f"Running: {' '.join(cmd)}", file=sys.stderr) - print(f"Working directory: {state_dir}", file=sys.stderr) - proc = subprocess.run( - cmd, - cwd=str(state_dir), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - text=True, - ) - return proc.stdout - - -# --------------------------------------------------------------------------- -# JSON test lookup -# --------------------------------------------------------------------------- - -VECTORS_DIR = SCRIPT_DIR / "blockchain" / "vectors" - - -def get_json(search_term: str) -> None: - """Find and print the JSON test fixture matching the search term.""" - if not VECTORS_DIR.exists(): - print(f"Error: vectors directory not found at {VECTORS_DIR}", file=sys.stderr) - sys.exit(1) - - # Strategy 1: Search by file path - matches: list[tuple[Path, str]] = [] # (file_path, key) - - for json_file in VECTORS_DIR.rglob("*.json"): - rel = str(json_file.relative_to(SCRIPT_DIR)) - if search_term.lower() in rel.lower(): - try: - with open(json_file) as f: - data = json.load(f) - for key in data: - matches.append((json_file, key)) - except (json.JSONDecodeError, OSError): - continue - - # Strategy 2: Search by test key inside JSON files (if no path matches) - if not matches: - for json_file in VECTORS_DIR.rglob("*.json"): - try: - with open(json_file) as f: - data = json.load(f) - for key in data: - if search_term.lower() in key.lower(): - matches.append((json_file, key)) - except (json.JSONDecodeError, OSError): - continue - - if not matches: - print(f"No test found matching: {search_term}") - return - - if len(matches) > 20: - print(f"Found {len(matches)} matches. Showing first 20:\n") - for fpath, key in matches[:20]: - print(f" {fpath.relative_to(SCRIPT_DIR)}") - print(f" Key: {key}\n") - print(f"... and {len(matches) - 20} more. Refine your search term.") - return - - if len(matches) > 1: - print(f"Found {len(matches)} matches:\n") - for idx, (fpath, key) in enumerate(matches, 1): - print(f" [{idx}] {fpath.relative_to(SCRIPT_DIR)}") - print(f" Key: {key}\n") - - # If few enough, print all; otherwise just list - if len(matches) > 5: - print("Refine your search term or use a more specific path/key.") - return - - # Print matching entries - for fpath, key in matches: - with open(fpath) as f: - data = json.load(f) - entry = data[key] - - print(f"--- File: {fpath.relative_to(SCRIPT_DIR)}") - print(f"--- Key: {key}") - if "network" in entry: - print(f"--- Network: {entry['network']}") - if "blocks" in entry: - print(f"--- Blocks: {len(entry['blocks'])}") - if "_info" in entry and "description" in entry["_info"]: - print(f"--- Description: {entry['_info']['description']}") - print() - - # Print the entry without _info (it's metadata noise) - filtered = {k: v for k, v in entry.items() if k != "_info"} - print(json.dumps(filtered, indent=2)) - print() - - -# --------------------------------------------------------------------------- -# Output formatters -# --------------------------------------------------------------------------- - -def group_by_category(failures: list[TestFailure]) -> dict[str, list[TestFailure]]: - groups: dict[str, list[TestFailure]] = {} - for f in failures: - groups.setdefault(f.category, []).append(f) - return groups - - -def extract_eip(text: str) -> Optional[str]: - """Extract EIP number from a test path or key.""" - m = re.search(r"(eip\d+)", text, re.IGNORECASE) - return m.group(1).lower() if m else None - - -def group_by_eip(failures: list[TestFailure]) -> dict[str, list[TestFailure]]: - groups: dict[str, list[TestFailure]] = {} - for f in failures: - eip = extract_eip(f.file_path) or extract_eip(f.test_key) or "unknown" - groups.setdefault(eip, []).append(f) - return groups - - -def print_report(results: TestResults, summary_only: bool = False, - count_by_eip: bool = False) -> None: - """Print a markdown-formatted report.""" - total = results.passed + results.failed + results.ignored - - print(f"# EF Blockchain Test Results") - print(f"Run: {results.timestamp} | Duration: {results.duration_secs:.1f}s") - print() - print(f"## Summary") - print(f"Passed: {results.passed} | Failed: {results.failed} | " - f"Ignored: {results.ignored} | Total: {total}") - print() - - if results.failed == 0: - print("All tests passed!") - return - - # Category breakdown - by_cat = group_by_category(results.failures) - print(f"## Failures by Category") - sorted_cats = sorted(by_cat.items(), key=lambda x: len(x[1]), reverse=True) - for cat, items in sorted_cats: - pct = len(items) / len(results.failures) * 100 if results.failures else 0 - print(f" {cat:<50s} {len(items):>4d} ({pct:5.1f}%)") - print() - - # EIP breakdown - if count_by_eip: - by_eip = group_by_eip(results.failures) - print(f"## Failures by EIP") - sorted_eips = sorted(by_eip.items(), key=lambda x: len(x[1]), reverse=True) - for eip, items in sorted_eips: - print(f" {eip:<20s} {len(items):>4d}") - print() - - if summary_only: - return - - # Detailed failures grouped by category - print(f"## Detailed Failures") - for cat, items in sorted_cats: - print(f"\n### {cat} ({len(items)})") - for idx, f in enumerate(items, 1): - print(f"{idx:>3d}. File: {f.file_path}") - if f.test_key != f.file_path: - print(f" Key: {f.test_key}") - # Truncate very long error messages - err = f.error_text - if len(err) > 500: - err = err[:500] + "... [truncated]" - print(f" Error: {err}") - print() - - -def print_json_output(results: TestResults) -> None: - """Print machine-readable JSON output.""" - by_cat = group_by_category(results.failures) - output = { - "timestamp": results.timestamp, - "duration_secs": results.duration_secs, - "summary": { - "passed": results.passed, - "failed": results.failed, - "ignored": results.ignored, - "total": results.passed + results.failed + results.ignored, - }, - "categories": { - cat: { - "count": len(items), - "failures": [ - { - "file_path": f.file_path, - "test_key": f.test_key, - "error_text": f.error_text[:1000], - } - for f in items - ], - } - for cat, items in by_cat.items() - }, - } - print(json.dumps(output, indent=2)) - - -def print_categories() -> None: - """Print all failure category definitions.""" - print("# EF Test Failure Categories\n") - print(f"{'Category':<35s} {'Pattern':<45s} Description") - print("-" * 120) - for cat, pattern in CATEGORY_PATTERNS: - desc = CATEGORY_DESCRIPTIONS.get(cat, "") - print(f"{cat.name:<35s} {pattern.pattern:<45s} {desc}") - print() - - -# --------------------------------------------------------------------------- -# Main -# --------------------------------------------------------------------------- - -def main() -> None: - parser = argparse.ArgumentParser( - description="EF Blockchain Test Runner & Analyzer for ethrex", - formatter_class=argparse.RawDescriptionHelpFormatter, - epilog=__doc__, - ) - parser.add_argument( - "--from-file", metavar="FILE", - help="Parse saved cargo test output instead of running tests", - ) - parser.add_argument( - "--filter", metavar="PATTERN", - help="Run only tests matching this pattern (passed to cargo test)", - ) - parser.add_argument( - "--get-json", metavar="NAME", - help="Find & print JSON fixture for a test name", - ) - parser.add_argument( - "--summary-only", action="store_true", - help="Show only counts and category table", - ) - parser.add_argument( - "--json-output", action="store_true", - help="Output results as machine-readable JSON", - ) - parser.add_argument( - "--save-output", metavar="FILE", - help="Save raw cargo test output to file", - ) - parser.add_argument( - "--count-by-eip", action="store_true", - help="Include failure breakdown by EIP number", - ) - parser.add_argument( - "--list-categories", action="store_true", - help="Show all failure category definitions and exit", - ) - parser.add_argument( - "--state", action="store_true", - help="Run state tests instead of blockchain tests", - ) - parser.add_argument( - "--forks", metavar="FORKS", - help="Comma-separated fork list for state tests (e.g., Amsterdam)", - ) - - args = parser.parse_args() - - # --list-categories: just print and exit - if args.list_categories: - print_categories() - return - - # --get-json: lookup mode - if args.get_json: - get_json(args.get_json) - return - - # Get test output - if args.from_file: - with open(args.from_file) as f: - raw = f.read() - else: - if args.state: - raw = run_state_tests(args.forks) - else: - raw = run_tests(args.filter) - - # Save output if requested - if args.save_output: - with open(args.save_output, "w") as f: - f.write(raw) - print(f"Saved raw output to {args.save_output}", file=sys.stderr) - - # Parse and report - if args.state: - results = parse_state_output(raw) - else: - results = parse_output(raw) - - if args.json_output: - print_json_output(results) - else: - print_report(results, summary_only=args.summary_only, - count_by_eip=args.count_by_eip) - - -if __name__ == "__main__": - main() diff --git a/tooling/ef_tests/state/.fixtures_url b/tooling/ef_tests/state/.fixtures_url deleted file mode 100644 index ea7c838993b..00000000000 --- a/tooling/ef_tests/state/.fixtures_url +++ /dev/null @@ -1 +0,0 @@ -https://github.com/ethereum/execution-spec-tests/releases/download/v5.3.0/fixtures_develop.tar.gz diff --git a/tooling/ef_tests/state/.fixtures_url_amsterdam b/tooling/ef_tests/state/.fixtures_url_amsterdam deleted file mode 100644 index 2290401371e..00000000000 --- a/tooling/ef_tests/state/.fixtures_url_amsterdam +++ /dev/null @@ -1 +0,0 @@ -https://github.com/ethereum/execution-spec-tests/releases/download/bal%40v5.6.1/fixtures_bal.tar.gz diff --git a/tooling/ef_tests/state/.gitignore b/tooling/ef_tests/state/.gitignore deleted file mode 100644 index 03314f77b5a..00000000000 --- a/tooling/ef_tests/state/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Cargo.lock diff --git a/tooling/ef_tests/state/Cargo.toml b/tooling/ef_tests/state/Cargo.toml deleted file mode 100644 index 8a835822417..00000000000 --- a/tooling/ef_tests/state/Cargo.toml +++ /dev/null @@ -1,59 +0,0 @@ -[package] -name = "ef_tests-state" -version = "0.1.0" -edition = "2024" -authors = ["LambdaClass"] -license = "MIT OR Apache-2.0" - -[dependencies] -ethrex-blockchain = { path = "../../../crates/blockchain", default-features = false } -ethrex-common = { path = "../../../crates/common", default-features = false } -ethrex-crypto = { path = "../../../crates/common/crypto", default-features = false } -ethrex-storage = { path = "../../../crates/storage" } -ethrex-rlp = { path = "../../../crates/common/rlp" } -ethrex-vm = { path = "../../../crates/vm", default-features = false } -ethrex-levm = { path = "../../../crates/vm/levm", default-features = false } - -serde = { version = "1.0.203", features = ["derive"] } -serde_json = "1.0.117" -# Since we do heavy parsing of ef tests, simd-json is more than 2x faster than serde_json -simd-json = "0.15.1" -bytes = { version = "1.6.0", features = ["serde"] } -colored = "2.1.0" -spinoff = "0.8.0" -thiserror = "2.0.9" -hex = "0.4.3" -clap = { version = "4.3", features = ["derive", "env"] } -clap_complete = "4.5.17" -itertools = "0.13.0" -revm = { version = "27.0.3", features = [ - "serde", - "std", - "serde-json", - "optional_no_base_fee", - "optional_block_gas_limit", -], default-features = false } -alloy-rlp = "0.3.12" -tokio = { version = "1.41.1", features = ["full"] } -rayon = "1.10.0" - -[dev-dependencies] -hex = "0.4.3" - -[lib] -path = "./lib.rs" - -[features] -default = ["c-kzg", "secp256k1"] -c-kzg = ["ethrex-vm/c-kzg", "ethrex-levm/c-kzg", "ethrex-common/c-kzg"] -secp256k1 = ["ethrex-blockchain/secp256k1", "ethrex-common/secp256k1", "ethrex-vm/secp256k1"] -# Runs EF tests with no_std-compatible crypto fallbacks (k256, num-bigint, kzg-rs) -nostd-crypto = ["ethrex-crypto/kzg-rs"] - -[[test]] -name = "all" -harness = false - -[profile.release-with-debug] -inherits = "release" -debug = 2 diff --git a/tooling/ef_tests/state/Makefile b/tooling/ef_tests/state/Makefile deleted file mode 100644 index 336e53208b1..00000000000 --- a/tooling/ef_tests/state/Makefile +++ /dev/null @@ -1,174 +0,0 @@ -.PHONY: download-evm-ef-tests clean-evm-ef-tests run-evm-ef-tests test-levm test-levm-nostd-crypto test-revm run-evm-ef-tests flamegraph-run-ef-tests samply-run-ef-tests amsterdam-vectors - -FIXTURES_FILE := .fixtures_url -STATETEST_ARTIFACT := test.tar.gz -VECTORS_DIR := vectors - -TMP_DIR := tmp -TESTS_REPO := $(TMP_DIR)/ethereum-tests - -ETH_TEST_URL := https://github.com/ethereum/tests.git -ETH_TEST_TAG := v17.0 -COMMIT_LEGACY_TESTS_FOR_TAG := b3f67fe - -STATETEST_URL := $(shell cat $(FIXTURES_FILE)) - -AMSTERDAM_FIXTURES_FILE := .fixtures_url_amsterdam -AMSTERDAM_ARTIFACT := amsterdam-tests.tar.gz -AMSTERDAM_URL := $(shell cat $(AMSTERDAM_FIXTURES_FILE)) - -$(STATETEST_ARTIFACT): $(FIXTURES_FILE) - $(MAKE) clean-evm-ef-tests - curl -L -o $(STATETEST_ARTIFACT) $(STATETEST_URL) - -$(VECTORS_DIR): $(STATETEST_ARTIFACT) - $(MAKE) setup-test-dirs - $(MAKE) clone-ef-tests - tar -xzf $(STATETEST_ARTIFACT) --strip-components=2 -C $(VECTORS_DIR)/state_tests fixtures/state_tests - rm -f $(STATETEST_ARTIFACT) - rm -rf $(TMP_DIR) - -$(AMSTERDAM_ARTIFACT): $(AMSTERDAM_FIXTURES_FILE) - curl -L -o $(AMSTERDAM_ARTIFACT) $(AMSTERDAM_URL) - -amsterdam-vectors: $(AMSTERDAM_ARTIFACT) $(VECTORS_DIR) - tar -xzf $(AMSTERDAM_ARTIFACT) --strip-components=2 -C $(VECTORS_DIR)/state_tests fixtures/state_tests/for_amsterdam - -help: ## 📚 Show help for each of the Makefile recipes - @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' - -setup-test-dirs: - mkdir -p $(VECTORS_DIR) - mkdir -p $(VECTORS_DIR)/LegacyTests/Cancun/GeneralStateTests - mkdir -p $(VECTORS_DIR)/GeneralStateTests - mkdir -p $(VECTORS_DIR)/state_tests - -clone-ef-tests: ## 📥 Download Ethereum Tests repository with submodules - mkdir -p $(TMP_DIR) - git clone --recurse-submodules --depth 1 --branch $(ETH_TEST_TAG) $(ETH_TEST_URL) $(TESTS_REPO) - cd $(TESTS_REPO)/LegacyTests && git checkout $(COMMIT_LEGACY_TESTS_FOR_TAG) - cp -r $(TESTS_REPO)/GeneralStateTests/* $(VECTORS_DIR)/GeneralStateTests/ - cp -r $(TESTS_REPO)/LegacyTests/Cancun/GeneralStateTests/* $(VECTORS_DIR)/LegacyTests/Cancun/GeneralStateTests/; - -download-evm-ef-tests: $(VECTORS_DIR) amsterdam-vectors ## 📥 Download and setup state tests fixtures - -clean-evm-ef-tests: ## 🗑️ Clean test vectors and temporary files - rm -rf $(VECTORS_DIR) - rm -rf $(TMP_DIR) - rm -f $(STATETEST_ARTIFACT) $(AMSTERDAM_ARTIFACT) - -refresh-evm-ef-tests: clean-evm-ef-tests download-evm-ef-tests ## Cleans and re-downloads tests, useful when they are outdated! - -run-evm-ef-tests: ## 🏃‍♂️ Run EF Tests - if [ "$(QUIET)" = "true" ]; then \ - time cargo test --quiet --test all --profile release-with-debug -- $(flags) --summary;\ - elif [ "$(DEBUG)" = "true" ]; then \ - time cargo test --test all -- $(flags);\ - else \ - time cargo test --test all --profile release-with-debug -- $(flags);\ - fi - -run-evm-ef-tests-ci: $(VECTORS_DIR) ## 🏃‍♂️ Run EF Tests only with LEVM and without spinner, for CI. - time cargo test -p ef_tests-state --test all --profile release-with-debug -- --summary - -test-levm: $(VECTORS_DIR) - $(MAKE) run-evm-ef-tests flags="--summary" - -test-levm-nostd-crypto: $(VECTORS_DIR) ## 🧪 Run EF state tests with no_std crypto fallbacks - cargo test --test all --profile release-with-debug --no-default-features --features nostd-crypto -- --summary - -test-revm: $(VECTORS_DIR) - $(MAKE) run-evm-ef-tests flags="--revm" - -###### Running Flamegraphs ###### - -SUBDIRS := $(shell find $(VECTORS_DIR)/GeneralStateTests -maxdepth 1 -type d ! -path "$(VECTORS_DIR)/GeneralStateTests" -exec basename {} \;) - -flamegraph-run-ef-tests: ## 🔥 Run EF tests and create a flamegraph per test folder - mkdir -p levm_perfgraphs/flamegraph/ef_tests/state ||: && \ - mkdir -p levm_perfgraphs/flamegraph/ef_tests/revm ||: - $(MAKE) flamegraph-run-ef-tests-revm - $(MAKE) flamegraph-run-ef-tests-levm - -flamegraph-run-ef-tests-revm: - @for dir in $(SUBDIRS); do\ - CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --root \ - --output levm_perfgraphs/flamegraph/ef_tests/revm/$$dir.svg\ - -p ef_tests-state --test all -- --summary --revm --tests $$dir;\ - done - -flamegraph-run-ef-tests-levm: - @for dir in $(SUBDIRS); do\ - CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --root \ - --output levm_perfgraphs/flamegraph/ef_tests/state/$$dir.svg\ - -p ef_tests-state --test all -- --summary --tests $$dir;\ - done - -samply-run-ef-tests: ## ⚡️ Run EF tests and create a samply profiling file per test folder - mkdir -p levm_perfgraphs/samply/ef_tests/state ||: && \ - mkdir -p levm_perfgraphs/samply/ef_tests/revm ||: - $(MAKE) samply-run-ef-tests-revm - $(MAKE) samply-run-ef-tests-levm - -samply-run-ef-tests-revm: - @for dir in $(SUBDIRS); do\ - CARGO_PROFILE_RELEASE_DEBUG=true samply record --save-only \ - -o levm_perfgraphs/samply/ef_tests/revm/prof_$$dir.json \ - cargo test --profile release-with-debug -p ef_tests-state --test all -- --summary --revm --tests $$dir;\ - done - -samply-run-ef-tests-levm: - @for dir in $(SUBDIRS); do\ - CARGO_PROFILE_RELEASE_DEBUG=true samply record --save-only \ - -o levm_perfgraphs/samply/ef_tests/state/prof_$$dir.json \ - cargo test --profile release-with-debug -p ef_tests-state --test all -- --summary --tests $$dir;\ - done - -################ -# FLAMEGRAPHS -################ -define run_flamegraph - # revm - CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph \ - --root --output $(FLAMEGRAPH_DIR)/revm_$(1).svg \ - -p revm_comparison --bin benchmark -- revm $(1) $($(2)) $($(3)) - # levm - CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph \ - --root --output $(FLAMEGRAPH_DIR)/levm_$(1).svg \ - -p revm_comparison --bin benchmark -- levm $(1) $($(2)) $($(3)) -endef - -define run_samply - # revm - CARGO_PROFILE_RELEASE_DEBUG=true samply record --save-only \ - -o $(SAMPLY_DIR)/prof_revm_$(1).json \ - cargo run --profile release-with-debug -p revm_comparison --bin benchmark -- revm $(1) $($(2)) $($(3)) - # levm - CARGO_PROFILE_RELEASE_DEBUG=true samply record --save-only \ - -o $(SAMPLY_DIR)/prof_levm_$(1).json \ - cargo run --profile release-with-debug -p revm_comparison --bin benchmark -- levm $(1) $($(2)) $($(3)) -endef - -FLAMEGRAPH_DIR := levm_perfgraphs/flamegraph/bench -flamegraph-benchmarks: ## 🔥 Run benchmarks and create flamegraph - mkdir -p levm_perfgraphs/flamegraph/bench ||: - $(call run_flamegraph,Fibonacci,REPETITIONS,BENCH_FIB_ITERATIONS) - $(call run_flamegraph,Factorial,REPETITIONS,BENCH_FACT_ITERATIONS) - $(call run_flamegraph,FactorialRecursive,REPETITIONS,BENCH_FACT_ITERATIONS) - $(call run_flamegraph,ManyHashes,REPETITIONS_SLOW,BENCH_HASHES_ITERATIONS) - $(call run_flamegraph,BubbleSort,REPETITIONS_SLOW,BENCH_BUBBLESORT_ITERATIONS) - $(call run_flamegraph,ERC20Approval,REPETITIONS_SLOW,BENCH_APPROVAL_ITERATIONS) - $(call run_flamegraph,ERC20Transfer,REPETITIONS_SLOW,BENCH_TRANSFER_ITERATIONS) - $(call run_flamegraph,ERC20Mint,REPETITIONS_SLOW,BENCH_MINT_ITERATIONS) - -SAMPLY_DIR := levm_perfgraphs/samply/bench -samply-benchmarks: ## ⚡️ Run benchmarks and create samply profiling file - mkdir -p levm_perfgraphs/samply/bench ||: - $(call run_samply,Fibonacci,REPETITIONS,BENCH_FIB_ITERATIONS) - $(call run_samply,Factorial,REPETITIONS,BENCH_FACT_ITERATIONS) - $(call run_samply,FactorialRecursive,REPETITIONS,BENCH_FACT_ITERATIONS) - $(call run_samply,ManyHashes,REPETITIONS_SLOW,BENCH_HASHES_ITERATIONS) - $(call run_samply,BubbleSort,REPETITIONS_SLOW,BENCH_BUBBLESORT_ITERATIONS) - $(call run_samply,ERC20Approval,REPETITIONS_SLOW,BENCH_APPROVAL_ITERATIONS) - $(call run_samply,ERC20Transfer,REPETITIONS_SLOW,BENCH_TRANSFER_ITERATIONS) - $(call run_samply,ERC20Mint,REPETITIONS_SLOW,BENCH_MINT_ITERATIONS) diff --git a/tooling/ef_tests/state/README.md b/tooling/ef_tests/state/README.md deleted file mode 100644 index df8018692f6..00000000000 --- a/tooling/ef_tests/state/README.md +++ /dev/null @@ -1,105 +0,0 @@ -# State Tests - -The state tests are individual transactions not related one to each other that test particular behavior of the EVM. Tests are usually run for multiple forks and the result of execution may vary between forks. -Some [docs](https://ethereum.github.io/execution-spec-tests/main/consuming_tests/state_test/). - -## Running the tests - -```bash -make run-evm-ef-tests flags= -``` -or -```bash -cargo test --package ef_tests-state --test all --release -- -``` - -All tests are first run on levm for the most recent forks (Merge,Shangai,Cancun and Prague), and then any failing tests are re-run on revm. If you want to run the tests with a different set up, you can see how on the following sections. - -## Setting up the tests if you are running flamegraphs - -```bash -make download-evm-ef-tests -``` - -## Reloading tests when outdated - -```bash -make refresh-evm-ef-tests -``` - -### Flags -- `forks`: Forks for which we want to run the tests for. -- `tests`: Tests (.json files) we want to run -- `specific-tests`: For running tests with a specific name. (Sometimes a .json file has multiple tests) -- `summary`: For not doing a re-run with REVM of failed tests after LEVM's run. -- `skip`: For skipping tests -- `verbose`: For more info while running, like tests names being run. -- `revm`: For running EFTests ONLY with REVM. -- `path`: For running particular tests that have their specified paths listed with the tests flag. - - -**Example usage**: -```bash -cargo test --package ef_tests-state --test all --release -- --forks Prague,Cancun --summary --tests push0.json,invalidAddr.json -``` -This runs 2 specific tests with LEVM just for Prague and Cancun. If they fail they are not re-run with REVM. - -```bash -cargo test --package ef_tests-state --test all --release -- --forks Prague,Cancun --summary --paths --tests LegacyTests/Cancun/GeneralStateTests/Shanghai/stEIP3855-push0/push0.json,GeneralStateTests/Shanghai/stEIP3855-push0/push0.json,GeneralStateTests/stBadOpcode/invalidAddr.json,LegacyTests/Cancun/GeneralStateTests/stBadOpcode/invalidAddr.json -``` -This runs the same 2 tests with LEVM as before, but by specifying the files you want to run. If they fail they are not re-run with REVM. - - -Most of the tests that we run are from [this repository](https://github.com/ethereum/tests). We run the `GeneralStateTests` from that repo and also from `LegacyTests`, which is another repository that has snapshots of tests from previous forks. - - -Beware: Sometimes there is a test overlap between the tests folders we have downloaded and we may run the same test for a recent fork (Cancun ATTOW) twice. The impact of this in performance is minimal because we are doing runs for other forks anyway so one more run won't harm, but we should be aware that may lead to an inaccurate test count. We chose not to handle this because it wasn't a huge problem, but be conscious about this. - -## Running all the tests with either levm or revm - -```bash -make test-levm -``` -or -```bash -make test-revm -``` -## Performance metrics - -### To run Flamegraph on the Ethereum Foundation tests - -First install Flamegraph - -```Shell -cargo install flamegraph -``` - -Run the tests - -```Shell -make flamegraph-run-ef-tests -``` - -This will create a folder inside named `levm_ef_test_perfgraphs` you can find the flamegraphs inside the folder `levm_ef_test_perfgraphs/flamegraph` open them with your preferred browser. - -### To run Samply on the Ethereum Foundation tests - -First install Samply - -```Shell -cargo install --locked samply -``` - -Run the tests - -```Shell -make samply-run-ef-tests -``` - -This will create a folder inside named `levm_ef_test_perfgraphs` you can find the flamegraphs inside the folder `levm_ef_test_perfgraphs/samply` run - -```Shell -samply load -``` - -samply will open Firefox with the desired profile file. diff --git a/tooling/ef_tests/state/deserialize.rs b/tooling/ef_tests/state/deserialize.rs deleted file mode 100644 index d2d9a32cafb..00000000000 --- a/tooling/ef_tests/state/deserialize.rs +++ /dev/null @@ -1,448 +0,0 @@ -use crate::types::{ - EFTest, EFTestAccessListItem, EFTestAuthorizationListTuple, EFTestPostValue, EFTests, - TransactionExpectedException, -}; -use bytes::Bytes; -use ethrex_common::{H256, U256, types::Fork}; -use serde::{Deserialize, Deserializer}; -use std::{collections::HashMap, str::FromStr}; - -use crate::types::{EFTestRawTransaction, EFTestTransaction}; - -pub fn deserialize_transaction_expected_exception<'de, D>( - deserializer: D, -) -> Result>, D::Error> -where - D: Deserializer<'de>, -{ - let option: Option = Option::deserialize(deserializer)?; - - if let Some(value) = option { - let exceptions = value - .split('|') - .map(|s| { - match s.trim() { - "TransactionException.INITCODE_SIZE_EXCEEDED" => { - TransactionExpectedException::InitcodeSizeExceeded - } - "TransactionException.NONCE_IS_MAX" => TransactionExpectedException::NonceIsMax, - "TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED" => { - TransactionExpectedException::Type3TxBlobCountExceeded - } - "TransactionException.TYPE_3_TX_ZERO_BLOBS" => { - TransactionExpectedException::Type3TxZeroBlobs - } - "TransactionException.TYPE_3_TX_CONTRACT_CREATION" => { - TransactionExpectedException::Type3TxContractCreation - } - "TransactionException.TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH" => { - TransactionExpectedException::Type3TxInvalidBlobVersionedHash - } - "TransactionException.INTRINSIC_GAS_TOO_LOW" => { - TransactionExpectedException::IntrinsicGasTooLow - } - "TransactionException.INTRINSIC_BELOW_FLOOR_GAS_COST" => { - TransactionExpectedException::IntrinsicGasBelowFloorGasCost - } - "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS" => { - TransactionExpectedException::InsufficientAccountFunds - } - "TransactionException.SENDER_NOT_EOA" => { - TransactionExpectedException::SenderNotEoa - } - "TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS" => { - TransactionExpectedException::PriorityGreaterThanMaxFeePerGas - } - "TransactionException.GAS_ALLOWANCE_EXCEEDED" => { - TransactionExpectedException::GasAllowanceExceeded - } - "TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS" => { - TransactionExpectedException::InsufficientMaxFeePerGas - } - "TransactionException.RLP_INVALID_VALUE" => { - TransactionExpectedException::RlpInvalidValue - } - "TransactionException.GASLIMIT_PRICE_PRODUCT_OVERFLOW" => { - TransactionExpectedException::GasLimitPriceProductOverflow - } - "TransactionException.TYPE_3_TX_PRE_FORK" => { - TransactionExpectedException::Type3TxPreFork - } - "TransactionException.TYPE_4_TX_CONTRACT_CREATION" => { - TransactionExpectedException::Type4TxContractCreation - } - "TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS" => { - TransactionExpectedException::InsufficientMaxFeePerBlobGas - } - "TransactionException.GAS_LIMIT_EXCEEDS_MAXIMUM" => { - TransactionExpectedException::TxMaxGasLimitExceeded - } - _other => TransactionExpectedException::Other, //TODO: Support exceptions that enter here. - } - }) - .collect(); - - Ok(Some(exceptions)) - } else { - Ok(None) - } -} - -pub fn deserialize_ef_post_value_indexes<'de, D>( - deserializer: D, -) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - let aux: HashMap = HashMap::deserialize(deserializer)?; - let indexes = aux - .iter() - .map(|(key, value)| (key.clone(), U256::from(*value))) - .collect(); - Ok(indexes) -} - -pub fn deserialize_hex_bytes<'de, D>(deserializer: D) -> Result -where - D: serde::Deserializer<'de>, -{ - let s = String::deserialize(deserializer)?; - Ok(Bytes::from( - hex::decode(s.trim_start_matches("0x")).map_err(|err| { - serde::de::Error::custom(format!( - "error decoding hex data when deserializing bytes: {err}" - )) - })?, - )) -} - -pub fn deserialize_hex_bytes_vec<'de, D>(deserializer: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - let s = Vec::::deserialize(deserializer)?; - let mut ret = Vec::new(); - for s in s { - ret.push(Bytes::from( - hex::decode(s.trim_start_matches("0x")).map_err(|err| { - serde::de::Error::custom(format!( - "error decoding hex data when deserializing bytes vec: {err}" - )) - })?, - )); - } - Ok(ret) -} - -pub fn deserialize_u256_safe<'de, D>(deserializer: D) -> Result -where - D: serde::Deserializer<'de>, -{ - U256::from_str(String::deserialize(deserializer)?.trim_start_matches("0x:bigint ")).map_err( - |err| { - serde::de::Error::custom(format!( - "error parsing U256 when deserializing U256 safely: {err}" - )) - }, - ) -} - -/// This serializes a hexadecimal string to u64 -pub fn deserialize_u64_safe<'de, D>(deserializer: D) -> Result -where - D: serde::Deserializer<'de>, -{ - u64::from_str_radix( - String::deserialize(deserializer)?.trim_start_matches("0x"), - 16, - ) - .map_err(|err| { - serde::de::Error::custom(format!( - "error parsing U64 when deserializing U64 safely: {err}" - )) - }) -} - -pub fn deserialize_h256_vec_optional_safe<'de, D>( - deserializer: D, -) -> Result>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let s = Option::>::deserialize(deserializer)?; - match s { - Some(s) => { - let mut ret = Vec::new(); - for s in s { - ret.push(H256::from_str(s.trim_start_matches("0x")).map_err(|err| { - serde::de::Error::custom(format!( - "error parsing H256 when deserializing H256 vec optional: {err}" - )) - })?); - } - Ok(Some(ret)) - } - None => Ok(None), - } -} - -pub fn deserialize_access_lists<'de, D>( - deserializer: D, -) -> Result>>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let access_lists: Option>>> = - Option::>>>::deserialize(deserializer)?; - - let mut final_access_lists: Vec> = Vec::new(); - - if let Some(access_lists) = access_lists { - for access_list in access_lists { - // Treat `null` as an empty vector - final_access_lists.push(access_list.unwrap_or_default()); - } - } - - Ok(Some(final_access_lists)) -} - -pub fn deserialize_authorization_lists<'de, D>( - deserializer: D, -) -> Result>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let authorization_list: Option> = - Option::>::deserialize(deserializer)?; - - Ok(authorization_list) -} - -pub fn deserialize_u256_optional_safe<'de, D>(deserializer: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - let s = Option::::deserialize(deserializer)?; - match s { - Some(s) => U256::from_str(s.trim_start_matches("0x:bigint ")) - .map_err(|err| { - serde::de::Error::custom(format!( - "error parsing U256 when deserializing U256 safely: {err}" - )) - }) - .map(Some), - None => Ok(None), - } -} - -pub fn deserialize_u256_vec_safe<'de, D>(deserializer: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - Vec::::deserialize(deserializer)? - .iter() - .map(|s| { - U256::from_str(s.trim_start_matches("0x:bigint ")).map_err(|err| { - serde::de::Error::custom(format!( - "error parsing U256 when deserializing U256 vector safely: {err}" - )) - }) - }) - .collect() -} -pub fn deserialize_u64_vec_safe<'de, D>(deserializer: D) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - Vec::::deserialize(deserializer)? - .iter() - .map(|s| { - u64::from_str_radix(s.trim_start_matches("0x"), 16).map_err(|err| { - serde::de::Error::custom(format!( - "error parsing u64 when deserializing u64 vector safely: {err}" - )) - }) - }) - .collect() -} - -pub fn deserialize_u256_valued_hashmap_safe<'de, D>( - deserializer: D, -) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - HashMap::::deserialize(deserializer)? - .iter() - .map(|(key, value)| { - let key = U256::from_str(key.trim_start_matches("0x:bigint ")).map_err(|err| { - serde::de::Error::custom(format!( - "(key) error parsing U256 when deserializing U256 valued hashmap safely: {err}" - )) - })?; - let value = U256::from_str(value.trim_start_matches("0x:bigint ")).map_err(|err| { - serde::de::Error::custom(format!( - "(value) error parsing U256 when deserializing U256 valued hashmap safely: {err}" - )) - })?; - Ok((key, value)) - }) - .collect() -} - -pub fn deserialize_post<'de, D>( - deserializer: D, -) -> Result>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let post_deserialized = HashMap::>::deserialize(deserializer)?; - let mut post_parsed = HashMap::new(); - for (fork_str, values) in post_deserialized { - let fork = match fork_str.as_str() { - "Frontier" => Fork::Frontier, - "Homestead" => Fork::Homestead, - "Constantinople" => Fork::Constantinople, - "ConstantinopleFix" | "Petersburg" => Fork::Petersburg, - "Istanbul" => Fork::Istanbul, - "Berlin" => Fork::Berlin, - "London" => Fork::London, - "Paris" | "Merge" => Fork::Paris, - "Shanghai" => Fork::Shanghai, - "Cancun" => Fork::Cancun, - "Prague" => Fork::Prague, - "Osaka" => Fork::Osaka, - "Amsterdam" => Fork::Amsterdam, - "Byzantium" => Fork::Byzantium, - "EIP158" => Fork::SpuriousDragon, - "EIP150" => Fork::Tangerine, - other => { - return Err(serde::de::Error::custom(format!( - "Unknown fork name: {other}", - ))); - } - }; - post_parsed.insert(fork, values); - } - - Ok(post_parsed) -} - -impl<'de> Deserialize<'de> for EFTests { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - let mut ef_tests = Vec::new(); - let aux: HashMap> = - HashMap::deserialize(deserializer)?; - - for test_name in aux.keys() { - let test_data = aux - .get(test_name) - .ok_or(serde::de::Error::missing_field("test data value"))?; - - let raw_tx: EFTestRawTransaction = serde_json::from_value( - test_data - .get("transaction") - .ok_or(serde::de::Error::missing_field("transaction"))? - .clone(), - ) - .map_err(|err| { - serde::de::Error::custom(format!( - "error deserializing test \"{test_name}\", \"transaction\" field: {err}" - )) - })?; - - let mut transactions = HashMap::new(); - - // Note that in this order of iteration, in an example tx with 2 datas, 2 gasLimit and 2 values, order would be - // 111, 112, 121, 122, 211, 212, 221, 222 - for (data_id, data) in raw_tx.data.iter().enumerate() { - for (gas_limit_id, gas_limit) in raw_tx.gas_limit.iter().enumerate() { - for (value_id, value) in raw_tx.value.iter().enumerate() { - let tx = EFTestTransaction { - data: data.clone(), - gas_limit: *gas_limit, - gas_price: raw_tx.gas_price, - nonce: raw_tx.nonce, - secret_key: raw_tx.secret_key, - sender: raw_tx.sender, - to: raw_tx.to.clone(), - value: *value, - blob_versioned_hashes: raw_tx - .blob_versioned_hashes - .clone() - .unwrap_or_default(), - max_fee_per_blob_gas: raw_tx.max_fee_per_blob_gas, - max_priority_fee_per_gas: raw_tx.max_priority_fee_per_gas, - max_fee_per_gas: raw_tx.max_fee_per_gas, - access_list: raw_tx - .access_lists - .clone() - .unwrap_or_default() - .get(data_id) - .cloned() - .unwrap_or_default(), - authorization_list: raw_tx.authorization_list.clone(), - }; - transactions.insert((data_id, gas_limit_id, value_id), tx); - } - } - } - - let ef_test = EFTest { - name: test_name.to_owned().to_owned(), - dir: String::default(), - _info: serde_json::from_value( - test_data - .get("_info") - .ok_or(serde::de::Error::missing_field("_info"))? - .clone(), - ) - .map_err(|err| { - serde::de::Error::custom(format!( - "error deserializing test \"{test_name}\", \"_info\" field: {err}" - )) - })?, - env: serde_json::from_value( - test_data - .get("env") - .ok_or(serde::de::Error::missing_field("env"))? - .clone(), - ) - .map_err(|err| { - serde::de::Error::custom(format!( - "error deserializing test \"{test_name}\", \"env\" field: {err}" - )) - })?, - post: serde_json::from_value( - test_data - .get("post") - .ok_or(serde::de::Error::missing_field("post"))? - .clone(), - ) - .map_err(|err| { - serde::de::Error::custom(format!( - "error deserializing test \"{test_name}\", \"post\" field: {err}" - )) - })?, - pre: serde_json::from_value( - test_data - .get("pre") - .ok_or(serde::de::Error::missing_field("pre"))? - .clone(), - ) - .map_err(|err| { - serde::de::Error::custom(format!( - "error deserializing test \"{test_name}\", \"pre\" field: {err}" - )) - })?, - transactions, - }; - ef_tests.push(ef_test); - } - Ok(Self(ef_tests)) - } -} diff --git a/tooling/ef_tests/state/lib.rs b/tooling/ef_tests/state/lib.rs deleted file mode 100644 index 27e09b70957..00000000000 --- a/tooling/ef_tests/state/lib.rs +++ /dev/null @@ -1,6 +0,0 @@ -mod deserialize; -pub mod parser; -mod report; -pub mod runner; -pub mod types; -mod utils; diff --git a/tooling/ef_tests/state/parser.rs b/tooling/ef_tests/state/parser.rs deleted file mode 100644 index ca356115c41..00000000000 --- a/tooling/ef_tests/state/parser.rs +++ /dev/null @@ -1,295 +0,0 @@ -use crate::{ - report::format_duration_as_mm_ss, - runner::EFTestRunnerOptions, - types::{EFTest, EFTests}, -}; -use colored::Colorize; -use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; -use simd_json; -use std::{fs::DirEntry, path::PathBuf}; - -#[derive(Debug, thiserror::Error)] -pub enum EFTestParseError { - #[error("Failed to read directory: {0}")] - FailedToReadDirectory(String), - #[error("Failed to read file: {0}")] - FailedToReadFile(String), - #[error("Failed to get file type: {0}")] - FailedToGetFileType(String), - #[error("Failed to parse test file: {0}")] - FailedToParseTestFile(String), -} - -const IGNORED_TESTS: &[&str] = &[ - "ValueOverflow.json", // Skip because it tries to deserialize number > U256::MAX - "ValueOverflowParis.json", // Skip because it tries to deserialize number > U256::MAX - // Skip because they take too long to run: - "static_Call50000_sha256.json", - "CALLBlake2f_MaxRounds.json", - "loopMul.json", -]; - -// One .json can have multiple tests, sometimes we want to skip one of those. -pub const SPECIFIC_IGNORED_TESTS: [&str; 1] = [ - "test_set_code_to_non_empty_storage[fork_Prague-state_test-zero_nonce]", // Skip because EIP-7702 has changed. See https://github.com/ethereum/EIPs/pull/9710 -]; - -// This constant is used as the reference from which to keep the relative path of the tests. -const START_DIR_NAME: &str = "vectors"; - -pub fn parse_ef_tests(opts: &EFTestRunnerOptions) -> Result, EFTestParseError> { - let parsing_time = std::time::Instant::now(); - let cargo_manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let ef_general_state_tests_path = cargo_manifest_dir.join("vectors"); - println!("{}", "Parsing EF Tests".bold().cyan()); - - let mut tests = Vec::new(); - if opts.paths { - for test_path in &opts.tests { - let mut full_path = ef_general_state_tests_path.clone(); - full_path.push(test_path); - let tests_from_files = parse_ef_test_file(full_path, opts)?; - tests.extend(tests_from_files); - } - } else { - let tests_dir: Vec<_> = std::fs::read_dir(ef_general_state_tests_path.clone()) - .map_err(|err| { - EFTestParseError::FailedToReadDirectory(format!( - "{:?}: {err}", - ef_general_state_tests_path.file_name() - )) - })? - .flatten() - .collect(); - - let mut directory_tests_results = Vec::new(); - tests_dir - .into_par_iter() - .map(|test_dir| parse_ef_test_dir(test_dir, opts)) - .collect_into_vec(&mut directory_tests_results); - - tests = directory_tests_results - .into_iter() - .collect::, _>>()? - .into_iter() - .flatten() - .collect(); - } - - println!( - "Parsed EF Tests in {}", - format_duration_as_mm_ss(parsing_time.elapsed()) - ); - - Ok(tests) -} - -pub fn parse_ef_test_dir( - test_dir: DirEntry, - opts: &EFTestRunnerOptions, -) -> Result, EFTestParseError> { - let mut directory_tests_results = Vec::new(); - - let tests_dir: Vec<_> = std::fs::read_dir(test_dir.path()) - .map_err(|err| { - EFTestParseError::FailedToReadDirectory(format!("{:?}: {err}", test_dir.file_name())) - })? - .flatten() - .collect(); - - tests_dir - .into_par_iter() - .map( - |test: DirEntry| -> Result>, EFTestParseError> { - if test - .file_type() - .map_err(|err| { - EFTestParseError::FailedToGetFileType(format!( - "{:?}: {err}", - test.file_name() - )) - })? - .is_dir() - { - let sub_directory_tests = parse_ef_test_dir(test, opts)?; - return Ok(Some(sub_directory_tests)); - } - // Skip non-JSON files. - if test.path().extension().is_some_and(|ext| ext != "json") - | test.path().extension().is_none() - { - return Ok(None); - } - // Skip ignored tests - if test - .path() - .file_name() - .is_some_and(|name| IGNORED_TESTS.contains(&name.to_str().unwrap_or(""))) - { - return Ok(None); - } - - // Skip tests that are not in the list of tests to run. - if !opts.tests.is_empty() - && !opts - .tests - .contains(&test_dir.file_name().to_str().unwrap().to_owned()) - && !opts.tests.contains( - &test - .path() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_owned(), - ) - { - return Ok(None); - } - - // Skips all tests in a particular directory. - if opts - .skip - .contains(&test_dir.file_name().to_str().unwrap().to_owned()) - { - println!( - "Skipping test {:?} as it is in the folder of tests to skip", - test.path().file_name().unwrap() - ); - return Ok(None); - } - - // Skip tests by name (with .json extension) - if opts.skip.contains( - &test - .path() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_owned(), - ) { - println!( - "Skipping test file {:?} as it is in the list of tests to skip", - test.path().file_name().unwrap() - ); - return Ok(None); - } - - let test_file = std::fs::File::open(test.path()).map_err(|err| { - EFTestParseError::FailedToReadFile(format!("{:?}: {err}", test.path())) - })?; - let mut tests: EFTests = simd_json::from_reader(test_file).map_err(|err| { - EFTestParseError::FailedToParseTestFile(format!( - "{:?} parse error: {err}", - test.path() - )) - })?; - - for test in tests.0.iter_mut() { - test.dir = test_dir.path().to_str().unwrap().to_string(); - } - - // We only want to include tests that have post states from the specified forks in EFTestsRunnerOptions. - if let Some(forks) = &opts.forks { - for test in tests.0.iter_mut() { - let test_forks_numbers: Vec = - forks.iter().map(|fork| *fork as u8).collect(); - - test.post.forks = test - .post - .forks - .iter() - .filter(|a| test_forks_numbers.contains(&(*a.0 as u8))) - .map(|(k, v)| (*k, v.clone())) - .collect(); - } - - tests.0.retain(|test| !test.post.forks.is_empty()); - } - - Ok(Some(tests.0)) - }, - ) - .collect_into_vec(&mut directory_tests_results); - - let directory_tests: Vec = directory_tests_results - .into_iter() - .filter_map(|x| x.transpose()) - .collect::, _>>()? - .into_iter() - .flatten() - .collect(); - - let mut parsed_directory_tests = Vec::new(); - - for test in directory_tests.iter() { - let relative_path = get_test_relative_path(PathBuf::from(&test.dir)); - if !parsed_directory_tests.contains(&relative_path) { - parsed_directory_tests.push(relative_path); - } - } - - print_parsed_directories(parsed_directory_tests); - - Ok(directory_tests) -} - -/// Given the full path of a json test file, returns its path relative to the vectors directory. -/// Panics if the file is not in the vectors directory. -pub fn get_test_relative_path(full_path: PathBuf) -> String { - let mut path_prefix = PathBuf::new(); - - for dir in full_path.components() { - if dir.as_os_str().to_str().unwrap() == START_DIR_NAME { - break; - } - path_prefix.push(dir); - } - path_prefix.push(PathBuf::from(START_DIR_NAME)); - - full_path - .strip_prefix(path_prefix) - .unwrap() - .to_str() - .unwrap() - .to_string() -} - -fn print_parsed_directories(parsed_directory_tests: Vec) { - for dir in parsed_directory_tests { - println!("Parsed directory {}", dir); - } -} - -fn parse_ef_test_file( - full_path: PathBuf, - opts: &EFTestRunnerOptions, -) -> Result, EFTestParseError> { - let test_file = std::fs::File::open(&full_path) - .map_err(|err| EFTestParseError::FailedToReadFile(format!("{:?}: {err}", full_path)))?; - let mut tests_in_file: EFTests = simd_json::from_reader(test_file).map_err(|err| { - EFTestParseError::FailedToParseTestFile(format!("{:?} parse error: {err}", full_path)) - })?; - for test in tests_in_file.0.iter_mut() { - test.dir = full_path.to_str().unwrap().to_string(); - } - - // We only want to include tests that have post states from the specified forks in EFTestsRunnerOptions. - if let Some(forks) = &opts.forks { - for test in tests_in_file.0.iter_mut() { - let test_forks_numbers: Vec = forks.iter().map(|fork| *fork as u8).collect(); - - test.post.forks = test - .post - .forks - .iter() - .filter(|a| test_forks_numbers.contains(&(*a.0 as u8))) - .map(|(k, v)| (*k, v.clone())) - .collect(); - } - - tests_in_file.0.retain(|test| !test.post.forks.is_empty()); - } - Ok(tests_in_file.0) -} diff --git a/tooling/ef_tests/state/report.rs b/tooling/ef_tests/state/report.rs deleted file mode 100644 index a475ea165b2..00000000000 --- a/tooling/ef_tests/state/report.rs +++ /dev/null @@ -1,958 +0,0 @@ -use crate::parser::get_test_relative_path; -use crate::runner::revm_db::RevmError; -use crate::runner::{EFTestRunnerError, InternalError}; -use crate::types::EFTestInfo; -use colored::Colorize; -use ethrex_common::{ - Address, H256, - types::{Account, AccountUpdate, Fork}, -}; -use ethrex_levm::account::LevmAccount; -use ethrex_levm::errors::{ExecutionReport, TxResult, VMError}; -use itertools::Itertools; -use revm::context::result::{EVMError as REVMError, ExecutionResult as RevmExecutionResult}; -use serde::{Deserialize, Serialize}; -use spinoff::{Color, Spinner, spinners::Dots}; -use std::{ - collections::{BTreeMap, HashMap, HashSet}, - fmt::{self, Display}, - path::PathBuf, - time::Duration, -}; - -pub const LEVM_EF_TESTS_SUMMARY_SLACK_FILE_PATH: &str = "./levm_ef_tests_summary_slack.txt"; -pub const LEVM_EF_TESTS_SUMMARY_GITHUB_FILE_PATH: &str = "./levm_ef_tests_summary_github.txt"; -pub const EF_TESTS_CACHE_FILE_PATH: &str = "./levm_ef_tests_cache.json"; - -pub type TestVector = (usize, usize, usize); - -pub fn progress(reports: &[EFTestReport], time: Duration) -> String { - format!( - "\r{}: {} {} {} - {}", - "Ethereum Foundation Tests".bold(), - format!( - "{} passed", - reports.iter().filter(|report| report.passed()).count() - ) - .green() - .bold(), - format!( - "{} failed", - reports.iter().filter(|report| !report.passed()).count() - ) - .red() - .bold(), - format!("{} total run", reports.len()).blue().bold(), - format_duration_as_mm_ss(time) - ) -} - -pub fn format_duration_as_mm_ss(duration: Duration) -> String { - let total_seconds = duration.as_secs(); - let minutes = total_seconds / 60; - let seconds = total_seconds % 60; - format!("{minutes:02}:{seconds:02}") -} - -pub fn write(reports: &[EFTestReport]) -> Result { - let report_file_path = PathBuf::from("./levm_ef_tests_report.txt"); - let failed_test_reports = EFTestsReport( - reports - .iter() - .filter(|&report| !report.passed()) - .cloned() - .collect(), - ); - std::fs::write( - "./levm_ef_tests_report.txt", - failed_test_reports.to_string(), - ) - .map_err(|err| { - EFTestRunnerError::Internal(InternalError::MainRunnerInternal(format!( - "Failed to write report to file: {err}" - ))) - })?; - Ok(report_file_path) -} - -pub fn cache(reports: &[EFTestReport]) -> Result { - let cache_file_path = PathBuf::from(EF_TESTS_CACHE_FILE_PATH); - let cache = serde_json::to_string_pretty(&reports).map_err(|err| { - EFTestRunnerError::Internal(InternalError::MainRunnerInternal(format!( - "Failed to serialize cache: {err}" - ))) - })?; - std::fs::write(&cache_file_path, cache).map_err(|err| { - EFTestRunnerError::Internal(InternalError::MainRunnerInternal(format!( - "Failed to write cache to file: {err}" - ))) - })?; - Ok(cache_file_path) -} - -pub fn load() -> Result, EFTestRunnerError> { - let mut reports_loading_spinner = - Spinner::new(Dots, "Loading reports...".to_owned(), Color::Cyan); - match std::fs::read_to_string(EF_TESTS_CACHE_FILE_PATH).ok() { - Some(cache) => { - reports_loading_spinner.success("Reports loaded"); - serde_json::from_str(&cache).map_err(|err| { - EFTestRunnerError::Internal(InternalError::MainRunnerInternal(format!( - "Cache exists but there was an error loading it: {err}" - ))) - }) - } - None => { - reports_loading_spinner.success("No cache found"); - Ok(Vec::default()) - } - } -} - -pub fn summary_for_slack(reports: &[EFTestReport]) -> String { - let total_passed = total_fork_test_passed(reports); - let total_run = total_fork_test_run(reports); - let success_percentage = (total_passed as f64 / total_run as f64) * 100.0; - format!( - r#"{{ - "blocks": [ - {{ - "type": "header", - "text": {{ - "type": "plain_text", - "text": "Daily LEVM EF Tests Run Report" - }} - }}, - {{ - "type": "divider" - }}, - {{ - "type": "section", - "text": {{ - "type": "mrkdwn", - "text": "*Summary*: {total_passed}/{total_run} ({success_percentage:.2}%)\n\n{}\n{}\n{}\n{}\n" - }} - }} - ] -}}"#, - fork_summary_for_slack(reports, Fork::Prague), - fork_summary_for_slack(reports, Fork::Cancun), - fork_summary_for_slack(reports, Fork::Shanghai), - fork_summary_for_slack(reports, Fork::Paris), - ) -} - -fn fork_summary_for_slack(reports: &[EFTestReport], fork: Fork) -> String { - let fork_str: &str = fork.into(); - let (fork_tests, fork_passed_tests, fork_success_percentage) = fork_statistics(reports, fork); - format!(r#"*{fork_str}:* {fork_passed_tests}/{fork_tests} ({fork_success_percentage:.2}%)"#) -} - -pub fn write_summary_for_slack(reports: &[EFTestReport]) -> Result { - let summary_file_path = PathBuf::from(LEVM_EF_TESTS_SUMMARY_SLACK_FILE_PATH); - std::fs::write( - LEVM_EF_TESTS_SUMMARY_SLACK_FILE_PATH, - summary_for_slack(reports), - ) - .map_err(|err| { - EFTestRunnerError::Internal(InternalError::MainRunnerInternal(format!( - "Failed to write summary to file: {err}" - ))) - })?; - Ok(summary_file_path) -} - -pub fn summary_for_github(reports: &[EFTestReport]) -> String { - let total_passed = total_fork_test_passed(reports); - let total_run = total_fork_test_run(reports); - let success_percentage = (total_passed as f64 / total_run as f64) * 100.0; - format!( - r#"Summary: {total_passed}/{total_run} ({success_percentage:.2}%)\n\n{}\n{}\n{}\n{}\n"#, - fork_summary_for_github(reports, Fork::Prague), - fork_summary_for_github(reports, Fork::Cancun), - fork_summary_for_github(reports, Fork::Shanghai), - fork_summary_for_github(reports, Fork::Paris), - ) -} - -fn fork_summary_for_github(reports: &[EFTestReport], fork: Fork) -> String { - let fork_str: &str = fork.into(); - let (fork_tests, fork_passed_tests, fork_success_percentage) = fork_statistics(reports, fork); - format!("{fork_str}: {fork_passed_tests}/{fork_tests} ({fork_success_percentage:.2}%)") -} - -pub fn write_summary_for_github(reports: &[EFTestReport]) -> Result { - let summary_file_path = PathBuf::from(LEVM_EF_TESTS_SUMMARY_GITHUB_FILE_PATH); - std::fs::write( - LEVM_EF_TESTS_SUMMARY_GITHUB_FILE_PATH, - summary_for_github(reports), - ) - .map_err(|err| { - EFTestRunnerError::Internal(InternalError::MainRunnerInternal(format!( - "Failed to write summary to file: {err}" - ))) - })?; - Ok(summary_file_path) -} - -pub fn summary_for_shell(reports: &[EFTestReport]) -> String { - let total_passed = total_fork_test_passed(reports); - let total_run = total_fork_test_run(reports); - let success_percentage = (total_passed as f64 / total_run as f64) * 100.0; - format!( - "{} {}/{total_run} ({success_percentage:.2}%)\n\n{}\n{}\n{}\n{}\n{}\n\n\n{}\n", - "Summary:".bold(), - if total_passed == total_run { - format!("{total_passed}").green() - } else if total_passed > 0 { - format!("{total_passed}").yellow() - } else { - format!("{total_passed}").red() - }, - // NOTE: Keep in order, see the Fork Enum to check - // NOTE: Uncomment the summaries if EF tests for those specific forks exist. - fork_summary_shell(reports, Fork::Osaka), - fork_summary_shell(reports, Fork::Prague), - fork_summary_shell(reports, Fork::Cancun), - fork_summary_shell(reports, Fork::Shanghai), - fork_summary_shell(reports, Fork::Paris), - test_dir_summary_for_shell(reports), - ) -} - -fn fork_summary_shell(reports: &[EFTestReport], fork: Fork) -> String { - let fork_str: &str = fork.into(); - let (fork_tests, fork_passed_tests, fork_success_percentage) = fork_statistics(reports, fork); - format!( - "{}: {}/{fork_tests} ({fork_success_percentage:.2}%)", - fork_str.bold(), - if fork_passed_tests == fork_tests { - format!("{fork_passed_tests}").green() - } else if fork_passed_tests > 0 { - format!("{fork_passed_tests}").yellow() - } else { - format!("{fork_passed_tests}").red() - }, - ) -} - -fn fork_statistics(reports: &[EFTestReport], fork: Fork) -> (usize, usize, f64) { - let fork_tests = reports - .iter() - .filter(|report| report.fork_results.contains_key(&fork)) - .count(); - let fork_passed_tests = reports - .iter() - .filter(|report| match report.fork_results.get(&fork) { - Some(result) => result.failed_vectors.is_empty(), - None => false, - }) - .count(); - let fork_success_percentage = (fork_passed_tests as f64 / fork_tests as f64) * 100.0; - (fork_tests, fork_passed_tests, fork_success_percentage) -} - -pub fn test_dir_summary_for_shell(reports: &[EFTestReport]) -> String { - let mut test_dirs_summary = String::new(); - reports - .iter() - .into_group_map_by(|report| report.dir.clone()) - .iter() - .map(|(dir, reports)| { - let total_passed = - total_fork_test_passed(&reports.iter().map(|&r| r.clone()).collect::>()); - let total_run = - total_fork_test_run(&reports.iter().map(|&r| r.clone()).collect::>()); - if total_passed == 0 { - (dir, reports, 0) - } else if total_passed > 0 && total_passed < total_run { - (dir, reports, 1) - } else { - (dir, reports, 2) - } - }) - .sorted_by_key(|(_dir, _reports, weight)| *weight) - .rev() - .for_each(|(dir, reports, _weight)| { - let total_passed = - total_fork_test_passed(&reports.iter().map(|&r| r.clone()).collect::>()); - let total_run = - total_fork_test_run(&reports.iter().map(|&r| r.clone()).collect::>()); - let success_percentage = (total_passed as f64 / total_run as f64) * 100.0; - let test_dir_summary = format!( - "{}: {}/{total_run} ({success_percentage:.2}%)\n", - get_test_relative_path(PathBuf::from(dir)).bold(), - if total_passed == total_run { - format!("{total_passed}").green() - } else if total_passed > 0 { - format!("{total_passed}").yellow() - } else { - format!("{total_passed}").red() - }, - ); - test_dirs_summary.push_str(&test_dir_summary); - }); - test_dirs_summary -} - -#[derive(Debug, Default, Clone)] -pub struct EFTestsReport(pub Vec); - -pub fn total_fork_test_passed(reports: &[EFTestReport]) -> usize { - let mut tests_passed = 0; - for report in reports { - for fork_result in report.fork_results.values() { - if fork_result.failed_vectors.is_empty() { - tests_passed += 1; - } - } - } - tests_passed -} - -pub fn total_fork_test_run(reports: &[EFTestReport]) -> usize { - let mut tests_run = 0; - for report in reports { - tests_run += report.fork_results.len(); - } - tests_run -} - -impl Display for EFTestsReport { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let total_passed = total_fork_test_passed(&self.0); - let total_run = total_fork_test_run(&self.0); - writeln!(f, "Summary: {total_passed}/{total_run}",)?; - writeln!(f)?; - writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Osaka))?; - writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Prague))?; - writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Cancun))?; - writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Shanghai))?; - writeln!(f, "{}", fork_summary_shell(&self.0, Fork::Paris))?; - writeln!(f)?; - writeln!(f, "Passed tests:")?; - writeln!(f)?; - writeln!(f, "{}", test_dir_summary_for_shell(&self.0))?; - writeln!(f)?; - writeln!(f, "Failed tests:")?; - writeln!(f)?; - for report in self.0.iter() { - if report.passed() { - continue; - } - writeln!(f, "{} \n{}", "Test:".bold(), report)?; - writeln!(f)?; - for (fork, result) in &report.fork_results { - if result.failed_vectors.is_empty() { - continue; - } - writeln!(f, "\tFork: {fork:?}")?; - for (failed_vector, error) in &result.failed_vectors { - writeln!( - f, - "\t\tFailed Vector: (data_index: {}, gas_limit_index: {}, value_index: {})", - failed_vector.0, failed_vector.1, failed_vector.2 - )?; - writeln!(f, "\t\t\tError: {error}")?; - if let Some(re_run_report) = &report.re_run_report { - if let Some(execution_report) = - re_run_report.execution_report.get(&(*failed_vector, *fork)) - { - if let Some((levm_result, revm_result)) = - &execution_report.execution_result_mismatch - { - writeln!( - f, - "\t\t\tExecution result mismatch: LEVM: {levm_result:?}, REVM: {revm_result:?}", - )?; - } - if let Some((levm_gas_used, revm_gas_used)) = - &execution_report.gas_used_mismatch - { - writeln!( - f, - "\t\t\tGas used mismatch: LEVM: {levm_gas_used}, REVM: {revm_gas_used} (diff: {})", - levm_gas_used.abs_diff(*revm_gas_used) - )?; - } - if let Some((levm_gas_refunded, revm_gas_refunded)) = - &execution_report.gas_refunded_mismatch - { - writeln!( - f, - "\t\t\tGas refunded mismatch: LEVM: {levm_gas_refunded}, REVM: {revm_gas_refunded} (diff: {})", - levm_gas_refunded.abs_diff(*revm_gas_refunded) - )?; - } - if let Some((levm_logs, revm_logs)) = &execution_report.logs_mismatch { - writeln!(f, "\t\t\tLogs mismatch:")?; - writeln!(f, "\t\t\t\tLevm Logs: ")?; - let levm_log_report = levm_logs.iter().map(|log| format!( - "\t\t\t\t Log {{ address: {:#x}, topic: {:?}, data: {:#x} }} \n", - log.address, log.topics, log.data - )) - .fold(String::new(), |acc, arg| acc + arg.as_str()); - writeln!(f, "{levm_log_report}")?; - writeln!(f, "\t\t\t\tRevm Logs: ")?; - let revm_log_report = revm_logs - .iter() - .map(|log| format!("\t\t\t\t {log:?} \n")) - .fold(String::new(), |acc, arg| acc + arg.as_str()); - writeln!(f, "{revm_log_report}")?; - } - if let Some((levm_result, revm_error)) = - &execution_report.re_runner_error - { - writeln!( - f, - "\t\t\tRe-run error: LEVM: {levm_result:?}, REVM: {revm_error}", - )?; - } - } - - if let Some(account_update) = re_run_report - .account_updates_report - .get(&(*failed_vector, *fork)) - { - writeln!(f, "\t\t\t{}", &account_update.to_string())?; - } else { - writeln!( - f, - "\t\t\tNo account updates report found. Account update reports are only generated for tests that failed at the post-state validation stage." - )?; - } - } else { - writeln!( - f, - "\t\t\tNo re-run report found. Re-run reports are only generated for tests that failed at the post-state validation stage." - )?; - } - writeln!(f)?; - } - } - } - Ok(()) - } -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct EFTestReport { - pub name: String, - pub dir: String, - pub description: String, - pub url: String, - pub reference_spec: String, - pub test_hash: H256, - pub re_run_report: Option, - pub fork_results: HashMap, -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct EFTestReportForkResult { - pub skipped: bool, - pub failed_vectors: HashMap, -} - -impl EFTestReport { - pub fn new(name: String, dir: String, info: EFTestInfo, test_hash: H256) -> Self { - EFTestReport { - name, - dir, - description: info - .description - .unwrap_or("No description provided by this tests".to_string()), - url: info - .url - .unwrap_or("No url provided by this tests".to_string()), - reference_spec: info - .reference_spec - .unwrap_or("No reference spec provided by this tests".to_string()), - test_hash, - re_run_report: None, - fork_results: HashMap::new(), - } - } - - pub fn register_re_run_report(&mut self, re_run_report: TestReRunReport) { - self.re_run_report = Some(re_run_report); - } - - pub fn register_fork_result( - &mut self, - fork: Fork, - ef_test_report_fork: EFTestReportForkResult, - ) { - self.fork_results.insert(fork, ef_test_report_fork); - } - - pub fn passed(&self) -> bool { - self.fork_results - .values() - .all(|fork_result| fork_result.failed_vectors.is_empty()) - } -} - -impl Display for EFTestReport { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut json_name = String::from(""); //In some cases there are more than one tests per file, so the name of the tests and the name of the file are different. - if self.name.contains("::") { - json_name = self.name.clone().split("::").collect::>()[1] - .split("[") - .collect::>()[0] - .strip_prefix("test") - .unwrap_or("") - .to_owned() - + ".json"; - } - writeln!(f, "Test name: {}", self.name)?; - writeln!(f, "Test path: {}", self.dir.clone() + &json_name)?; - writeln!(f)?; - writeln!(f, "Test description: {}", self.description)?; - writeln!(f)?; - writeln!( - f, - "Note: The following links may help when debugging `ef-tests`:" - )?; - writeln!( - f, - "- https://ethereum-tests.readthedocs.io/en/latest/test_types/gstate_tests.html#" - )?; - writeln!(f, "- Test reference spec: {}", self.reference_spec)?; - writeln!(f, "- Test url: {}", self.url)?; - Ok(()) - } -} - -impl EFTestReportForkResult { - pub fn new() -> Self { - Self { - skipped: false, - failed_vectors: HashMap::new(), - } - } - - pub fn register_unexpected_execution_failure( - &mut self, - error: VMError, - failed_vector: TestVector, - ) { - self.failed_vectors.insert( - failed_vector, - EFTestRunnerError::ExecutionFailedUnexpectedly(error), - ); - } - - pub fn register_vm_initialization_failure( - &mut self, - reason: String, - failed_vector: TestVector, - ) { - self.failed_vectors.insert( - failed_vector, - EFTestRunnerError::VMInitializationFailed(reason), - ); - } - - pub fn register_pre_state_validation_failure( - &mut self, - reason: String, - failed_vector: TestVector, - ) { - self.failed_vectors.insert( - failed_vector, - EFTestRunnerError::FailedToEnsurePreState(reason), - ); - } - - pub fn register_post_state_validation_failure( - &mut self, - transaction_report: ExecutionReport, - reason: String, - failed_vector: TestVector, - levm_cache: BTreeMap, - ) { - self.failed_vectors.insert( - failed_vector, - EFTestRunnerError::FailedToEnsurePostState( - Box::new(transaction_report), - reason, - levm_cache, - ), - ); - } - - pub fn register_post_state_validation_error_mismatch( - &mut self, - reason: String, - failed_vector: TestVector, - ) { - self.failed_vectors.insert( - failed_vector, - EFTestRunnerError::ExpectedExceptionDoesNotMatchReceived(reason), - ); - } - - pub fn register_error_on_reverting_levm_state( - &mut self, - reason: String, - failed_vector: TestVector, - ) { - self.failed_vectors.insert( - failed_vector, - EFTestRunnerError::FailedToRevertLEVMState(reason), - ); - } - - pub fn register_failed_vector(&mut self, vector: TestVector, error: EFTestRunnerError) { - self.failed_vectors.insert(vector, error); - } -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct ComparisonReport { - pub expected_post_state_root: H256, - pub levm_post_state_root: H256, - pub revm_post_state_root: H256, - pub initial_accounts: HashMap, - pub levm_account_updates: Vec, - pub revm_account_updates: Vec, - pub levm_updated_accounts_only: HashSet
, - pub revm_updated_accounts_only: HashSet
, - pub shared_updated_accounts: HashSet
, -} - -impl fmt::Display for ComparisonReport { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.revm_post_state_root != self.expected_post_state_root { - writeln!(f, "\n\t\t\tWARNING: REVM fails this test")?; - if self.levm_post_state_root != self.revm_post_state_root { - writeln!(f, "\t\t\tPost-state root mismatch between LEVM and REVM\n")?; - } else { - writeln!(f, "\t\t\tSame Post-state root in LEVM and REVM\n")?; - } - } else { - writeln!(f, "\n\t\t\tREVM passes this test")?; - } - - let all_updated_accounts = &(&self.levm_updated_accounts_only - | &self.revm_updated_accounts_only) - | &self.shared_updated_accounts; - - for address in all_updated_accounts { - writeln!(f, "\n\t\t\t{address:#x}:")?; - - let account_updates_for_address: Vec<(String, AccountUpdate)> = - if self.levm_updated_accounts_only.contains(&address) { - writeln!(f, "\t\t\t\tWas updated in LEVM but not in REVM")?; - self.levm_account_updates - .clone() - .iter() - .filter(|account_update| account_update.address == address) - .map(|account_update| ("LEVM".to_string(), account_update.clone())) - .collect() - } else if self.revm_updated_accounts_only.contains(&address) { - writeln!(f, "\t\t\t\tWas updated in REVM but not in LEVM")?; - self.revm_account_updates - .clone() - .iter() - .filter(|account_update| account_update.address == address) - .map(|account_update| ("REVM".to_string(), account_update.clone())) - .collect() - } else { - writeln!(f, "\t\t\t\tWas updated in both LEVM and REVM")?; - [ - self.revm_account_updates - .clone() - .iter() - .filter(|account_update| account_update.address == address) - .map(|account_update| ("REVM".to_string(), account_update.clone())) - .collect::>(), - self.levm_account_updates - .clone() - .iter() - .filter(|account_update| account_update.address == address) - .map(|account_update| ("LEVM".to_string(), account_update.clone())) - .collect::>(), - ] - .concat() - }; - - // Account before Tx execution - let base_account = self - .initial_accounts - .get(&address) - .cloned() - .unwrap_or_default(); - - for (vm, account_update) in &account_updates_for_address { - writeln!(f, "\t\t\t\t{vm} Account Update:")?; - - if account_update.removed { - writeln!(f, "\t\t\t\t\tAccount was removed")?; - continue; - } - - // Display changes in Account Info - if let Some(new_info) = &account_update.info { - writeln!( - f, - "\t\t\t\t\tNonce: {} -> {}", - base_account.info.nonce, new_info.nonce - )?; - writeln!( - f, - "\t\t\t\t\tBalance: {} -> {}", - base_account.info.balance, new_info.balance - )?; - - if base_account.info.code_hash != new_info.code_hash { - writeln!( - f, - "\t\t\t\t\tCode: {} -> {}", - if base_account.code.bytecode.is_empty() { - "empty".to_string() - } else { - hex::encode(&base_account.code.bytecode) - }, - account_update - .code - .as_ref() - .map(|code| if code.bytecode.is_empty() { - "empty".to_string() - } else { - hex::encode(&code.bytecode) - }) - .expect("If code hash changed then 'code' shouldn't be None.") - )?; - } - } - - for (key, value) in &account_update.added_storage { - let initial_value = base_account.storage.get(key).cloned().unwrap_or_default(); - writeln!( - f, - "\t\t\t\t\tStorage slot: {key:#x}: {initial_value} -> {value}", - )?; - } - } - - if self.shared_updated_accounts.contains(&address) { - let levm_account_update = account_updates_for_address - .iter() - .find(|(vm, _)| vm == "LEVM") - .map(|(_, update)| update) - .expect("LEVM account update not found"); - let revm_account_update = account_updates_for_address - .iter() - .find(|(vm, _)| vm == "REVM") - .map(|(_, update)| update) - .expect("REVM account update not found"); - - if levm_account_update == revm_account_update { - writeln!(f, "\t\t\t\tNo differences between updates")?; - continue; - } - - if levm_account_update.removed != revm_account_update.removed { - writeln!( - f, - "\t\t\t\tAccount removal mismatch: LEVM: {}, REVM: {}", - levm_account_update.removed, revm_account_update.removed - )?; - } - - if levm_account_update.info != revm_account_update.info { - match (&levm_account_update.info, &revm_account_update.info) { - (Some(levm_info), Some(revm_info)) => { - if levm_info.nonce != revm_info.nonce { - writeln!( - f, - "\t\t\t\tNonce mismatch: LEVM: {}, REVM: {}", - levm_info.nonce, revm_info.nonce - )?; - } - if levm_info.balance != revm_info.balance { - writeln!( - f, - "\t\t\t\tBalance mismatch: LEVM: {}, REVM: {}", - levm_info.balance, revm_info.balance - )?; - } - } - (Some(levm_info), None) => { - writeln!( - f, - "\t\t\t\tLEVM has account info but REVM does not: Nonce: {}, Balance: {}", - levm_info.nonce, levm_info.balance - )?; - } - (None, Some(revm_info)) => { - writeln!( - f, - "\t\t\t\tREVM has account info but LEVM does not: Nonce: {}, Balance: {}", - revm_info.nonce, revm_info.balance - )?; - } - (None, None) => { - // No account info in either LEVM or REVM, nothing to report. - } - } - } - - // Compare all storage changes between LEVM and REVM. - let all_keys: HashSet<_> = levm_account_update - .added_storage - .keys() - .chain(revm_account_update.added_storage.keys()) - .collect(); - - for key in all_keys { - let levm_value = levm_account_update - .added_storage - .get(key) - .cloned() - .unwrap_or_default(); - let revm_value = revm_account_update - .added_storage - .get(key) - .cloned() - .unwrap_or_default(); - - if levm_value != revm_value { - writeln!( - f, - "\t\t\t\tStorage slot mismatch at key {key:#x}: LEVM: {levm_value}, REVM: {revm_value}", - )?; - } - } - } - } - - Ok(()) - } -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct TestReRunExecutionReport { - pub execution_result_mismatch: Option<(TxResult, RevmExecutionResult)>, - pub gas_used_mismatch: Option<(u64, u64)>, - pub gas_refunded_mismatch: Option<(u64, u64)>, - pub logs_mismatch: Option<(Vec, Vec)>, - pub re_runner_error: Option<(TxResult, String)>, -} - -#[derive(Debug, Default, Clone, Serialize, Deserialize)] -pub struct TestReRunReport { - pub execution_report: HashMap<(TestVector, Fork), TestReRunExecutionReport>, - pub account_updates_report: HashMap<(TestVector, Fork), ComparisonReport>, -} - -impl TestReRunReport { - pub fn new() -> Self { - Self::default() - } - - pub fn register_execution_result_mismatch( - &mut self, - vector: TestVector, - levm_result: TxResult, - revm_result: RevmExecutionResult, - fork: Fork, - ) { - let value = Some((levm_result, revm_result)); - self.execution_report - .entry((vector, fork)) - .and_modify(|report| { - report.execution_result_mismatch = value.clone(); - }) - .or_insert(TestReRunExecutionReport { - execution_result_mismatch: value, - ..Default::default() - }); - } - - pub fn register_gas_used_mismatch( - &mut self, - vector: TestVector, - levm_gas_used: u64, - revm_gas_used: u64, - fork: Fork, - ) { - let value = Some((levm_gas_used, revm_gas_used)); - self.execution_report - .entry((vector, fork)) - .and_modify(|report| { - report.gas_used_mismatch = value; - }) - .or_insert(TestReRunExecutionReport { - gas_used_mismatch: value, - ..Default::default() - }); - } - - pub fn register_gas_refunded_mismatch( - &mut self, - vector: TestVector, - levm_gas_refunded: u64, - revm_gas_refunded: u64, - fork: Fork, - ) { - let value = Some((levm_gas_refunded, revm_gas_refunded)); - self.execution_report - .entry((vector, fork)) - .and_modify(|report| { - report.gas_refunded_mismatch = value; - }) - .or_insert(TestReRunExecutionReport { - gas_refunded_mismatch: value, - ..Default::default() - }); - } - - pub fn register_logs_mismatch( - &mut self, - vector: TestVector, - levm_logs: Vec, - revm_logs: Vec, - fork: Fork, - ) { - let value = Some((levm_logs, revm_logs)); - self.execution_report - .entry((vector, fork)) - .and_modify(|report| { - report.logs_mismatch = value.clone(); - }) - .or_insert(TestReRunExecutionReport { - logs_mismatch: value, - ..Default::default() - }); - } - - pub fn register_account_updates_report( - &mut self, - vector: TestVector, - report: ComparisonReport, - fork: Fork, - ) { - self.account_updates_report.insert((vector, fork), report); - } - - pub fn register_re_run_failure( - &mut self, - vector: TestVector, - levm_result: TxResult, - revm_error: REVMError, - fork: Fork, - ) { - let value = Some((levm_result, revm_error.to_string())); - self.execution_report - .entry((vector, fork)) - .and_modify(|report| { - report.re_runner_error = value.clone(); - }) - .or_insert(TestReRunExecutionReport { - re_runner_error: value, - ..Default::default() - }); - } -} diff --git a/tooling/ef_tests/state/runner/levm_runner.rs b/tooling/ef_tests/state/runner/levm_runner.rs deleted file mode 100644 index 4990484dd9d..00000000000 --- a/tooling/ef_tests/state/runner/levm_runner.rs +++ /dev/null @@ -1,495 +0,0 @@ -use crate::{ - report::{EFTestReport, EFTestReportForkResult, TestVector}, - runner::{EFTestRunnerError, InternalError}, - types::{EFTest, TransactionExpectedException}, - utils::{self, effective_gas_price}, -}; -use ethrex_common::utils::keccak; -use ethrex_common::{ - H256, U256, - types::{ - AccountUpdate, EIP1559Transaction, EIP7702Transaction, Fork, Transaction, TxKind, - tx_fields::*, - }, -}; -use ethrex_crypto::NativeCrypto; -use ethrex_levm::{ - EVMConfig, Environment, - db::gen_db::GeneralizedDatabase, - errors::{ExecutionReport, TxValidationError, VMError}, - tracing::LevmCallTracer, - utils::get_base_fee_per_blob_gas, - vm::{VM, VMType}, -}; -use ethrex_rlp::encode::RLPEncode; -use ethrex_vm::backends; - -pub async fn run_ef_test(test: &EFTest) -> Result { - // There are some tests that don't have a hash, unwrap will panic - let hash = test - ._info - .generated_test_hash - .or(test._info.hash) - .unwrap_or_default(); - - let mut ef_test_report = EFTestReport::new( - test.name.clone(), - test.dir.clone(), - test._info.clone(), - hash, - ); - - for fork in test.post.forks.keys() { - let mut ef_test_report_fork = EFTestReportForkResult::new(); - - for (vector, _tx) in test.transactions.iter() { - // This is because there are some test vectors that are not valid for the current fork. - if !test.post.has_vector_for_fork(vector, *fork) { - continue; - } - match run_ef_test_tx(vector, test, fork).await { - Ok(_) => continue, - Err(EFTestRunnerError::VMInitializationFailed(reason)) => { - ef_test_report_fork.register_vm_initialization_failure(reason, *vector); - } - Err(EFTestRunnerError::FailedToEnsurePreState(reason)) => { - ef_test_report_fork.register_pre_state_validation_failure(reason, *vector); - } - Err(EFTestRunnerError::ExecutionFailedUnexpectedly(error)) => { - ef_test_report_fork.register_unexpected_execution_failure(error, *vector); - } - Err(EFTestRunnerError::FailedToEnsurePostState( - transaction_report, - reason, - levm_cache, - )) => { - ef_test_report_fork.register_post_state_validation_failure( - *transaction_report, - reason, - *vector, - levm_cache, - ); - } - Err(EFTestRunnerError::VMExecutionMismatch(_)) => { - return Err(EFTestRunnerError::Internal(InternalError::FirstRunInternal( - format!("VM execution mismatch errors should only happen when running with revm. This failed during levm's execution. LEVM runner {}",line!()) - .to_owned(), - ))); - } - Err(EFTestRunnerError::ExpectedExceptionDoesNotMatchReceived(reason)) => { - ef_test_report_fork - .register_post_state_validation_error_mismatch(reason, *vector); - } - Err(EFTestRunnerError::FailedToRevertLEVMState(reason)) => { - ef_test_report_fork.register_error_on_reverting_levm_state(reason, *vector); - } - Err(EFTestRunnerError::Internal(reason)) => { - return Err(EFTestRunnerError::Internal(reason)); - } - Err(EFTestRunnerError::EIP7702ShouldNotBeCreateType) => { - return Err(EFTestRunnerError::Internal(InternalError::Custom( - format!("This case should not happen. LEVM Runner {}.", line!()).to_owned(), - ))); - } - Err(EFTestRunnerError::TestsFailed) => { - unreachable!( - "An EFTestRunnerError::TestsFailed can't happen at this point. This error is only thrown in run_ef_tests under the summary flag. LEVM Runner {}.", - line!() - ) - } - } - } - ef_test_report.register_fork_result(*fork, ef_test_report_fork); - } - Ok(ef_test_report) -} - -pub async fn run_ef_test_tx( - vector: &TestVector, - test: &EFTest, - fork: &Fork, -) -> Result<(), EFTestRunnerError> { - let mut db = utils::load_initial_state_levm(test).await; - let vm_creation_result = prepare_vm_for_tx(vector, test, fork, &mut db); - // For handling edge case in which there's a create in a Type 4 Transaction, that sadly is detected before actual execution of the vm, when building the "Transaction" for creating a new instance of vm. - let levm_execution_result = match vm_creation_result { - Err(EFTestRunnerError::EIP7702ShouldNotBeCreateType) => Err(VMError::TxValidation( - TxValidationError::Type4TxContractCreation, - )), - Err(error) => return Err(error), - Ok(mut levm) => { - ensure_pre_state(&levm, test)?; - levm.execute() - } - }; - - ensure_post_state(&levm_execution_result, vector, test, fork, &mut db).await?; - Ok(()) -} - -pub fn prepare_vm_for_tx<'a>( - vector: &TestVector, - test: &EFTest, - fork: &Fork, - db: &'a mut GeneralizedDatabase, -) -> Result, EFTestRunnerError> { - let test_tx = test - .transactions - .get(vector) - .ok_or(EFTestRunnerError::Internal( - InternalError::FirstRunInternal( - format!( - "Failed to get transaction in prepare_vm_for_tx(). LEVM runner, line: {}.", - line!() - ) - .to_owned(), - ), - ))?; - - let access_list = test_tx - .access_list - .iter() - .map(|arg| (arg.address, arg.storage_keys.clone())) - .collect(); - - // Check if the tx has the authorization_lists field implemented by eip7702. - let authorization_list = test_tx.authorization_list.clone().map(|list| { - list.iter() - .map(|auth_tuple| AuthorizationTuple { - chain_id: auth_tuple.chain_id, - address: auth_tuple.address, - nonce: auth_tuple.nonce, - y_parity: auth_tuple.v, - r_signature: auth_tuple.r, - s_signature: auth_tuple.s, - }) - .collect::>() - }); - - let blob_schedule = EVMConfig::canonical_values(*fork); - let config = EVMConfig::new(*fork, blob_schedule); - - let tx = match authorization_list { - Some(list) => Transaction::EIP7702Transaction(EIP7702Transaction { - to: match test_tx.to { - TxKind::Call(to) => to, - TxKind::Create => return Err(EFTestRunnerError::EIP7702ShouldNotBeCreateType), - }, - value: test_tx.value, - data: test_tx.data.clone(), - access_list, - authorization_list: list, - gas_limit: test_tx.gas_limit, - ..Default::default() - }), - None => Transaction::EIP1559Transaction(EIP1559Transaction { - to: test_tx.to.clone(), - value: test_tx.value, - data: test_tx.data.clone(), - access_list, - gas_limit: test_tx.gas_limit, - ..Default::default() - }), - }; - let base_blob_fee_per_gas = get_base_fee_per_blob_gas( - test.env - .current_excess_blob_gas - .map(|x| x.try_into().unwrap()), - &config, - ) - .map_err(|e| { - EFTestRunnerError::FailedToEnsurePreState(format!("Failed to calculate base blob fee: {e}")) - })?; - - VM::new( - Environment { - origin: test_tx.sender, - gas_limit: test_tx.gas_limit, - config, - block_number: test.env.current_number.try_into().unwrap(), - coinbase: test.env.current_coinbase, - timestamp: test.env.current_timestamp.try_into().unwrap(), - prev_randao: test.env.current_random, - difficulty: test.env.current_difficulty, - slot_number: test.env.slot_number.unwrap_or_default(), - chain_id: U256::from(1), - base_fee_per_gas: test.env.current_base_fee.unwrap_or_default(), - base_blob_fee_per_gas, - gas_price: effective_gas_price(test, &test_tx)?, - block_excess_blob_gas: test - .env - .current_excess_blob_gas - .map(|x| x.try_into().unwrap()), - block_blob_gas_used: None, - tx_blob_hashes: test_tx.blob_versioned_hashes.clone(), - tx_max_priority_fee_per_gas: test_tx.max_priority_fee_per_gas, - tx_max_fee_per_gas: test_tx.max_fee_per_gas, - tx_max_fee_per_blob_gas: test_tx.max_fee_per_blob_gas, - tx_nonce: test_tx.nonce, - block_gas_limit: test.env.current_gas_limit, - is_privileged: false, - fee_token: None, - disable_balance_check: false, - }, - db, - &tx, - LevmCallTracer::disabled(), - VMType::L1, // TODO: Should we run the EF tests with L2? - &NativeCrypto, - ) - .map_err(|e| EFTestRunnerError::FailedToEnsurePreState(format!("Failed to initialize VM: {e}"))) -} - -pub fn ensure_pre_state(evm: &VM, test: &EFTest) -> Result<(), EFTestRunnerError> { - let world_state = &evm.db.store; - for (address, pre_value) in &test.pre.0 { - let account_info = world_state.get_account_state(*address).map_err(|e| { - EFTestRunnerError::Internal(InternalError::Custom(format!( - "Failed to read account {address:#x} from world state: {e}", - ))) - })?; - ensure_pre_state_condition( - account_info.nonce == pre_value.nonce, - format!( - "Nonce mismatch for account {address:#x}: expected {}, got {}", - pre_value.nonce, account_info.nonce - ), - )?; - ensure_pre_state_condition( - account_info.balance == pre_value.balance, - format!( - "Balance mismatch for account {address:#x}: expected {}, got {}", - pre_value.balance, account_info.balance - ), - )?; - for (k, v) in &pre_value.storage { - let storage_slot = world_state - .get_storage_value(*address, H256::from_slice(&k.to_big_endian())) - .unwrap(); - ensure_pre_state_condition( - &storage_slot == v, - format!( - "Storage slot mismatch for account {address:#x} at key {k:?}: expected {v}, got {storage_slot}", - ), - )?; - } - ensure_pre_state_condition( - account_info.code_hash == keccak(pre_value.code.as_ref()), - format!( - "Code hash mismatch for account {address:#x}: expected {}, got {}", - keccak(pre_value.code.as_ref()), - account_info.code_hash - ), - )?; - } - Ok(()) -} - -fn ensure_pre_state_condition( - condition: bool, - error_reason: String, -) -> Result<(), EFTestRunnerError> { - if !condition { - return Err(EFTestRunnerError::FailedToEnsurePreState(error_reason)); - } - Ok(()) -} - -// Exceptions not covered: RlpInvalidValue -fn exception_is_expected( - expected_exceptions: Vec, - returned_error: VMError, -) -> bool { - expected_exceptions.iter().any(|exception| { - matches!( - (exception, &returned_error), - ( - TransactionExpectedException::IntrinsicGasTooLow, - VMError::TxValidation(TxValidationError::IntrinsicGasTooLow) - ) | ( - TransactionExpectedException::IntrinsicGasBelowFloorGasCost, - VMError::TxValidation(TxValidationError::IntrinsicGasBelowFloorGasCost) - ) | ( - TransactionExpectedException::InsufficientAccountFunds, - VMError::TxValidation(TxValidationError::InsufficientAccountFunds) - ) | ( - TransactionExpectedException::PriorityGreaterThanMaxFeePerGas, - VMError::TxValidation(TxValidationError::PriorityGreaterThanMaxFeePerGas { - priority_fee: _, - max_fee_per_gas: _ - }) - ) | ( - TransactionExpectedException::GasLimitPriceProductOverflow, - VMError::TxValidation(TxValidationError::GasLimitPriceProductOverflow) - ) | ( - TransactionExpectedException::SenderNotEoa, - VMError::TxValidation(TxValidationError::SenderNotEOA(_)) - ) | ( - TransactionExpectedException::InsufficientMaxFeePerGas, - VMError::TxValidation(TxValidationError::InsufficientMaxFeePerGas) - ) | ( - TransactionExpectedException::NonceIsMax, - VMError::TxValidation(TxValidationError::NonceIsMax) - ) | ( - TransactionExpectedException::GasAllowanceExceeded, - VMError::TxValidation(TxValidationError::GasAllowanceExceeded { - block_gas_limit: _, - tx_gas_limit: _ - }) - ) | ( - TransactionExpectedException::Type3TxPreFork, - VMError::TxValidation(TxValidationError::Type3TxPreFork) - ) | ( - TransactionExpectedException::Type3TxBlobCountExceeded, - VMError::TxValidation(TxValidationError::Type3TxBlobCountExceeded { - max_blob_count: _, - actual_blob_count: _ - }) - ) | ( - TransactionExpectedException::Type3TxZeroBlobs, - VMError::TxValidation(TxValidationError::Type3TxZeroBlobs) - ) | ( - TransactionExpectedException::Type3TxContractCreation, - VMError::TxValidation(TxValidationError::Type3TxContractCreation) - ) | ( - TransactionExpectedException::Type3TxInvalidBlobVersionedHash, - VMError::TxValidation(TxValidationError::Type3TxInvalidBlobVersionedHash) - ) | ( - TransactionExpectedException::InsufficientMaxFeePerBlobGas, - VMError::TxValidation(TxValidationError::InsufficientMaxFeePerBlobGas { - base_fee_per_blob_gas: _, - tx_max_fee_per_blob_gas: _, - }) - ) | ( - TransactionExpectedException::InitcodeSizeExceeded, - VMError::TxValidation(TxValidationError::InitcodeSizeExceeded { - max_size: _, - actual_size: _ - }) - ) | ( - TransactionExpectedException::Type4TxContractCreation, - VMError::TxValidation(TxValidationError::Type4TxContractCreation) - ) | ( - TransactionExpectedException::TxMaxGasLimitExceeded, - VMError::TxValidation(TxValidationError::TxMaxGasLimitExceeded { - tx_hash: _, - tx_gas_limit: _ - }) - ) | ( - TransactionExpectedException::Other, - VMError::TxValidation(_) //TODO: Decide whether to support more specific errors, I think this is enough. - ) - ) - }) -} - -pub async fn ensure_post_state( - levm_execution_result: &Result, - vector: &TestVector, - test: &EFTest, - fork: &Fork, - db: &mut GeneralizedDatabase, -) -> Result<(), EFTestRunnerError> { - let cache = db.current_accounts_state.clone(); - match levm_execution_result { - Ok(execution_report) => { - match test.post.vector_post_value(vector, *fork).expect_exception { - // Execution result was successful but an exception was expected. - Some(expected_exceptions) => { - let error_reason = format!("Expected exception: {expected_exceptions:?}"); - return Err(EFTestRunnerError::FailedToEnsurePostState( - Box::new(execution_report.clone()), - error_reason, - cache.into_iter().collect(), - )); - } - // Execution result was successful and no exception was expected. - None => { - let levm_account_updates = backends::levm::LEVM::get_state_transitions(db) - .map_err(|_| { - InternalError::Custom( - format!("Error at LEVM::get_state_transitions in ensure_post_state(). LEVM runner, line:{}",line!()) - .to_owned(), - ) - })?; - let vector_post_value = test.post.vector_post_value(vector, *fork); - - // 1. Compare the post-state root hash with the expected post-state root hash - if vector_post_value.hash != post_state_root(&levm_account_updates, test).await - { - return Err(EFTestRunnerError::FailedToEnsurePostState( - Box::new(execution_report.clone()), - format!("Post-state root mismatch. LEVM runner, line:{}", line!()), - cache.into_iter().collect(), - )); - } - - // 2. Compare keccak of logs with test's expected logs hash. - - // Do keccak of the RLP of logs - let keccak_logs = { - let logs = execution_report.logs.clone(); - let mut encoded_logs = Vec::new(); - logs.encode(&mut encoded_logs); - keccak(encoded_logs) - }; - - if keccak_logs != vector_post_value.logs { - return Err(EFTestRunnerError::FailedToEnsurePostState( - Box::new(execution_report.clone()), - format!("Logs mismatch. LEVM runner, line:{}", line!()), - cache.into_iter().collect(), - )); - } - } - } - } - Err(err) => { - match test.post.vector_post_value(vector, *fork).expect_exception { - // Execution result was unsuccessful and an exception was expected. - Some(expected_exceptions) => { - if !exception_is_expected(expected_exceptions.clone(), err.clone()) { - let error_reason = format!( - "Returned exception {err:?} does not match expected {expected_exceptions:?}", - ); - return Err(EFTestRunnerError::ExpectedExceptionDoesNotMatchReceived( - error_reason, - )); - } - - let levm_account_updates = backends::levm::LEVM::get_state_transitions(db) - .map_err(|_| { - InternalError::Custom( - format!("Error at LEVM::get_state_transitions in ensure_post_state(). LEVM runner, line:{}", line!()) - .to_owned(), - ) - })?; - let vector_post_value = test.post.vector_post_value(vector, *fork); - - // Compare the post-state root hash with the expected post-state root hash to ensure levm state is correct (was reverted) - if vector_post_value.hash != post_state_root(&levm_account_updates, test).await - { - return Err(EFTestRunnerError::FailedToRevertLEVMState(format!( - "Failed to revert LEVM state. LEVM runner, line:{}", - line!() - ))); - } - } - // Execution result was unsuccessful but no exception was expected. - None => { - return Err(EFTestRunnerError::ExecutionFailedUnexpectedly(err.clone())); - } - } - } - }; - Ok(()) -} - -pub async fn post_state_root(account_updates: &[AccountUpdate], test: &EFTest) -> H256 { - let (_initial_state, block_hash, store) = utils::load_initial_state_revm(test).await; - let ret_account_updates_batch = store - .apply_account_updates_batch(block_hash, account_updates) - .unwrap() - .unwrap(); - ret_account_updates_batch.state_trie_hash -} diff --git a/tooling/ef_tests/state/runner/mod.rs b/tooling/ef_tests/state/runner/mod.rs deleted file mode 100644 index efd2d46ec7c..00000000000 --- a/tooling/ef_tests/state/runner/mod.rs +++ /dev/null @@ -1,339 +0,0 @@ -use std::collections::BTreeMap; - -use crate::report::EFTestsReport; -use crate::{ - parser::SPECIFIC_IGNORED_TESTS, - report::{self, EFTestReport, TestReRunReport, format_duration_as_mm_ss}, - types::EFTest, -}; -use clap::Parser; -use colored::Colorize; -use ethrex_common::Address; -use ethrex_common::types::Fork; -use ethrex_levm::account::LevmAccount; -use ethrex_levm::errors::{ExecutionReport, VMError}; -pub use revm::primitives::hardfork::SpecId; -use serde::{Deserialize, Serialize}; -use spinoff::{Color, Spinner, spinners::Dots}; - -pub mod levm_runner; -pub mod revm_db; -pub mod revm_runner; - -#[derive(Debug, thiserror::Error, Clone, Serialize, Deserialize)] -pub enum EFTestRunnerError { - #[error("VM initialization failed: {0}")] - VMInitializationFailed(String), - #[error("Transaction execution failed when it was not expected to fail: {0}")] - ExecutionFailedUnexpectedly(VMError), - #[error("Failed to ensure pre-state: {0}")] - FailedToEnsurePreState(String), - #[error("Failed to ensure post-state: {1}")] - FailedToEnsurePostState(Box, String, BTreeMap), - #[error("VM run mismatch: {0}")] - VMExecutionMismatch(String), - #[error("Exception does not match the expected: {0}")] - ExpectedExceptionDoesNotMatchReceived(String), - #[error("EIP-7702 should not be a create type")] - EIP7702ShouldNotBeCreateType, - #[error("This is a bug: {0}")] - Internal(#[from] InternalError), - #[error("Failed to revert levm state after transaction error: {0}")] - FailedToRevertLEVMState(String), - #[error("Tests failed")] - TestsFailed, -} - -#[derive(Debug, thiserror::Error, Clone, Serialize, Deserialize)] -pub enum InternalError { - #[error("First run failed unexpectedly: {0}")] - FirstRunInternal(String), - #[error("Re-runner failed unexpectedly: {0}")] - ReRunInternal(String, TestReRunReport), - #[error("Main runner failed unexpectedly: {0}")] - MainRunnerInternal(String), - #[error("{0}")] - Custom(String), -} - -#[derive(Parser, Debug, Default)] -pub struct EFTestRunnerOptions { - /// For running tests of specific forks. - // Amsterdam fork removed until EIPs are implemented. - // To re-enable: add "Amsterdam" back to default_value after implementing: - // - EIP-7928: Block-Level Access Lists - // - EIP-7708: ETH Transfers Emit a Log - // - EIP-7778: Block Gas Accounting without Refunds - // - EIP-7843: SLOTNUM Opcode - // - EIP-8024: DUPN/SWAPN/EXCHANGE - #[arg( - long, - value_name = "FORK", - value_delimiter = ',', - value_parser=parse_fork, - default_value = "Paris,Shanghai,Cancun,Prague,Osaka" - )] - pub forks: Option>, - /// For running specific .json files - #[arg(short, long, value_name = "TESTS", value_delimiter = ',')] - pub tests: Vec, - /// For running tests with a specific name - #[arg(long, value_name = "SPECIFIC_TESTS", value_delimiter = ',')] - pub specific_tests: Vec, - /// For running tests only with LEVM without the REVM re-run. - #[arg(short, long, value_name = "SUMMARY", default_value = "false")] - pub summary: bool, - #[arg(long, value_name = "SKIP", value_delimiter = ',')] - pub skip: Vec, - /// For providing more detailed information - #[arg(long, value_name = "VERBOSE", default_value = "false")] - pub verbose: bool, - /// For running tests ONLY with revm - #[arg(long, value_name = "REVM", default_value = "false")] - pub revm: bool, - /// For running particular tests that have their specified paths listed with the tests flag. - #[arg(long, value_name = "PATHS", default_value = "false")] - pub paths: bool, -} - -fn parse_fork(value: &str) -> Result { - match value { - "Frontier" => Ok(Fork::Frontier), - "FrontierThawing" => Ok(Fork::FrontierThawing), - "Homestead" => Ok(Fork::Homestead), - "DaoFork" => Ok(Fork::DaoFork), - "Tangerine" => Ok(Fork::Tangerine), - "SpuriousDragon" => Ok(Fork::SpuriousDragon), - "Byzantium" => Ok(Fork::Byzantium), - "Constantinople" => Ok(Fork::Constantinople), - "Petersburg" => Ok(Fork::Petersburg), - "Istanbul" => Ok(Fork::Istanbul), - "MuirGlacier" => Ok(Fork::MuirGlacier), - "Berlin" => Ok(Fork::Berlin), - "London" => Ok(Fork::London), - "ArrowGlacier" => Ok(Fork::ArrowGlacier), - "GrayGlacier" => Ok(Fork::GrayGlacier), - "Paris" | "Merge" => Ok(Fork::Paris), - "Shanghai" => Ok(Fork::Shanghai), - "Cancun" => Ok(Fork::Cancun), - "Prague" => Ok(Fork::Prague), - "Osaka" => Ok(Fork::Osaka), - "Amsterdam" => Ok(Fork::Amsterdam), - other => Err(format!("Unknown fork: {other}")), - } -} - -pub async fn run_ef_tests( - ef_tests: Vec, - opts: &EFTestRunnerOptions, -) -> Result<(), EFTestRunnerError> { - let mut reports = report::load()?; - if reports.is_empty() { - if opts.revm { - run_with_revm(&mut reports, &ef_tests, opts).await?; - return Ok(()); - } else { - run_with_levm(&mut reports, &ef_tests, opts).await?; - } - } - if opts.summary { - if reports.iter().any(|r| !r.passed()) { - println!( - "{}", - EFTestsReport(reports.iter().filter(|&r| !r.passed()).cloned().collect(),) - ); - return Err(EFTestRunnerError::TestsFailed); - } - return Ok(()); - } - if reports.iter().any(|r| !r.passed()) { - re_run_with_revm(&mut reports, &ef_tests, opts).await?; - return write_report(&reports); - } - let mut report_spinner = Spinner::new(Dots, "Loading report...".to_owned(), Color::Cyan); - report_spinner.success(&format!("{}", "All tests passed!".bold())); - Ok(()) -} - -async fn run_with_levm( - reports: &mut Vec, - ef_tests: &[EFTest], - opts: &EFTestRunnerOptions, -) -> Result<(), EFTestRunnerError> { - let levm_run_time = std::time::Instant::now(); - - print!("{}", report::progress(reports, levm_run_time.elapsed())); - - for test in ef_tests.iter() { - let is_not_specific = !opts.specific_tests.is_empty() - && !opts - .specific_tests - .iter() - .any(|name| test.name.contains(name)); - let is_ignored = SPECIFIC_IGNORED_TESTS - .iter() - .any(|skip| test.name.contains(skip)); - - // Skip tests that are not specific (if specific tests were indicated) and ignored ones. - if is_not_specific || is_ignored { - continue; - } - if opts.verbose { - println!("Running test: {:?}", test.name); - } - let ef_test_report = match levm_runner::run_ef_test(test).await { - Ok(ef_test_report) => ef_test_report, - Err(EFTestRunnerError::Internal(err)) => return Err(EFTestRunnerError::Internal(err)), - non_internal_errors => { - return Err(EFTestRunnerError::Internal( - InternalError::FirstRunInternal(format!( - "Non-internal error raised when executing levm. This should not happen: {non_internal_errors:?}", - )), - )); - } - }; - reports.push(ef_test_report); - print!("{}", report::progress(reports, levm_run_time.elapsed())); - } - print!("{}", report::progress(reports, levm_run_time.elapsed())); - - if opts.summary { - report::write_summary_for_slack(reports)?; - report::write_summary_for_github(reports)?; - } - - println!("{}", "\nLoading summary...".to_owned()); - println!("{}", report::summary_for_shell(reports)); - - Ok(()) -} - -/// ### Runs all tests with REVM -async fn run_with_revm( - reports: &mut Vec, - ef_tests: &[EFTest], - opts: &EFTestRunnerOptions, -) -> Result<(), EFTestRunnerError> { - let revm_run_time = std::time::Instant::now(); - println!("{}", "Running all tests with REVM...".to_owned()); - - for (idx, test) in ef_tests.iter().enumerate() { - if opts.verbose { - println!("Running test: {:?}", test.name); - } - let total_tests = ef_tests.len(); - println!( - "{} {}/{total_tests} - {}", - "Running all tests with REVM".bold(), - idx + 1, - format_duration_as_mm_ss(revm_run_time.elapsed()) - ); - let ef_test_report = match revm_runner::_run_ef_test_revm(test).await { - Ok(ef_test_report) => ef_test_report, - Err(EFTestRunnerError::Internal(err)) => return Err(EFTestRunnerError::Internal(err)), - non_internal_errors => { - return Err(EFTestRunnerError::Internal( - InternalError::FirstRunInternal(format!( - "Non-internal error raised when executing revm. This should not happen: {non_internal_errors:?}", - )), - )); - } - }; - reports.push(ef_test_report); - println!("{}", report::progress(reports, revm_run_time.elapsed())); - } - println!( - "Ran all tests with REVM in {}", - format_duration_as_mm_ss(revm_run_time.elapsed()) - ); - Ok(()) -} - -async fn re_run_with_revm( - reports: &mut [EFTestReport], - ef_tests: &[EFTest], - opts: &EFTestRunnerOptions, -) -> Result<(), EFTestRunnerError> { - let revm_run_time = std::time::Instant::now(); - println!("{}", "Running failed tests with REVM...".to_owned()); - let failed_tests = reports.iter().filter(|report| !report.passed()).count(); - - // Iterate only over failed tests - for (idx, failed_test_report) in reports - .iter_mut() - .filter(|report| !report.passed()) - .enumerate() - { - if opts.verbose { - println!("Running test: {:?}", failed_test_report.name); - } - print!( - "\r{} {}/{failed_tests} - {}", - "Re-running failed tests with REVM".bold(), - idx + 1, - format_duration_as_mm_ss(revm_run_time.elapsed()) - ); - - match revm_runner::re_run_failed_ef_test( - ef_tests - .iter() - .find(|test| { - let hash = test - ._info - .generated_test_hash - .or(test._info.hash) - .unwrap_or_default(); - - let failed_hash = failed_test_report.test_hash; - - hash == failed_hash && test.name == failed_test_report.name - }) - .unwrap(), - failed_test_report, - ) - .await - { - Ok(re_run_report) => { - failed_test_report.register_re_run_report(re_run_report.clone()); - } - Err(EFTestRunnerError::Internal(InternalError::ReRunInternal( - reason, - re_run_report, - ))) => { - write_report(reports)?; - cache_re_run(reports)?; - return Err(EFTestRunnerError::Internal(InternalError::ReRunInternal( - reason, - re_run_report, - ))); - } - non_re_run_internal_errors => { - println!("{} \n{failed_test_report}", "Failing Test:".bold()); - return Err(EFTestRunnerError::Internal( - InternalError::MainRunnerInternal(format!( - "Non-internal error raised when executing revm. This should not happen: {non_re_run_internal_errors:?}" - )), - )); - } - } - } - println!( - "\nRe-ran failed tests with REVM in {}", - format_duration_as_mm_ss(revm_run_time.elapsed()) - ); - Ok(()) -} - -fn write_report(reports: &[EFTestReport]) -> Result<(), EFTestRunnerError> { - let mut report_spinner = Spinner::new(Dots, "Loading report...".to_owned(), Color::Cyan); - let report_file_path = report::write(reports)?; - report_spinner.success(&format!("Report written to file {report_file_path:?}").bold()); - Ok(()) -} - -fn cache_re_run(reports: &[EFTestReport]) -> Result<(), EFTestRunnerError> { - let mut cache_spinner = Spinner::new(Dots, "Caching re-run...".to_owned(), Color::Cyan); - let cache_file_path = report::cache(reports)?; - cache_spinner.success(&format!("Re-run cached to file {cache_file_path:?}").bold()); - Ok(()) -} diff --git a/tooling/ef_tests/state/runner/revm_db.rs b/tooling/ef_tests/state/runner/revm_db.rs deleted file mode 100644 index ee0caa2b284..00000000000 --- a/tooling/ef_tests/state/runner/revm_db.rs +++ /dev/null @@ -1,251 +0,0 @@ -use ethrex_common::types::{AccountInfo, AccountUpdate, ChainConfig, Code}; -use ethrex_common::{Address as CoreAddress, BigEndianHash, H256, U256}; -use ethrex_vm::{DynVmDatabase, EvmError, VmDatabase}; -use revm::context::DBErrorMarker; -use revm::database::states::{AccountStatus, bundle_state::BundleRetention}; -use revm::primitives::{ - Address as RevmAddress, B256 as RevmB256, Bytes as RevmBytes, U256 as RevmU256, -}; -use revm::state::{AccountInfo as RevmAccountInfo, Bytecode as RevmBytecode}; - -#[derive(thiserror::Error, Debug)] -#[error(transparent)] -pub struct RevmError(#[from] pub EvmError); - -impl DBErrorMarker for RevmError {} - -/// State used when running the EVM. The state can be represented with a [VmDbWrapper] database -pub struct RevmState { - pub inner: revm::database::State, -} - -/// Wrapper used so we can implement revm-specific traits over `DynVmDatabase` -#[derive(Clone)] -pub struct RevmDynVmDatabase(DynVmDatabase); - -// Needed because revm::database::State is not cloneable and we need to -// restore the previous EVM state after executing a transaction in L2 mode whose resulting state diff doesn't fit in a blob. -impl Clone for RevmState { - fn clone(&self) -> Self { - let inner = revm::database::State:: { - cache: self.inner.cache.clone(), - database: self.inner.database.clone(), - transition_state: self.inner.transition_state.clone(), - bundle_state: self.inner.bundle_state.clone(), - use_preloaded_bundle: self.inner.use_preloaded_bundle, - block_hashes: self.inner.block_hashes.clone(), - }; - - Self { inner } - } -} - -/// Builds RevmState from a Store -pub fn revm_state(db: DynVmDatabase) -> RevmState { - let inner = revm::database::State::builder() - .with_database(RevmDynVmDatabase(db)) - .with_bundle_update() - .without_state_clear() - .build(); - RevmState { inner } -} - -impl revm::Database for RevmDynVmDatabase { - type Error = RevmError; - - fn basic(&mut self, address: RevmAddress) -> Result, Self::Error> { - let acc_info = match ::get_account_state( - self.0.as_ref(), - CoreAddress::from(address.0.as_ref()), - )? { - None => return Ok(None), - Some(acc_info) => acc_info, - }; - let code = self.code_by_hash(acc_info.code_hash.0.into())?; - - Ok(Some(RevmAccountInfo { - balance: RevmU256::from_limbs(acc_info.balance.0), - nonce: acc_info.nonce, - code_hash: RevmB256::from(acc_info.code_hash.0), - code: Some(code), - })) - } - - fn code_by_hash(&mut self, code_hash: RevmB256) -> Result { - let code = - ::get_account_code(self.0.as_ref(), H256::from(code_hash.as_ref()))?; - Ok(RevmBytecode::new_raw(RevmBytes(code.bytecode))) - } - - fn storage(&mut self, address: RevmAddress, index: RevmU256) -> Result { - Ok(::get_storage_slot( - self.0.as_ref(), - CoreAddress::from(address.0.as_ref()), - H256::from(index.to_be_bytes()), - )? - .map(|value| RevmU256::from_limbs(value.0)) - .unwrap_or_else(|| RevmU256::ZERO)) - } - - fn block_hash(&mut self, number: u64) -> Result { - Ok(::get_block_hash(self.0.as_ref(), number) - .map(|hash| RevmB256::from_slice(&hash.0))?) - } -} - -impl revm::DatabaseRef for RevmDynVmDatabase { - type Error = RevmError; - - fn basic_ref(&self, address: RevmAddress) -> Result, Self::Error> { - let acc_info = match ::get_account_state( - self.0.as_ref(), - CoreAddress::from(address.0.as_ref()), - )? { - None => return Ok(None), - Some(acc_info) => acc_info, - }; - let code = self.code_by_hash_ref(acc_info.code_hash.0.into())?; - - Ok(Some(RevmAccountInfo { - balance: RevmU256::from_limbs(acc_info.balance.0), - nonce: acc_info.nonce, - code_hash: RevmB256::from(acc_info.code_hash.0), - code: Some(code), - })) - } - - fn code_by_hash_ref(&self, code_hash: RevmB256) -> Result { - let code = - ::get_account_code(self.0.as_ref(), H256::from(code_hash.as_ref()))?; - Ok(RevmBytecode::new_raw(RevmBytes(code.bytecode))) - } - - fn storage_ref(&self, address: RevmAddress, index: RevmU256) -> Result { - Ok(::get_storage_slot( - self.0.as_ref(), - CoreAddress::from(address.0.as_ref()), - H256::from(index.to_be_bytes()), - )? - .map(|value| RevmU256::from_limbs(value.0)) - .unwrap_or_else(|| RevmU256::ZERO)) - } - - fn block_hash_ref(&self, number: u64) -> Result { - Ok(::get_block_hash(self.0.as_ref(), number) - .map(|hash| RevmB256::from_slice(&hash.0))?) - } -} - -impl RevmState { - /// Gets the stored chain config - pub fn chain_config(&self) -> Result { - self.inner.database.0.get_chain_config() - } - - /// Gets the state_transitions == [AccountUpdate] from the [RevmState]. - pub fn get_state_transitions(&mut self) -> Vec { - let initial_state = &mut self.inner; - initial_state.merge_transitions(BundleRetention::PlainState); - let bundle = initial_state.take_bundle(); - - // Update accounts - let mut account_updates = Vec::new(); - for (address, account) in bundle.state() { - if account.status.is_not_modified() { - continue; - } - let address = CoreAddress::from_slice(address.0.as_slice()); - // Remove account from DB if destroyed (Process DestroyedChanged as changed account) - if matches!( - account.status, - AccountStatus::Destroyed | AccountStatus::DestroyedAgain - ) { - account_updates.push(AccountUpdate::removed(address)); - continue; - } - - // If account is empty, do not add to the database - if account - .account_info() - .is_some_and(|acc_info| acc_info.is_empty()) - { - continue; - } - - // Edge case: Account was destroyed and created again afterwards with CREATE2. - if matches!(account.status, AccountStatus::DestroyedChanged) { - // Push to account updates the removal of the account and then push the new state of the account. - // This is for clearing the account's storage when it was selfdestructed in the first place. - account_updates.push(AccountUpdate::removed(address)); - // This will always be Some though, because it is DestroyedChanged - if let Some(new_acc_info) = account.account_info() { - let new_acc_update = AccountUpdate { - address, - removed: false, - info: Some(AccountInfo { - code_hash: H256::from_slice(new_acc_info.code_hash.as_slice()), - balance: U256::from_little_endian(new_acc_info.balance.as_le_slice()), - nonce: new_acc_info.nonce, - }), - code: new_acc_info - .code - .map(|c| c.original_bytes().0) - .map(|code| Code::from_bytecode(code, ðrex_crypto::NativeCrypto)), - added_storage: account - .storage - .iter() - .map(|(key, slot)| { - ( - H256::from_uint(&U256::from_little_endian(key.as_le_slice())), - U256::from_little_endian(slot.present_value().as_le_slice()), - ) - }) - .collect(), - removed_storage: false, - }; - account_updates.push(new_acc_update); - } - continue; - } - // Apply account changes to DB - let mut account_update = AccountUpdate::new(address); - // If the account was changed then both original and current info will be present in the bundle account - if account.is_info_changed() - && let Some(new_acc_info) = account.account_info() - { - // Update account info in DB - let code_hash = H256::from_slice(new_acc_info.code_hash.as_slice()); - let account_info = AccountInfo { - code_hash, - balance: U256::from_little_endian(new_acc_info.balance.as_le_slice()), - nonce: new_acc_info.nonce, - }; - account_update.info = Some(account_info); - // Update code in db - if account.is_contract_changed() - && let Some(code) = new_acc_info.code - { - account_update.code = Some(Code::from_bytecode( - code.original_bytes().0, - ðrex_crypto::NativeCrypto, - )); - } - } - // Update account storage in DB - for (key, slot) in account.storage.iter() { - if slot.is_changed() { - // TODO check if we need to remove the value from our db when value is zero - // if slot.present_value().is_zero() { - // account_update.removed_keys.push(H256::from_uint(&U256::from_little_endian(key.as_le_slice()))) - // } - account_update.added_storage.insert( - H256::from_uint(&U256::from_little_endian(key.as_le_slice())), - U256::from_little_endian(slot.present_value().as_le_slice()), - ); - } - } - account_updates.push(account_update) - } - account_updates - } -} diff --git a/tooling/ef_tests/state/runner/revm_runner.rs b/tooling/ef_tests/state/runner/revm_runner.rs deleted file mode 100644 index 93dc6851f80..00000000000 --- a/tooling/ef_tests/state/runner/revm_runner.rs +++ /dev/null @@ -1,727 +0,0 @@ -use super::revm_db::{RevmDynVmDatabase, RevmState}; -use crate::runner::revm_db::RevmError; -use crate::types::EFTestTransaction; -use crate::{ - report::{ComparisonReport, EFTestReport, EFTestReportForkResult, TestReRunReport, TestVector}, - runner::{EFTestRunnerError, InternalError, levm_runner::post_state_root}, - types::EFTest, - utils::{effective_gas_price, load_initial_state_levm, load_initial_state_revm}, -}; -use alloy_rlp::Encodable; -use bytes::Bytes; -use ethrex_common::types::{Code, TxType}; -use ethrex_common::utils::keccak; -use ethrex_common::{ - Address, H256, - types::{Account, AccountUpdate, Fork, TxKind}, -}; -use ethrex_levm::constants::{BLOB_BASE_FEE_UPDATE_FRACTION, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE}; -use ethrex_levm::{ - account::LevmAccount, - errors::{ExecutionReport, TxResult}, -}; -use ethrex_rlp::encode::RLPEncode; -use revm::primitives::{ - Address as RevmAddress, TxKind as RevmTxKind, U256 as RevmU256, hardfork::SpecId, -}; -use revm::{ - Context, ExecuteCommitEvm, Journal, MainBuilder, MainContext, - context::{ - BlockEnv as RevmBlockEnv, CfgEnv, Evm as Revm, TxEnv as RevmTxEnv, either::Either, - transaction::AccessList, - }, - context_interface::{ - block::blob::BlobExcessGasAndPrice, - result::{EVMError as REVMError, ExecutionResult as RevmExecutionResult}, - transaction::{AccessListItem, Authorization, SignedAuthorization}, - }, - database::State, - handler::{EthFrame, EthPrecompiles, instructions::EthInstructions}, - inspector::inspectors::TracerEip3155 as RevmTracerEip3155, - interpreter::interpreter::EthInterpreter, - primitives::B256, -}; -use std::collections::{BTreeMap, HashMap, HashSet}; - -fn levm_and_revm_logs_match( - levm_logs: &Vec, - revm_logs: &Vec, -) -> bool { - let levm_keccak_logs = { - let logs = levm_logs; - let mut encoded_logs = Vec::new(); - logs.encode(&mut encoded_logs); - keccak(encoded_logs) - }; - - let revm_keccak_logs = { - let logs = revm_logs; - let mut encoded_logs = Vec::new(); - logs.encode(&mut encoded_logs); - keccak(encoded_logs) - }; - - levm_keccak_logs == revm_keccak_logs -} - -pub async fn re_run_failed_ef_test( - test: &EFTest, - failed_test_report: &EFTestReport, -) -> Result { - assert_eq!(test.name, failed_test_report.name); - let mut re_run_report = TestReRunReport::new(); - for (fork, fork_result) in failed_test_report.fork_results.iter() { - for (vector, vector_failure) in fork_result.failed_vectors.iter() { - match vector_failure { - // We only want to re-run tests that failed in the post-state validation. - EFTestRunnerError::FailedToEnsurePostState(transaction_report, _, levm_cache) => { - match re_run_failed_ef_test_tx( - levm_cache.clone(), - vector, - test, - transaction_report, - &mut re_run_report, - fork, - ) - .await - { - Ok(_) => continue, - Err(EFTestRunnerError::VMInitializationFailed(reason)) => { - return Err(EFTestRunnerError::Internal(InternalError::ReRunInternal( - format!( - "REVM initialization failed when re-running failed test on re_run_failed_ef_test() in REVM runner, line: {}: {reason}", - line!() - ), - re_run_report.clone(), - ))); - } - Err(EFTestRunnerError::Internal(reason)) => { - return Err(EFTestRunnerError::Internal(reason)); - } - unexpected_error => { - return Err(EFTestRunnerError::Internal(InternalError::ReRunInternal( - format!( - "Unexpected error when re-running failed test on re_run_failed_ef_test() in REVM runner, line:{} : {unexpected_error:?}", - line!() - ), - re_run_report.clone(), - ))); - } - } - } - // Currently, we decided not to re-execute the test when the Expected exception does not match - // with the received. This can change in the future. - EFTestRunnerError::ExpectedExceptionDoesNotMatchReceived(_) => continue, - EFTestRunnerError::VMInitializationFailed(_) - | EFTestRunnerError::ExecutionFailedUnexpectedly(_) - | EFTestRunnerError::FailedToEnsurePreState(_) - | EFTestRunnerError::EIP7702ShouldNotBeCreateType - | EFTestRunnerError::FailedToRevertLEVMState(_) => continue, - EFTestRunnerError::VMExecutionMismatch(reason) => { - return Err(EFTestRunnerError::Internal(InternalError::ReRunInternal( - format!( - "VM execution mismatch errors should only happen when running with revm. This failed during levm's execution on REVM runner, line: {} : {reason}", - line!() - ), - re_run_report.clone(), - ))); - } - EFTestRunnerError::Internal(reason) => { - return Err(EFTestRunnerError::Internal(reason.to_owned())); - } - EFTestRunnerError::TestsFailed => { - unreachable!( - "An EFTestRunnerError::TestsFailed can't happen at this point. This error is only thrown in run_ef_tests under the summary flag. REVM runner {}.", - line!() - ) - } - } - } - } - Ok(re_run_report) -} - -pub async fn re_run_failed_ef_test_tx( - levm_cache: BTreeMap, - vector: &TestVector, - test: &EFTest, - levm_execution_report: &ExecutionReport, - re_run_report: &mut TestReRunReport, - fork: &Fork, -) -> Result<(), EFTestRunnerError> { - let (mut state, _block_hash, _store) = load_initial_state_revm(test).await; - let (mut revm, tx_env) = prepare_revm_for_tx(&mut state, vector, test, fork)?; - if !test.post.has_vector_for_fork(vector, *fork) { - return Ok(()); - } - let revm_execution_result = revm.transact_commit(tx_env); - drop(revm); // Need to drop the state mutable reference. - compare_levm_revm_execution_results( - vector, - levm_execution_report, - revm_execution_result, - re_run_report, - fork, - )?; - ensure_post_state(levm_cache, vector, &mut state, test, re_run_report, fork).await?; - Ok(()) -} - -type RevmStateT<'state> = &'state mut State; -type RevmContext<'state> = - Context, Journal>>; -type RevmWithTracer<'state> = Revm< - RevmContext<'state>, - RevmTracerEip3155, - EthInstructions>, - EthPrecompiles, - EthFrame, ->; - -pub fn prepare_revm_for_tx<'state>( - initial_state: &'state mut RevmState, - vector: &TestVector, - test: &EFTest, - fork: &Fork, -) -> Result<(RevmWithTracer<'state>, RevmTxEnv), EFTestRunnerError> { - let chain_spec = initial_state - .chain_config() - .map_err(|err| EFTestRunnerError::VMInitializationFailed(err.to_string()))?; - - let blob_excess_gas_and_price = if test.env.current_excess_blob_gas.is_none() { - None - } else { - Some(BlobExcessGasAndPrice::new( - test.env - .current_excess_blob_gas - .unwrap() - .try_into() - .unwrap(), - if fork >= &Fork::Prague { - BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE - } else { - BLOB_BASE_FEE_UPDATE_FRACTION - }, - )) - }; - let block_env = RevmBlockEnv { - number: RevmU256::from_limbs(test.env.current_number.0), - beneficiary: RevmAddress(test.env.current_coinbase.0.into()), - timestamp: RevmU256::from_limbs(test.env.current_timestamp.0), - gas_limit: test.env.current_gas_limit, - basefee: test - .env - .current_base_fee - .unwrap_or_default() - .try_into() - .unwrap_or_default(), - difficulty: RevmU256::from_limbs(test.env.current_difficulty.0), - prevrandao: test.env.current_random.map(|v| v.0.into()), - blob_excess_gas_and_price, - }; - let tx = &test - .transactions - .get(vector) - .ok_or(EFTestRunnerError::VMInitializationFailed(format!( - "Vector {vector:?} not found in test {}. REVM runner, line: {}", - test.name, - line!() - )))?; - - let revm_access_list: Vec = tx - .access_list - .iter() - .map(|eftest_access_list_item| AccessListItem { - address: RevmAddress(eftest_access_list_item.address.0.into()), - storage_keys: eftest_access_list_item - .storage_keys - .iter() - .map(|key| B256::from(key.0)) - .collect(), - }) - .collect(); - - // The latest version of revm(27.0.3) is needed to run the ef-tests with the latest changes. - // Update it in every Cargo.toml. - // revm-inspectors and revm-primitives have to be bumped too. - // NOTE: - // - rust 1.87.X is needed - // - rust-toolchain 1.87.X is needed (this can be found in ethrex/crates/vm/levm/rust-toolchain.toml) - let authorization_list = tx - .authorization_list - .clone() - .map(|list| { - list.iter() - .map(|auth_t| { - Either::Left(SignedAuthorization::new_unchecked( - Authorization { - // The latest spec defined chain_id as a U256 - chain_id: RevmU256::from_le_bytes(auth_t.chain_id.to_little_endian()), - address: RevmAddress(auth_t.address.0.into()), - nonce: auth_t.nonce, - }, - u8::try_from(u32::try_from(auth_t.v).unwrap()).unwrap(), - RevmU256::from_le_bytes(auth_t.r.to_little_endian()), - RevmU256::from_le_bytes(auth_t.s.to_little_endian()), - )) - }) - .collect::>() - }) - .unwrap_or_default(); - - let tx_env = RevmTxEnv { - caller: tx.sender.0.into(), - gas_limit: tx.gas_limit, - gas_price: effective_gas_price(test, tx)? - .try_into() - .unwrap_or_default(), - kind: match tx.to { - TxKind::Call(to) => RevmTxKind::Call(to.0.into()), - TxKind::Create => RevmTxKind::Create, - }, - value: RevmU256::from_limbs(tx.value.0), - data: tx.data.to_vec().into(), - nonce: tx.nonce, - chain_id: Some(chain_spec.chain_id), - access_list: AccessList(revm_access_list), - gas_priority_fee: tx - .max_priority_fee_per_gas - .map(|fee| fee.try_into().unwrap_or_default()), - blob_hashes: tx - .blob_versioned_hashes - .iter() - .map(|h256| B256::from(h256.0)) - .collect::>(), - max_fee_per_blob_gas: tx - .max_fee_per_blob_gas - .unwrap_or_default() - .try_into() - .unwrap_or_default(), - authorization_list, - tx_type: infer_tx_type(tx) as u8, - }; - - let mut evm_context = revm::context::Context::mainnet() - .with_block(block_env) - .with_db(&mut initial_state.inner); - evm_context.modify_cfg(|cfg| { - cfg.spec = fork_to_spec_id(*fork); - cfg.chain_id = chain_spec.chain_id; - }); - let evm = evm_context.build_mainnet_with_inspector( - RevmTracerEip3155::new(Box::new(std::io::stderr())).without_summary(), - ); - Ok((evm, tx_env)) -} - -pub fn compare_levm_revm_execution_results( - vector: &TestVector, - levm_execution_report: &ExecutionReport, - revm_execution_result: Result>, - re_run_report: &mut TestReRunReport, - fork: &Fork, -) -> Result<(), EFTestRunnerError> { - match (levm_execution_report, revm_execution_result) { - (levm_tx_report, Ok(revm_execution_result)) => { - match (&levm_tx_report.result, revm_execution_result.clone()) { - ( - TxResult::Success, - RevmExecutionResult::Success { - reason: _, - gas_used: revm_gas_used, - gas_refunded: revm_gas_refunded, - logs: revm_logs, - output: _, - }, - ) => { - if levm_tx_report.gas_used != revm_gas_used { - re_run_report.register_gas_used_mismatch( - *vector, - levm_tx_report.gas_used, - revm_gas_used, - *fork, - ); - } - if levm_tx_report.gas_refunded != revm_gas_refunded { - re_run_report.register_gas_refunded_mismatch( - *vector, - levm_tx_report.gas_refunded, - revm_gas_refunded, - *fork, - ); - } - - if !levm_and_revm_logs_match(&levm_tx_report.logs, &revm_logs) { - re_run_report.register_logs_mismatch( - *vector, - levm_tx_report.logs.clone(), - revm_logs.clone(), - *fork, - ) - } - } - ( - TxResult::Revert(_error), - RevmExecutionResult::Revert { - gas_used: revm_gas_used, - output: _, - }, - ) => { - if levm_tx_report.gas_used != revm_gas_used { - re_run_report.register_gas_used_mismatch( - *vector, - levm_tx_report.gas_used, - revm_gas_used, - *fork, - ); - } - } - ( - TxResult::Revert(_error), - RevmExecutionResult::Halt { - reason: _, - gas_used: revm_gas_used, - }, - ) => { - // TODO: Register the revert reasons. - if levm_tx_report.gas_used != revm_gas_used { - re_run_report.register_gas_used_mismatch( - *vector, - levm_tx_report.gas_used, - revm_gas_used, - *fork, - ); - } - } - _ => { - re_run_report.register_execution_result_mismatch( - *vector, - levm_tx_report.result.clone(), - revm_execution_result.clone(), - *fork, - ); - } - } - } - (levm_transaction_report, Err(revm_error)) => { - re_run_report.register_re_run_failure( - *vector, - levm_transaction_report.result.clone(), - revm_error, - *fork, - ); - } - } - Ok(()) -} - -pub async fn ensure_post_state( - levm_cache: BTreeMap, - vector: &TestVector, - revm_state: &mut RevmState, - test: &EFTest, - re_run_report: &mut TestReRunReport, - fork: &Fork, -) -> Result<(), EFTestRunnerError> { - match test.post.vector_post_value(vector, *fork).expect_exception { - Some(_expected_exception) => {} - // We only want to compare account updates when no exception is expected. - None => { - let mut db = load_initial_state_levm(test).await; - db.current_accounts_state = levm_cache.into_iter().collect(); - let levm_account_updates = db.get_state_transitions() - .map_err(|_| { - InternalError::Custom(format!("Error at LEVM::get_state_transitions() thrown in REVM runner line: {} when executing ensure_post_state()",line!()).to_owned()) - })?; - let revm_account_updates = revm_state.get_state_transitions(); - let account_updates_report = compare_levm_revm_account_updates( - vector, - test, - fork, - &levm_account_updates, - &revm_account_updates, - ) - .await; - re_run_report.register_account_updates_report(*vector, account_updates_report, *fork); - } - } - - Ok(()) -} - -pub async fn compare_levm_revm_account_updates( - vector: &TestVector, - test: &EFTest, - fork: &Fork, - levm_account_updates: &[AccountUpdate], - revm_account_updates: &[AccountUpdate], -) -> ComparisonReport { - let levm_post_state_root = post_state_root(levm_account_updates, test).await; - let revm_post_state_root = post_state_root(revm_account_updates, test).await; - let mut initial_accounts: HashMap = test - .pre - .0 - .iter() - .map(|(account_address, pre_state_value)| { - let account_storage = pre_state_value - .storage - .iter() - .map(|(key, value)| (H256::from_slice(&key.to_big_endian()), *value)) - .collect(); - let account = Account::new( - pre_state_value.balance, - Code::from_bytecode(pre_state_value.code.clone(), ðrex_crypto::NativeCrypto), - pre_state_value.nonce, - account_storage, - ); - (*account_address, account) - }) - .collect(); - initial_accounts - .entry(test.env.current_coinbase) - .or_default(); - - let (levm_updated_accounts, revm_updated_accounts): (HashSet<_>, HashSet<_>) = ( - levm_account_updates.iter().map(|u| u.address).collect(), - revm_account_updates.iter().map(|u| u.address).collect(), - ); - - ComparisonReport { - levm_post_state_root, - revm_post_state_root, - initial_accounts, - expected_post_state_root: test.post.vector_post_value(vector, *fork).hash, - levm_account_updates: levm_account_updates.to_vec(), - revm_account_updates: revm_account_updates.to_vec(), - levm_updated_accounts_only: &levm_updated_accounts - &revm_updated_accounts, - revm_updated_accounts_only: &revm_updated_accounts - &levm_updated_accounts, - shared_updated_accounts: &levm_updated_accounts & &revm_updated_accounts, - } -} - -pub async fn _run_ef_test_revm(test: &EFTest) -> Result { - let hash = test - ._info - .generated_test_hash - .or(test._info.hash) - .unwrap_or_default(); - - let mut ef_test_report = EFTestReport::new( - test.name.clone(), - test.dir.clone(), - test._info.clone(), - hash, - ); - for fork in test.post.forks.keys() { - let mut ef_test_report_fork = EFTestReportForkResult::new(); - - for (vector, _tx) in test.transactions.iter() { - if !test.post.has_vector_for_fork(vector, *fork) { - continue; - } - match _run_ef_test_tx_revm(vector, test, fork).await { - Ok(_) => continue, - Err(EFTestRunnerError::VMInitializationFailed(reason)) => { - ef_test_report_fork.register_vm_initialization_failure(reason, *vector); - } - Err(EFTestRunnerError::FailedToEnsurePreState(reason)) - | Err(EFTestRunnerError::FailedToRevertLEVMState(reason)) => { - ef_test_report_fork.register_pre_state_validation_failure(reason, *vector); - } - Err(EFTestRunnerError::ExecutionFailedUnexpectedly(error)) => { - ef_test_report_fork.register_unexpected_execution_failure(error, *vector); - } - Err(EFTestRunnerError::FailedToEnsurePostState( - transaction_report, - reason, - levm_cache, - )) => { - ef_test_report_fork.register_post_state_validation_failure( - *transaction_report, - reason, - *vector, - levm_cache, - ); - } - Err(EFTestRunnerError::VMExecutionMismatch(_)) => { - return Err(EFTestRunnerError::Internal(InternalError::FirstRunInternal( - format!("VM execution mismatch errors should only happen when COMPARING LEVM AND REVM. This failed during revm's execution. REVM runner, line: {}.",line!()) - .to_owned(), - ))); - } - Err(EFTestRunnerError::Internal(reason)) => { - return Err(EFTestRunnerError::Internal(reason)); - } - Err(EFTestRunnerError::ExpectedExceptionDoesNotMatchReceived(_)) => { - return Err(EFTestRunnerError::Internal(InternalError::MainRunnerInternal( - format!("The ExpectedExceptionDoesNotMatchReceived error should only happen when executing Levm, the errors matching is not implemented in Revm. REVM runner, line: {}",line!()) - .to_owned(), - ))); - } - Err(EFTestRunnerError::EIP7702ShouldNotBeCreateType) => { - return Err(EFTestRunnerError::Internal(InternalError::Custom( - format!( - "This case should not happen. REVM runner, line: {}", - line!() - ) - .to_owned(), - ))); - } - Err(EFTestRunnerError::TestsFailed) => { - unreachable!( - "An EFTestRunnerError::TestsFailed can't happen at this point. This error is only thrown in run_ef_tests under the summary flag. REVM runner, line: {}", - line!() - ) - } - } - } - ef_test_report.register_fork_result(*fork, ef_test_report_fork); - } - Ok(ef_test_report) -} - -pub async fn _run_ef_test_tx_revm( - vector: &TestVector, - test: &EFTest, - fork: &Fork, -) -> Result<(), EFTestRunnerError> { - let (mut state, _block_hash, _store) = load_initial_state_revm(test).await; - let (mut revm, tx_env) = prepare_revm_for_tx(&mut state, vector, test, fork)?; - let revm_execution_result = revm.transact_commit(tx_env); - drop(revm); // Need to drop the state mutable reference. - - _ensure_post_state_revm(revm_execution_result, vector, test, &mut state, fork).await?; - - Ok(()) -} - -pub async fn _ensure_post_state_revm( - revm_execution_result: Result>, - vector: &TestVector, - test: &EFTest, - revm_state: &mut RevmState, - fork: &Fork, -) -> Result<(), EFTestRunnerError> { - match revm_execution_result { - Ok(_execution_result) => { - match test.post.vector_post_value(vector, *fork).expect_exception { - // Execution result was successful but an exception was expected. - Some(expected_exception) => { - let error_reason = format!( - "On REVM runner, line: {} Expected exception: {expected_exception:?}", - line!() - ); - return Err(EFTestRunnerError::FailedToEnsurePostState( - Box::new(ExecutionReport { - result: TxResult::Success, - gas_used: 42, - gas_spent: 42, - gas_refunded: 42, - state_gas_used: 0, - logs: vec![], - output: Bytes::new(), - }), - //TODO: This is not a TransactionReport because it is REVM - error_reason, - BTreeMap::new(), - )); - } - // Execution result was successful and no exception was expected. - None => { - let revm_account_updates = revm_state.get_state_transitions(); - let pos_state_root = post_state_root(&revm_account_updates, test).await; - let expected_post_state_root_hash = - test.post.vector_post_value(vector, *fork).hash; - if expected_post_state_root_hash != pos_state_root { - println!("Post-state root mismatch on REVM runner, line: {}", line!()); - return Err(EFTestRunnerError::FailedToEnsurePostState( - Box::new(ExecutionReport { - result: TxResult::Success, - gas_used: 42, - gas_spent: 42, - gas_refunded: 42, - state_gas_used: 0, - logs: vec![], - output: Bytes::new(), - }), - //TODO: This is not a TransactionReport because it is REVM - format!("Post-state root mismatch on REVM runner, line: {}", line!()) - .to_string(), - BTreeMap::new(), - )); - } - } - } - } - Err(err) => { - match test.post.vector_post_value(vector, *fork).expect_exception { - // Execution result was unsuccessful and an exception was expected. - // TODO: See if we want to map revm exceptions to expected exceptions, probably not. - Some(_expected_exception) => {} - // Execution result was unsuccessful but no exception was expected. - None => { - println!( - "Unexpected exception on REVM runner, line: {} . Name: {}, vector: {:?}, error: {:?}", - line!(), - &test.name, - vector, - err - ); - return Err(EFTestRunnerError::ExecutionFailedUnexpectedly( - ethrex_levm::errors::InternalError::Custom(format!( - "Unexpected exception on REVM runner, line: {}: {err:?}", - line!() - )) - .into(), //TODO: Use another kind of error for this. - )); - } - } - } - }; - Ok(()) -} - -pub fn fork_to_spec_id(fork: Fork) -> SpecId { - match fork { - Fork::Frontier => SpecId::FRONTIER, - Fork::FrontierThawing => SpecId::FRONTIER_THAWING, - Fork::Homestead => SpecId::HOMESTEAD, - Fork::DaoFork => SpecId::DAO_FORK, - Fork::Tangerine => SpecId::TANGERINE, - Fork::SpuriousDragon => SpecId::SPURIOUS_DRAGON, - Fork::Byzantium => SpecId::BYZANTIUM, - Fork::Constantinople => SpecId::CONSTANTINOPLE, - Fork::Petersburg => SpecId::PETERSBURG, - Fork::Istanbul => SpecId::ISTANBUL, - Fork::MuirGlacier => SpecId::MUIR_GLACIER, - Fork::Berlin => SpecId::BERLIN, - Fork::London => SpecId::LONDON, - Fork::ArrowGlacier => SpecId::ARROW_GLACIER, - Fork::GrayGlacier => SpecId::GRAY_GLACIER, - Fork::Paris => SpecId::MERGE, - Fork::Shanghai => SpecId::SHANGHAI, - Fork::Cancun => SpecId::CANCUN, - Fork::Prague => SpecId::PRAGUE, - Fork::Osaka => SpecId::OSAKA, - Fork::BPO1 => SpecId::OSAKA, - Fork::BPO2 => SpecId::OSAKA, - Fork::BPO3 => SpecId::OSAKA, - Fork::BPO4 => SpecId::OSAKA, - Fork::BPO5 => SpecId::OSAKA, - Fork::Amsterdam => SpecId::OSAKA, // Amsterdam maps to OSAKA until revm adds AMSTERDAM SpecId - } -} - -pub fn infer_tx_type(tx: &EFTestTransaction) -> TxType { - if tx.authorization_list.is_some() { - TxType::EIP7702 - } else if !tx.blob_versioned_hashes.is_empty() { - TxType::EIP4844 - } else if !tx.access_list.is_empty() { - TxType::EIP2930 - } else if tx.max_priority_fee_per_gas.is_some() { - TxType::EIP1559 - } else { - TxType::Legacy - } -} diff --git a/tooling/ef_tests/state/tests/all.rs b/tooling/ef_tests/state/tests/all.rs deleted file mode 100644 index d1a0ce558f0..00000000000 --- a/tooling/ef_tests/state/tests/all.rs +++ /dev/null @@ -1,15 +0,0 @@ -use clap::Parser; -use ef_tests_state::{ - parser, - runner::{self, EFTestRunnerOptions}, -}; -use std::error::Error; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let opts = EFTestRunnerOptions::parse(); - dbg!(&opts); // Useful for testing. - let ef_tests = parser::parse_ef_tests(&opts)?; - runner::run_ef_tests(ef_tests, &opts).await?; - Ok(()) -} diff --git a/tooling/ef_tests/state/types.rs b/tooling/ef_tests/state/types.rs deleted file mode 100644 index 75e5e0a04f6..00000000000 --- a/tooling/ef_tests/state/types.rs +++ /dev/null @@ -1,293 +0,0 @@ -use crate::{ - deserialize::{ - deserialize_access_lists, deserialize_authorization_lists, - deserialize_ef_post_value_indexes, deserialize_h256_vec_optional_safe, - deserialize_hex_bytes, deserialize_hex_bytes_vec, deserialize_post, - deserialize_transaction_expected_exception, deserialize_u64_safe, deserialize_u64_vec_safe, - deserialize_u256_optional_safe, deserialize_u256_safe, - deserialize_u256_valued_hashmap_safe, deserialize_u256_vec_safe, - }, - report::TestVector, -}; -use bytes::Bytes; -use ethrex_common::{ - Address, H256, U256, - types::{Fork, Genesis, GenesisAccount, TxKind}, -}; -use serde::Deserialize; -use std::collections::{BTreeMap, HashMap}; - -#[derive(Debug)] -pub struct EFTests(pub Vec); - -#[derive(Debug)] -pub struct EFTest { - pub name: String, - pub dir: String, - pub _info: EFTestInfo, - pub env: EFTestEnv, - pub post: EFTestPost, - pub pre: EFTestPre, - pub transactions: HashMap, -} - -impl From<&EFTest> for Genesis { - fn from(test: &EFTest) -> Self { - Genesis { - alloc: { - let mut alloc = BTreeMap::new(); - for (account, ef_test_pre_value) in test.pre.0.iter() { - alloc.insert(*account, ef_test_pre_value.into()); - } - alloc - }, - coinbase: test.env.current_coinbase, - difficulty: test.env.current_difficulty, - gas_limit: test.env.current_gas_limit, - mix_hash: test.env.current_random.unwrap_or_default(), - timestamp: test.env.current_timestamp.try_into().unwrap(), - base_fee_per_gas: test.env.current_base_fee.map(|v| v.try_into().unwrap()), - excess_blob_gas: test - .env - .current_excess_blob_gas - .map(|v| v.try_into().unwrap()), - ..Default::default() - } - } -} - -#[derive(Debug, Deserialize, Clone)] -pub struct EFTestInfo { - #[serde(default)] - pub comment: Option, - #[serde(rename = "filling-rpc-server", default)] - pub filling_rpc_server: Option, - #[serde(rename = "filling-tool-version", default)] - pub filling_tool_version: Option, - #[serde(rename = "generatedTestHash", default)] - pub generated_test_hash: Option, - #[serde(default)] - pub labels: Option>, - #[serde(default)] - pub lllcversion: Option, - #[serde(default)] - pub solidity: Option, - #[serde(default)] - pub source: Option, - #[serde(rename = "sourceHash", default)] - pub source_hash: Option, - - // These fields are implemented in the new version of the test vectors (Prague). - #[serde(rename = "hash", default)] - pub hash: Option, - #[serde(rename = "filling-transition-tool", default)] - pub filling_transition_tool: Option, - #[serde(default)] - pub description: Option, - #[serde(default)] - pub url: Option, - #[serde(rename = "fixture_format", default)] - pub fixture_format: Option, - #[serde(rename = "reference-spec", default)] - pub reference_spec: Option, - #[serde(rename = "reference-spec-version", default)] - pub reference_spec_version: Option, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct EFTestEnv { - #[serde(default, deserialize_with = "deserialize_u256_optional_safe")] - pub current_base_fee: Option, - pub current_coinbase: Address, - #[serde(deserialize_with = "deserialize_u256_safe")] - pub current_difficulty: U256, - #[serde(default, deserialize_with = "deserialize_u256_optional_safe")] - pub current_excess_blob_gas: Option, - #[serde(deserialize_with = "deserialize_u64_safe")] - pub current_gas_limit: u64, - #[serde(deserialize_with = "deserialize_u256_safe")] - pub current_number: U256, - pub current_random: Option, - #[serde(deserialize_with = "deserialize_u256_safe")] - pub current_timestamp: U256, - #[serde(default, deserialize_with = "deserialize_u256_optional_safe")] - pub slot_number: Option, -} - -#[derive(Debug, Deserialize)] -pub struct EFTestPost { - #[serde(flatten)] - #[serde(deserialize_with = "deserialize_post")] - pub forks: HashMap>, -} - -impl EFTestPost { - pub fn vector_post_value(&self, vector: &TestVector, fork: Fork) -> EFTestPostValue { - let post_values = self.forks.get(&fork).unwrap(); - Self::find_vector_post_value(post_values, vector).unwrap() - } - - fn find_vector_post_value( - values: &[EFTestPostValue], - vector: &TestVector, - ) -> Option { - values - .iter() - .find(|v| { - let data_index: usize = (*v.indexes.get("data").unwrap()).try_into().unwrap(); - let gas_limit_index: usize = (*v.indexes.get("gas").unwrap()).try_into().unwrap(); - let value_index: usize = (*v.indexes.get("value").unwrap()).try_into().unwrap(); - vector == &(data_index, gas_limit_index, value_index) - }) - .cloned() - } - - pub fn has_vector_for_fork(&self, vector: &TestVector, fork: Fork) -> bool { - self.forks - .get(&fork) - .and_then(|values| Self::find_vector_post_value(values, vector)) - .is_some() - } -} - -#[derive(Debug, Deserialize, Clone)] -pub enum TransactionExpectedException { - InitcodeSizeExceeded, - NonceIsMax, - Type3TxBlobCountExceeded, - Type3TxZeroBlobs, - Type3TxContractCreation, - Type3TxInvalidBlobVersionedHash, - Type4TxContractCreation, - IntrinsicGasTooLow, - IntrinsicGasBelowFloorGasCost, - InsufficientAccountFunds, - SenderNotEoa, - PriorityGreaterThanMaxFeePerGas, - GasAllowanceExceeded, - InsufficientMaxFeePerGas, - RlpInvalidValue, - GasLimitPriceProductOverflow, - Type3TxPreFork, - InsufficientMaxFeePerBlobGas, - TxMaxGasLimitExceeded, - Other, //TODO: Implement exceptions -} - -#[derive(Debug, Deserialize, Clone)] -pub struct EFTestPostValue { - #[serde( - rename = "expectException", - default, - deserialize_with = "deserialize_transaction_expected_exception" - )] - pub expect_exception: Option>, - pub hash: H256, - #[serde(deserialize_with = "deserialize_ef_post_value_indexes")] - pub indexes: HashMap, - pub logs: H256, - // we add the default because some tests don't have this field - #[serde(default, deserialize_with = "deserialize_hex_bytes")] - pub txbytes: Bytes, -} - -#[derive(Debug, Deserialize)] -pub struct EFTestPre(pub HashMap); - -#[derive(Debug, Deserialize)] -pub struct EFTestPreValue { - #[serde(deserialize_with = "deserialize_u256_safe")] - pub balance: U256, - #[serde(deserialize_with = "deserialize_hex_bytes")] - pub code: Bytes, - #[serde(deserialize_with = "deserialize_u64_safe")] - pub nonce: u64, - #[serde(deserialize_with = "deserialize_u256_valued_hashmap_safe")] - pub storage: HashMap, -} - -impl From<&EFTestPreValue> for GenesisAccount { - fn from(value: &EFTestPreValue) -> Self { - Self { - code: value.code.clone(), - storage: value.storage.iter().map(|(k, v)| (*k, *v)).collect(), - balance: value.balance, - nonce: value.nonce, - } - } -} - -#[derive(Debug, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct EFTestAccessListItem { - pub address: Address, - pub storage_keys: Vec, -} - -#[derive(Debug, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct EFTestAuthorizationListTuple { - #[serde(deserialize_with = "deserialize_u256_safe")] - pub chain_id: U256, - pub address: Address, - #[serde(deserialize_with = "deserialize_u64_safe")] - pub nonce: u64, - #[serde(deserialize_with = "deserialize_u256_safe")] - pub v: U256, - #[serde(deserialize_with = "deserialize_u256_safe")] - pub r: U256, - #[serde(deserialize_with = "deserialize_u256_safe")] - pub s: U256, - pub signer: Option
, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct EFTestRawTransaction { - #[serde(deserialize_with = "deserialize_hex_bytes_vec")] - pub data: Vec, - #[serde(deserialize_with = "deserialize_u64_vec_safe")] - pub gas_limit: Vec, - #[serde(default, deserialize_with = "deserialize_u256_optional_safe")] - pub gas_price: Option, - #[serde(deserialize_with = "deserialize_u64_safe")] - pub nonce: u64, - pub secret_key: H256, - pub sender: Address, - pub to: TxKind, - #[serde(deserialize_with = "deserialize_u256_vec_safe")] - pub value: Vec, - #[serde(default, deserialize_with = "deserialize_u256_optional_safe")] - pub max_fee_per_gas: Option, - #[serde(default, deserialize_with = "deserialize_u256_optional_safe")] - pub max_priority_fee_per_gas: Option, - #[serde(default, deserialize_with = "deserialize_u256_optional_safe")] - pub max_fee_per_blob_gas: Option, - #[serde(default, deserialize_with = "deserialize_h256_vec_optional_safe")] - pub blob_versioned_hashes: Option>, - #[serde(default, deserialize_with = "deserialize_access_lists")] - pub access_lists: Option>>, - #[serde(default, deserialize_with = "deserialize_authorization_lists")] - pub authorization_list: Option>, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct EFTestTransaction { - pub data: Bytes, - pub gas_limit: u64, - pub gas_price: Option, - #[serde(deserialize_with = "deserialize_u64_safe")] - pub nonce: u64, - pub secret_key: H256, - pub sender: Address, - pub to: TxKind, - pub value: U256, - pub max_fee_per_gas: Option, - pub max_priority_fee_per_gas: Option, - pub max_fee_per_blob_gas: Option, - pub blob_versioned_hashes: Vec, - pub access_list: Vec, - pub authorization_list: Option>, -} diff --git a/tooling/ef_tests/state/utils.rs b/tooling/ef_tests/state/utils.rs deleted file mode 100644 index f2743462899..00000000000 --- a/tooling/ef_tests/state/utils.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::sync::Arc; - -use crate::{ - runner::{ - EFTestRunnerError, InternalError, - revm_db::{RevmState, revm_state}, - }, - types::{EFTest, EFTestTransaction}, -}; -use ethrex_blockchain::vm::StoreVmDatabase; -use ethrex_common::{H256, U256, types::Genesis}; -use ethrex_levm::db::gen_db::GeneralizedDatabase; -use ethrex_storage::{EngineType, Store}; -use ethrex_vm::DynVmDatabase; - -/// Loads initial state, used for REVM as it contains RevmState. -pub async fn load_initial_state_revm(test: &EFTest) -> (RevmState, H256, Store) { - let genesis = Genesis::from(test); - - let mut storage = Store::new("./temp", EngineType::InMemory).expect("Failed to create Store"); - storage.add_initial_state(genesis.clone()).await.unwrap(); - - let vm_db: DynVmDatabase = - Box::new(StoreVmDatabase::new(storage.clone(), genesis.get_block().header).unwrap()); - - (revm_state(vm_db), genesis.get_block().hash(), storage) -} - -/// Loads initial state, function for LEVM as it does not require RevmState -pub async fn load_initial_state_levm(test: &EFTest) -> GeneralizedDatabase { - let genesis = Genesis::from(test); - - let mut storage = Store::new("./temp", EngineType::InMemory).expect("Failed to create Store"); - storage.add_initial_state(genesis.clone()).await.unwrap(); - - let store: DynVmDatabase = - Box::new(StoreVmDatabase::new(storage, genesis.get_block().header).unwrap()); - - GeneralizedDatabase::new(Arc::new(store)) -} - -// If gas price is not provided, calculate it with current base fee and priority fee -pub fn effective_gas_price( - test: &EFTest, - tx: &&EFTestTransaction, -) -> Result { - match tx.gas_price { - None => { - let current_base_fee = test - .env - .current_base_fee - .ok_or(EFTestRunnerError::Internal( - InternalError::FirstRunInternal("current_base_fee not found".to_string()), - ))?; - let priority_fee = tx - .max_priority_fee_per_gas - .ok_or(EFTestRunnerError::Internal( - InternalError::FirstRunInternal( - "max_priority_fee_per_gas not found".to_string(), - ), - ))?; - let max_fee_per_gas = tx.max_fee_per_gas.ok_or(EFTestRunnerError::Internal( - InternalError::FirstRunInternal("max_fee_per_gas not found".to_string()), - ))?; - - Ok(std::cmp::min( - max_fee_per_gas, - current_base_fee + priority_fee, - )) - } - Some(price) => Ok(price), - } -} diff --git a/tooling/ef_tests/state_v2/.gitignore b/tooling/ef_tests/state_v2/.gitignore deleted file mode 100644 index 071e4791e5f..00000000000 --- a/tooling/ef_tests/state_v2/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Legacy report files (now generated in reports/ directory) -./success_report.txt -./failure_report.txt - -# Generated test reports directory -./reports/ - -# vectors is not yet part of this folder but will be soon. -./vectors diff --git a/tooling/ef_tests/state_v2/Cargo.toml b/tooling/ef_tests/state_v2/Cargo.toml deleted file mode 100644 index 0753cd5f980..00000000000 --- a/tooling/ef_tests/state_v2/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "ef_tests-statev2" -version.workspace = true -edition.workspace = true -authors.workspace = true -documentation.workspace = true -license.workspace = true - -[dependencies] -ethrex-blockchain.workspace = true -ethrex-common.workspace = true -ethrex-crypto.workspace = true -ethrex-storage.workspace = true -ethrex-rlp.workspace = true -ethrex-vm.workspace = true -ethrex-levm.workspace = true -ethrex-l2-rpc.workspace = true - -secp256k1.workspace = true -serde.workspace = true -serde_json.workspace = true -bytes.workspace = true -hex.workspace = true -thiserror.workspace = true -clap.workspace = true -clap_complete.workspace = true -tokio = { workspace = true, features = ["full"] } -rustc-hash = "2.1.1" -colored = "2.1.0" -alloy-rlp = "0.3.12" -chrono = "0.4" -prettytable-rs = "0.10" -rayon.workspace = true - -[dev-dependencies] -hex = "0.4.3" - -[features] -default = ["c-kzg"] -c-kzg = ["ethrex-vm/c-kzg", "ethrex-levm/c-kzg", "ethrex-common/c-kzg"] -block = [] diff --git a/tooling/ef_tests/state_v2/Makefile b/tooling/ef_tests/state_v2/Makefile deleted file mode 100644 index e1107ed30a0..00000000000 --- a/tooling/ef_tests/state_v2/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -.PHONY: help run-tests clean-reports - -help: ## 📚 Show help for each of the Makefile recipes - @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' - -run-tests: ## 🧪 Run all tests with optional flags. Usage: make run-tests flags="--flag1 --flag2" - cargo test --package ef_tests-statev2 --test all --release -- $(flags) - -clean-reports: ## 🗑️ Delete all generated test reports - rm -rf ./reports - diff --git a/tooling/ef_tests/state_v2/README.md b/tooling/ef_tests/state_v2/README.md deleted file mode 100644 index 77b8e32a2ac..00000000000 --- a/tooling/ef_tests/state_v2/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# EF State Tests Runner -This module includes a runner for the EF state tests. The main steps it performs include: -- Parsing the tests, that are written in `.json` files. -- Preparing the necessary execution enviroment for each test case. -- Executing the transaction described in the test case. -- Verifying the expected post state against the obtained one. -- Generating a report with the result of executing the tests. - -## What are EF State Tests? -The Ethereum Foundation State Tests are individual transactions not related to one another that test particular behavior of the EVM. Tests are usually run for multiple forks and the result of execution may vary between forks. - -For more information on these tests check [the docs](https://eest.ethereum.org/main/running_tests/test_formats/state_test/#fixtureconfig). - -## How to run the tests? - -First, make sure you have the EF tests downloaded. This can be achieved by running: -```bash -cd tooling/ef_tests/state -make download-evm-ef-tests -``` - -After the `vectors/` directory is set up, run: - -```bash -make run-new-runner -``` - -This will parse and execute everything in the `./vectors` directory by default. - -> You can also run `cargo run --package ef_tests-statev2 --release` - -## Execution options -In case you do not want to parse and execute everything in the `vectors/` directory there are three flags that can be used to specify files to be run: - -- `path`: it can be used to specify the path where the tests of interest are. It can either be a file or a directory. By default this flag is set to the `./vectors` directory. - -_Example:_ - -```bash -make run-new-runner flags="--path ./vectors/GeneralStateTests" -``` - -```bash -make run-new-runner flags="--path ./vectors/GeneralStateTests/stChainId/chainId.json" -``` - - -- `json-files`: it can be used to specify the `.json` files of interest (no need for full path). If this flag is set to some value, the `path` flag will be ignored. -_Example:_ - -```bash -make run-new-runner flags="--json-files chainId.json,transStorageReset.json" -``` - -> Note that different `.json` files are separated by a comma. - - -- `skip-files`: it can be used to skip certaing `.json` files. -_Example:_ - -```bash -make run-new-runner flags="--skip-files chainId.json,transStorageReset.json" -``` - -## Reports -For tests that succeded, a report can be found at: -`tooling/ef_tests/state_v2/success_report.txt` - -For tests that failed, a report can be found at: -`tooling/ef_tests/state_v2/failure_report.txt` - -If none of the tests failed the report will not get generated at all. In case any of the tests did fail, the report will show the differences with the expected post state for each failing test case. diff --git a/tooling/ef_tests/state_v2/src/lib.rs b/tooling/ef_tests/state_v2/src/lib.rs deleted file mode 100644 index 55e5f1d42f2..00000000000 --- a/tooling/ef_tests/state_v2/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod modules; diff --git a/tooling/ef_tests/state_v2/src/main.rs b/tooling/ef_tests/state_v2/src/main.rs deleted file mode 100644 index 100aba5090f..00000000000 --- a/tooling/ef_tests/state_v2/src/main.rs +++ /dev/null @@ -1,30 +0,0 @@ -#![allow(clippy::all)] - -use clap::Parser; -use ef_tests_statev2::modules::{ - error::RunnerError, - parser::{RunnerOptions, parse_tests}, -}; - -#[tokio::main] -pub async fn main() -> Result<(), RunnerError> { - let mut runner_options = RunnerOptions::parse(); - println!("Runner options: {:#?}", runner_options); - - println!("\nParsing test files..."); - let tests = parse_tests(&mut runner_options)?; - - println!("\nFinished parsing. Executing tests..."); - - if cfg!(feature = "block") { - ef_tests_statev2::modules::block_runner::run_tests(tests.clone()).await?; - } else { - ef_tests_statev2::modules::runner::run_tests(tests).await?; - } - println!( - "\nTests finished running. - Find reports in the './reports' directory. - " - ); - Ok(()) -} diff --git a/tooling/ef_tests/state_v2/src/modules/block_runner.rs b/tooling/ef_tests/state_v2/src/modules/block_runner.rs deleted file mode 100644 index f0c58c2c9d5..00000000000 --- a/tooling/ef_tests/state_v2/src/modules/block_runner.rs +++ /dev/null @@ -1,167 +0,0 @@ -use bytes::Bytes; -use ethrex_blockchain::Blockchain; -use ethrex_blockchain::get_total_blob_gas; -use ethrex_common::constants::DEFAULT_REQUESTS_HASH; -use ethrex_common::types::{ - Block, BlockBody, BlockHeader, Fork, Receipt, Transaction, compute_receipts_root, - compute_transactions_root, -}; -use ethrex_common::{H256, U256}; -use ethrex_crypto::NativeCrypto; -use ethrex_levm::{ - tracing::LevmCallTracer, - vm::{VM, VMType}, -}; -use std::str::FromStr; - -use crate::modules::types::TestCase; -use crate::modules::{ - error::RunnerError, - runner::{get_tx_from_test_case, get_vm_env_for_test}, - types::Test, - utils::load_initial_state, -}; - -pub async fn run_tests(tests: Vec) -> Result<(), RunnerError> { - for test in &tests { - println!("Running test group: {}", test.name); - for test_case in &test.test_cases { - let res = run_test(test, test_case).await; - if let Err(e) = res { - println!("Error: {:?}", e); - } - } - } - - Ok(()) -} - -pub async fn run_test(test: &Test, test_case: &TestCase) -> Result<(), RunnerError> { - // 1. We need to do a pre-execution with LEVM because we need to know gas used and generate receipts for the block header. - let env = get_vm_env_for_test(test.env, test_case)?; - let tx = get_tx_from_test_case(test_case).await?; - let tracer = LevmCallTracer::disabled(); - - let (mut db, initial_block_hash, store, _genesis) = - load_initial_state(test, &test_case.fork).await; - let mut vm = VM::new(env.clone(), &mut db, &tx, tracer, VMType::L1, &NativeCrypto) - .map_err(RunnerError::VMError)?; - let execution_result = vm.execute(); - - let (receipts, gas_used) = match execution_result { - Ok(report) => { - let receipt = Receipt::new( - tx.tx_type(), - report.is_success(), - report.gas_used, - report.logs.clone(), - ); - (vec![receipt], report.gas_used) - } - Err(e) => { - if test_case.post.expected_exceptions.is_some() { - (vec![], 0) - } else { - return Err(RunnerError::Custom(format!("Internal error {e}"))); - } - } - }; - - // 2. Set up Block Body and Block Header - - let transactions = vec![tx.clone()]; - let computed_tx_root = compute_transactions_root(&transactions, ðrex_crypto::NativeCrypto); - let body = BlockBody { - transactions, - ..Default::default() - }; - - let fork = test_case.fork; - // These variables are Some or None depending on the fork. - // So they could be specified in the test but if the fork is e.g. Paris we should set them to None despite that. - // Otherwise it will fail block header validations - let (excess_blob_gas, blob_gas_used, parent_beacon_block_root, requests_hash) = match fork { - Fork::Prague | Fork::Cancun => { - let blob_gas_used = match tx { - Transaction::EIP4844Transaction(blob_tx) => { - Some(get_total_blob_gas(&blob_tx) as u64) - } - _ => Some(0), - }; - - let excess_blob_gas = Some( - test.env - .current_excess_blob_gas - .unwrap_or_default() - .try_into() - .unwrap(), - ); - let parent_beacon_block_root = Some(H256::zero()); - let requests_hash = if fork == Fork::Prague { - Some(*DEFAULT_REQUESTS_HASH) - } else { - None - }; - ( - excess_blob_gas, - blob_gas_used, - parent_beacon_block_root, - requests_hash, - ) - } - _ => (None, None, None, None), - }; - - let header = BlockHeader { - hash: Default::default(), // It is initialized later with block.hash(). - parent_hash: initial_block_hash, - ommers_hash: H256::from_str( - "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", - ) - .unwrap(), - coinbase: test.env.current_coinbase, - state_root: test_case.post.hash, - transactions_root: computed_tx_root, - receipts_root: compute_receipts_root(&receipts, ðrex_crypto::NativeCrypto), - logs_bloom: Default::default(), - difficulty: U256::zero(), - number: 1, // In Ethereum state tests, the block being constructed is always the first block after genesis, which has block number 1. - gas_limit: test.env.current_gas_limit, - gas_used, - timestamp: test.env.current_timestamp.try_into().unwrap(), - extra_data: Bytes::new(), - prev_randao: test.env.current_random.unwrap_or_default(), - nonce: 0, - base_fee_per_gas: test.env.current_base_fee.map(|f| f.try_into().unwrap()), - withdrawals_root: Some( - H256::from_str("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421") - .unwrap(), - ), - blob_gas_used, - excess_blob_gas, - parent_beacon_block_root, - requests_hash, - block_access_list_hash: None, - slot_number: None, - }; - let block = Block::new(header, body); - - // 3. Create Blockchain and add block. - - let blockchain = Blockchain::new(store, ethrex_blockchain::BlockchainOptions::default()); - - let result = blockchain.add_block_pipeline(block, None); - - if result.is_err() && test_case.post.expected_exceptions.is_none() { - return Err(RunnerError::Custom( - "Execution failed but test didn't expect any error.".to_string(), - )); - } - if test_case.post.expected_exceptions.is_some() && result.is_ok() { - return Err(RunnerError::Custom( - "Test expected an error but execution didn't fail.".to_string(), - )); - } - - Ok(()) -} diff --git a/tooling/ef_tests/state_v2/src/modules/deserialize.rs b/tooling/ef_tests/state_v2/src/modules/deserialize.rs deleted file mode 100644 index d8d61457577..00000000000 --- a/tooling/ef_tests/state_v2/src/modules/deserialize.rs +++ /dev/null @@ -1,166 +0,0 @@ -use crate::modules::types::{ - AccessListItem, AuthorizationListTuple, RawPostValue, TransactionExpectedException, -}; - -use ethrex_common::{U256, types::Fork}; -use serde::{Deserialize, Deserializer}; -use std::collections::HashMap; - -pub fn deserialize_transaction_expected_exception<'de, D>( - deserializer: D, -) -> Result>, D::Error> -where - D: Deserializer<'de>, -{ - let option: Option = Option::deserialize(deserializer)?; - - if let Some(value) = option { - let exceptions = value - .split('|') - .map(|s| { - match s.trim() { - "TransactionException.INITCODE_SIZE_EXCEEDED" => { - TransactionExpectedException::InitcodeSizeExceeded - } - "TransactionException.NONCE_IS_MAX" => TransactionExpectedException::NonceIsMax, - "TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED" => { - TransactionExpectedException::Type3TxBlobCountExceeded - } - "TransactionException.TYPE_3_TX_ZERO_BLOBS" => { - TransactionExpectedException::Type3TxZeroBlobs - } - "TransactionException.TYPE_3_TX_CONTRACT_CREATION" => { - TransactionExpectedException::Type3TxContractCreation - } - "TransactionException.TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH" => { - TransactionExpectedException::Type3TxInvalidBlobVersionedHash - } - "TransactionException.INTRINSIC_GAS_TOO_LOW" => { - TransactionExpectedException::IntrinsicGasTooLow - } - "TransactionException.INTRINSIC_GAS_BELOW_FLOOR_GAS_COST" => { - TransactionExpectedException::IntrinsicGasBelowFloorGasCost - } - "TransactionException.INSUFFICIENT_ACCOUNT_FUNDS" => { - TransactionExpectedException::InsufficientAccountFunds - } - "TransactionException.SENDER_NOT_EOA" => { - TransactionExpectedException::SenderNotEoa - } - "TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS" => { - TransactionExpectedException::PriorityGreaterThanMaxFeePerGas - } - "TransactionException.GAS_ALLOWANCE_EXCEEDED" => { - TransactionExpectedException::GasAllowanceExceeded - } - "TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS" => { - TransactionExpectedException::InsufficientMaxFeePerGas - } - "TransactionException.RLP_INVALID_VALUE" => { - TransactionExpectedException::RlpInvalidValue - } - "TransactionException.GASLIMIT_PRICE_PRODUCT_OVERFLOW" => { - TransactionExpectedException::GasLimitPriceProductOverflow - } - "TransactionException.TYPE_3_TX_PRE_FORK" => { - TransactionExpectedException::Type3TxPreFork - } - "TransactionException.TYPE_4_TX_CONTRACT_CREATION" => { - TransactionExpectedException::Type4TxContractCreation - } - "TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS" => { - TransactionExpectedException::InsufficientMaxFeePerBlobGas - } - _other => TransactionExpectedException::Other, //TODO: Support exceptions that enter here. - } - }) - .collect(); - - Ok(Some(exceptions)) - } else { - Ok(None) - } -} - -pub fn deserialize_ef_post_value_indexes<'de, D>( - deserializer: D, -) -> Result, D::Error> -where - D: serde::Deserializer<'de>, -{ - let aux: HashMap = HashMap::deserialize(deserializer)?; - let indexes = aux - .iter() - .map(|(key, value)| (key.clone(), U256::from(*value))) - .collect(); - Ok(indexes) -} - -pub fn deserialize_access_lists<'de, D>( - deserializer: D, -) -> Result>>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let access_lists: Option>>> = - Option::>>>::deserialize(deserializer)?; - - let mut final_access_lists: Vec> = Vec::new(); - - if let Some(access_lists) = access_lists { - for access_list in access_lists { - // Treat `null` as an empty vector - final_access_lists.push(access_list.unwrap_or_default()); - } - } - - Ok(Some(final_access_lists)) -} - -pub fn deserialize_authorization_lists<'de, D>( - deserializer: D, -) -> Result>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let authorization_list: Option> = - Option::>::deserialize(deserializer)?; - - Ok(authorization_list) -} - -pub fn deserialize_post<'de, D>( - deserializer: D, -) -> Result>, D::Error> -where - D: serde::Deserializer<'de>, -{ - let post_deserialized = HashMap::>::deserialize(deserializer)?; - let mut post_parsed = HashMap::new(); - for (fork_str, values) in post_deserialized { - let fork = match fork_str.as_str() { - "Frontier" => Fork::Frontier, - "Homestead" => Fork::Homestead, - "Constantinople" => Fork::Constantinople, - "ConstantinopleFix" | "Petersburg" => Fork::Petersburg, - "Istanbul" => Fork::Istanbul, - "Berlin" => Fork::Berlin, - "London" => Fork::London, - "Paris" | "Merge" => Fork::Paris, - "Shanghai" => Fork::Shanghai, - "Cancun" => Fork::Cancun, - "Prague" => Fork::Prague, - "Byzantium" => Fork::Byzantium, - "EIP158" => Fork::SpuriousDragon, - "EIP150" => Fork::Tangerine, - other => { - return Err(serde::de::Error::custom(format!( - "Unknown fork name: {other}", - ))); - } - }; - post_parsed.insert(fork, values); - } - - Ok(post_parsed) -} diff --git a/tooling/ef_tests/state_v2/src/modules/error.rs b/tooling/ef_tests/state_v2/src/modules/error.rs deleted file mode 100644 index 68a3bdce253..00000000000 --- a/tooling/ef_tests/state_v2/src/modules/error.rs +++ /dev/null @@ -1,10 +0,0 @@ -use ethrex_levm::errors::VMError; - -#[derive(Debug)] -pub enum RunnerError { - FailedToGetAccountsUpdates(String), - VMError(VMError), - EIP7702ShouldNotBeCreateType, - FailedToGetIndexValue(String), - Custom(String), -} diff --git a/tooling/ef_tests/state_v2/src/modules/mod.rs b/tooling/ef_tests/state_v2/src/modules/mod.rs deleted file mode 100644 index a5b40ca2ee9..00000000000 --- a/tooling/ef_tests/state_v2/src/modules/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod block_runner; -pub mod deserialize; -pub mod error; -pub mod parser; -pub mod report; -pub mod result_check; -pub mod runner; -pub mod types; -pub mod utils; diff --git a/tooling/ef_tests/state_v2/src/modules/parser.rs b/tooling/ef_tests/state_v2/src/modules/parser.rs deleted file mode 100644 index b3aba48f805..00000000000 --- a/tooling/ef_tests/state_v2/src/modules/parser.rs +++ /dev/null @@ -1,139 +0,0 @@ -use std::path::PathBuf; - -use crate::modules::{ - error::RunnerError, - types::{Test, Tests}, -}; - -use clap::Parser; -use rayon::iter::{IntoParallelIterator, ParallelIterator}; - -/// Command line flags for runner execution. -#[derive(Parser, Debug)] -pub struct RunnerOptions { - /// For running tests in a specific file (could be either a directory or a .json) - //TODO: Change default path to ./vectors when the other EFTests are replaced by this runner - #[arg(short, long, value_name = "PATH", default_value = "../state/vectors")] - pub path: PathBuf, - /// For running tests in specific .json files. If this is not empty, "path" flag will be ignored. - #[arg(short, long, value_name = "JSON_FILES", value_delimiter = ',')] - pub json_files: Vec, - /// For skipping certain .json files - #[arg(long, value_name = "SKIP_FILES", value_delimiter = ',')] - pub skip_files: Vec, -} - -//TODO: Use this constant, improve it. -const IGNORED_TESTS: &[&str] = &[ - // These tests contain accounts without nonce or code but have storage, which is a virtually impossible scenario. That's why we fail, but that's okay. - // When creating an account we don't check the storage root but just if it has nonce or code, and that's the right check for real case scenarios. - "dynamicAccountOverwriteEmpty_Paris.json", - "RevertInCreateInInitCreate2Paris.json", - "RevertInCreateInInit_Paris.json", - "create2collisionStorageParis.json", - "InitCollisionParis.json", - "InitCollision.json", - // Gas price higher than u64::MAX; impractical scenario. We don't use 256 bits for gas price for performance reasons, however, it's debatable. See https://github.com/lambdaclass/ethrex/issues/3629 - "HighGasPrice.json", - "HighGasPriceParis.json", - // Skip because they take too long to run, but they pass - "static_Call50000_sha256.json", - "CALLBlake2f_MaxRounds.json", - "loopMul.json", - // Skip because it tries to deserialize number > U256::MAX - "ValueOverflow.json", - "ValueOverflowParis.json", - // Skip for now as it requires special transaction type handling in test runner, we should improve that. - "contract_create.json", -]; - -/// Parse a `.json` file of tests into a Vec. -pub fn parse_file(path: &PathBuf, log_parse_file: bool) -> Result, RunnerError> { - if log_parse_file { - println!("Parsing file: {:?}", path); - } - let test_file = std::fs::File::open(path.clone()).unwrap(); - let mut tests: Tests = serde_json::from_reader(test_file).unwrap(); - for test in tests.0.iter_mut() { - test.path = path.clone(); - } - Ok(tests.0) -} - -/// Parse a directory of tests into a Vec. -pub fn parse_dir( - path: &PathBuf, - skipped_files: &Vec, - only_files: &Vec, - log_parse_dir: bool, - log_parse_file: bool, -) -> Result, RunnerError> { - if log_parse_dir { - println!("Parsing test directory: {:?}", path); - } - let dir_entries: Vec<_> = std::fs::read_dir(path.clone()).unwrap().flatten().collect(); - - // Process directory entries in parallel - let directory_tests_results: Vec<_> = dir_entries - .into_par_iter() - .map(|entry| -> Result>, RunnerError> { - // Check entry type - let entry_type = entry.file_type().unwrap(); - if entry_type.is_dir() { - let dir_tests = parse_dir( - &entry.path(), - skipped_files, - only_files, - log_parse_dir, - log_parse_file, - )?; - return Ok(Some(dir_tests)); - } else { - let file_name = PathBuf::from(entry.file_name().as_os_str()); - let is_json_file = entry.path().extension().is_some_and(|ext| ext == "json"); - let is_not_skipped = !skipped_files.contains(&file_name); - // If only certain files were supposed to be parsed make sure this file is among them. - if !only_files.is_empty() && !only_files.contains(&file_name) { - return Ok(None); - } - - if is_json_file && is_not_skipped { - let file_tests = parse_file(&entry.path(), log_parse_file)?; - return Ok(Some(file_tests)); - } - } - Ok(None) - }) - .collect(); - - // Collect all results and flatten - let tests: Vec = directory_tests_results - .into_iter() - .filter_map(|x| x.transpose()) - .collect::, _>>()? - .into_iter() - .flatten() - .collect(); - - Ok(tests) -} - -/// Initiates the parser with the corresponding option flags. -pub fn parse_tests(options: &mut RunnerOptions) -> Result, RunnerError> { - let mut tests = Vec::new(); - let mut skipped: Vec = IGNORED_TESTS.iter().map(PathBuf::from).collect(); - skipped.append(&mut options.skip_files); - - // If the user selected specific `.json` files to be executed, parse only those files from the starting `path`. - if !options.json_files.is_empty() { - let file_tests = parse_dir(&options.path, &skipped, &options.json_files, false, true)?; - tests.push(file_tests); - } else if options.path.ends_with(".json") { - let file_tests = parse_file(&options.path, true)?; - tests.push(file_tests); - } else { - let dir_tests = parse_dir(&options.path, &skipped, &Vec::new(), true, false)?; - tests.push(dir_tests); - } - Ok(tests.concat()) -} diff --git a/tooling/ef_tests/state_v2/src/modules/report.rs b/tooling/ef_tests/state_v2/src/modules/report.rs deleted file mode 100644 index 9a892e2aec9..00000000000 --- a/tooling/ef_tests/state_v2/src/modules/report.rs +++ /dev/null @@ -1,264 +0,0 @@ -use std::{ - fmt, - fs::{self, OpenOptions}, - io::Write, - path::PathBuf, - sync::OnceLock, -}; - -use chrono::Local; -use prettytable::{Cell, Row, Table}; - -use crate::modules::{error::RunnerError, result_check::PostCheckResult, types::Test}; - -/// Static storage for report paths that are initialized once per program run -static REPORT_PATHS: OnceLock<(PathBuf, PathBuf)> = OnceLock::new(); - -/// Ensures the reports directory exists, creating it if necessary -pub fn ensure_reports_dir() -> Result<(), RunnerError> { - let reports_dir = PathBuf::from("./reports"); - if !reports_dir.exists() { - fs::create_dir_all(&reports_dir).map_err(|e| { - RunnerError::Custom(format!("Failed to create reports directory: {}", e)) - })?; - } - Ok(()) -} - -/// Generates timestamped report paths (called only once per run) -fn get_report_paths() -> &'static (PathBuf, PathBuf) { - REPORT_PATHS.get_or_init(|| { - let timestamp = Local::now().format("%Y-%m-%d_%H-%M-%S"); - let success_path = PathBuf::from(format!("./reports/success_report_{}.txt", timestamp)); - let failure_path = PathBuf::from(format!("./reports/failure_report_{}.txt", timestamp)); - (success_path, failure_path) - }) -} - -pub fn add_test_to_report(test_result: (&Test, Vec)) -> Result<(), RunnerError> { - ensure_reports_dir()?; - let (test, failed_test_cases) = test_result; - if failed_test_cases.is_empty() { - write_passing_test_to_report(test); - } else { - write_failing_test_to_report(test, failed_test_cases); - } - Ok(()) -} - -pub fn write_passing_test_to_report(test: &Test) { - let (successful_report_path, _) = get_report_paths(); - let mut report = OpenOptions::new() - .append(true) - .create(true) - .open(successful_report_path) - .unwrap(); - let content = format!("Test {:?} - Path {:?}\n", test.name, test.path); - report.write_all(content.as_bytes()).unwrap() -} - -pub fn write_failing_test_to_report(test: &Test, failing_test_cases: Vec) { - let (_, failing_report_path) = get_report_paths(); - let mut report = OpenOptions::new() - .append(true) - .create(true) - .open(failing_report_path) - .unwrap(); - - // Create header table - let mut header_table = Table::new(); - header_table.add_row(Row::new(vec![ - Cell::new("Test Information").style_spec("Fb"), - ])); - header_table.add_row(Row::new(vec![Cell::new("Name"), Cell::new(&test.name)])); - header_table.add_row(Row::new(vec![ - Cell::new("Path"), - Cell::new(&test.path.display().to_string()), - ])); - header_table.add_row(Row::new(vec![ - Cell::new("Description"), - Cell::new( - &test._info.description.clone().unwrap_or( - test._info - .comment - .clone() - .unwrap_or("No description or comment".to_string()), - ), - ), - ])); - header_table.add_row(Row::new(vec![ - Cell::new("Reference"), - Cell::new( - &test - ._info - .reference_spec - .clone() - .unwrap_or("No reference spec".to_string()), - ), - ])); - - let header_content = format!("{}\n", header_table); - report.write_all(header_content.as_bytes()).unwrap(); - - for check_result in failing_test_cases { - let content = format!("\n{}", check_result); - report.write_all(content.as_bytes()).unwrap(); - } - let dividing_line = - "\n═══════════════════════════════════════════════════════════════════════\n\n".to_string(); - let _ = report.write_all(dividing_line.as_bytes()); -} - -impl fmt::Display for PostCheckResult { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Fork and indexes table - let mut info_table = Table::new(); - info_table.add_row(Row::new(vec![ - Cell::new("Fork"), - Cell::new("Data Idx"), - Cell::new("Gas Idx"), - Cell::new("Val Idx"), - Cell::new("Status"), - ])); - info_table.add_row(Row::new(vec![ - Cell::new(&format!("{:?}", self.fork)), - Cell::new(&self.vector.0.to_string()), - Cell::new(&self.vector.1.to_string()), - Cell::new(&self.vector.2.to_string()), - Cell::new("FAILED").style_spec("Fr"), - ])); - writeln!(f, "{}", info_table)?; - - // Root mismatch - if let Some(root_mismatch) = self.root_diff { - let (expected_root, actual_root) = root_mismatch; - writeln!(f, "\nERROR: Root Mismatch")?; - let mut root_table = Table::new(); - root_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")])); - root_table.add_row(Row::new(vec![ - Cell::new("Expected"), - Cell::new(&format!("{:?}", expected_root)), - ])); - root_table.add_row(Row::new(vec![ - Cell::new("Actual"), - Cell::new(&format!("{:?}", actual_root)), - ])); - writeln!(f, "{}", root_table)?; - } - - // Exception mismatch - if let Some(exception_diff) = self.exception_diff.clone() { - let (expected_exception, actual_exception) = exception_diff; - writeln!(f, "\nERROR: Exception Mismatch")?; - let mut exception_table = Table::new(); - exception_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")])); - exception_table.add_row(Row::new(vec![ - Cell::new("Expected"), - Cell::new(&format!("{:?}", expected_exception)), - ])); - exception_table.add_row(Row::new(vec![ - Cell::new("Actual"), - Cell::new(&format!("{:?}", actual_exception)), - ])); - writeln!(f, "{}", exception_table)?; - } - - // Logs mismatch - if let Some(logs_mismatch) = self.logs_diff { - let (expected_log_hash, actual_log_hash) = logs_mismatch; - writeln!(f, "\nERROR: Logs Mismatch")?; - let mut logs_table = Table::new(); - logs_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")])); - logs_table.add_row(Row::new(vec![ - Cell::new("Expected"), - Cell::new(&format!("{:?}", expected_log_hash)), - ])); - logs_table.add_row(Row::new(vec![ - Cell::new("Actual"), - Cell::new(&format!("{:?}", actual_log_hash)), - ])); - writeln!(f, "{}", logs_table)?; - } - - // Account mismatches - if let Some(account_mismatches) = self.accounts_diff.clone() { - for acc_mismatch in account_mismatches { - writeln!(f, "\nERROR: Account State Mismatch")?; - writeln!(f, "Address: {:?}", acc_mismatch.address)?; - - if let Some(balance_diff) = acc_mismatch.balance_diff { - let (expected_balance, actual_balance) = balance_diff; - let net_difference = expected_balance.abs_diff(actual_balance); - let difference_sign = if expected_balance > actual_balance { - "-" - } else { - "+" - }; - - let mut balance_table = Table::new(); - balance_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")])); - balance_table.add_row(Row::new(vec![ - Cell::new("Expected Balance"), - Cell::new(&format!("{:?}", expected_balance)), - ])); - balance_table.add_row(Row::new(vec![ - Cell::new("Actual Balance"), - Cell::new(&format!("{:?}", actual_balance)), - ])); - balance_table.add_row(Row::new(vec![ - Cell::new("Difference"), - Cell::new(&format!("{}{:?}", difference_sign, net_difference)), - ])); - writeln!(f, "{}", balance_table)?; - } - - if let Some(nonce_diff) = acc_mismatch.nonce_diff { - let (expected_nonce, actual_nonce) = nonce_diff; - let mut nonce_table = Table::new(); - nonce_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")])); - nonce_table.add_row(Row::new(vec![ - Cell::new("Expected Nonce"), - Cell::new(&format!("{:?}", expected_nonce)), - ])); - nonce_table.add_row(Row::new(vec![ - Cell::new("Actual Nonce"), - Cell::new(&format!("{:?}", actual_nonce)), - ])); - writeln!(f, "{}", nonce_table)?; - } - - if let Some(code_diff) = acc_mismatch.code_diff { - let (expected_code_hash, actual_code_hash) = code_diff; - let mut code_table = Table::new(); - code_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")])); - code_table.add_row(Row::new(vec![ - Cell::new("Expected Code Hash"), - Cell::new(&format!("0x{}", hex::encode(expected_code_hash))), - ])); - code_table.add_row(Row::new(vec![ - Cell::new("Actual Code Hash"), - Cell::new(&format!("0x{}", hex::encode(actual_code_hash))), - ])); - writeln!(f, "{}", code_table)?; - } - - if let Some(storage_diff) = acc_mismatch.storage_diff { - let (expected_storage, actual_storage) = storage_diff; - let mut storage_table = Table::new(); - storage_table.add_row(Row::new(vec![Cell::new("Type"), Cell::new("Value")])); - storage_table.add_row(Row::new(vec![ - Cell::new("Expected Storage"), - Cell::new(&format!("{:?}", expected_storage)), - ])); - storage_table.add_row(Row::new(vec![ - Cell::new("Actual Storage"), - Cell::new(&format!("{:?}", actual_storage)), - ])); - writeln!(f, "{}", storage_table)?; - } - } - } - - Ok(()) - } -} diff --git a/tooling/ef_tests/state_v2/src/modules/result_check.rs b/tooling/ef_tests/state_v2/src/modules/result_check.rs deleted file mode 100644 index 7a981a019b7..00000000000 --- a/tooling/ef_tests/state_v2/src/modules/result_check.rs +++ /dev/null @@ -1,344 +0,0 @@ -use ethrex_common::H256; -use ethrex_common::utils::keccak; -use ethrex_common::{ - Address, U256, - types::{AccountUpdate, Fork, Genesis, code_hash}, -}; -use ethrex_levm::{ - account::LevmAccount, - db::gen_db::GeneralizedDatabase, - errors::{ExecutionReport, TxValidationError, VMError}, - vm::VM, -}; -use ethrex_rlp::encode::RLPEncode; -use ethrex_storage::Store; -use ethrex_vm::backends; -use rustc_hash::FxHashMap; - -use crate::modules::{ - error::RunnerError, - types::{AccountState, TestCase, TransactionExpectedException}, -}; - -/// Keeps record of the post checks results for a test case, including if it passed -/// and if it did not, the differences from the expected post state. -pub struct PostCheckResult { - pub fork: Fork, - pub vector: (usize, usize, usize), - pub passed: bool, - pub root_diff: Option<(H256, H256)>, - pub accounts_diff: Option>, - pub logs_diff: Option<(H256, H256)>, - pub exception_diff: Option<(Vec, Option)>, -} -impl Default for PostCheckResult { - fn default() -> Self { - Self { - fork: Fork::Prague, - vector: (0, 0, 0), - passed: true, - root_diff: None, - accounts_diff: None, - logs_diff: None, - exception_diff: None, - } - } -} - -#[derive(Debug, Clone, Default, PartialEq, Eq)] -pub struct AccountMismatch { - pub address: Address, - pub balance_diff: Option<(U256, U256)>, - pub nonce_diff: Option<(u64, u64)>, - pub code_diff: Option<(H256, H256)>, - pub storage_diff: Option<(FxHashMap, FxHashMap)>, -} - -/// Verify if the test has reached the expected results: if an exception was expected, check it was the corresponding -/// exception. If no exception was expected verify the result root. -pub async fn check_test_case_results( - vm: &mut VM<'_>, - initial_block_hash: H256, - store: Store, - test_case: &TestCase, - execution_result: Result, - genesis: Genesis, -) -> Result { - let mut checks_result = PostCheckResult { - fork: test_case.fork, - vector: test_case.vector, - ..Default::default() - }; - - if let Some(expected_exceptions) = test_case.post.expected_exceptions.clone() { - // Verify in case an exception was expected. - check_exception(expected_exceptions, execution_result, &mut checks_result); - Ok(checks_result) - } else { - // Verify expected root hash. - check_root(vm, initial_block_hash, store, test_case, &mut checks_result).await?; - // We only compare the other fields if there is a root mismatch, otherwise, test has passed. - if !checks_result.passed { - // Verify hashed logs. - check_logs( - test_case, - &execution_result.clone().unwrap(), - &mut checks_result, - ); - - // Verify accounts' post state. - check_accounts_state(&mut vm.db.clone(), test_case, &mut checks_result, genesis); - } - - Ok(checks_result) - } -} - -/// Verifies that the root of the state after executing the tests is the one expected -/// (the one that appears in the `.json` file). -pub async fn check_root( - vm: &mut VM<'_>, - initial_block_hash: H256, - store: Store, - test_case: &TestCase, - check_result: &mut PostCheckResult, -) -> Result<(), RunnerError> { - let account_updates = backends::levm::LEVM::get_state_transitions(&mut vm.db.clone()) - .map_err(|e| RunnerError::FailedToGetAccountsUpdates(e.to_string()))?; - let post_state_root = post_state_root(&account_updates, initial_block_hash, store); - if post_state_root != test_case.post.hash { - check_result.passed = false; - check_result.root_diff = Some((test_case.post.hash, post_state_root)); - } - Ok(()) -} - -/// Calculates the post state root applying the changes (the account updates) that are a -/// result of running the transaction to the storage. -pub fn post_state_root( - account_updates: &[AccountUpdate], - initial_block_hash: H256, - store: Store, -) -> H256 { - let ret_account_updates_batch = store - .apply_account_updates_batch(initial_block_hash, account_updates) - .unwrap() - .unwrap(); - ret_account_updates_batch.state_trie_hash -} - -/// Used when the test case expected an exception. Verifies first if it, indeed, failed -/// and if it did if it failed with the corresponding error. -pub fn check_exception( - expected_exceptions: Vec, - execution_result: Result, - check_result: &mut PostCheckResult, -) { - if execution_result.is_err() { - let execution_err = execution_result.err().unwrap(); - if !exception_matches_expected(expected_exceptions.clone(), execution_err.clone()) { - check_result.exception_diff = Some((expected_exceptions, Some(execution_err))); - check_result.passed = false; - } - } else { - check_result.exception_diff = Some((expected_exceptions, None)); - check_result.passed = false; - } -} - -/// Verifies whether a transaction execution error is contained in a vector of -/// expected exceptions. -fn exception_matches_expected( - expected_exceptions: Vec, - returned_error: VMError, -) -> bool { - expected_exceptions.iter().any(|exception| { - matches!( - (exception, &returned_error), - ( - TransactionExpectedException::IntrinsicGasTooLow, - VMError::TxValidation(TxValidationError::IntrinsicGasTooLow) - ) | ( - TransactionExpectedException::IntrinsicGasBelowFloorGasCost, - VMError::TxValidation(TxValidationError::IntrinsicGasBelowFloorGasCost) - ) | ( - TransactionExpectedException::InsufficientAccountFunds, - VMError::TxValidation(TxValidationError::InsufficientAccountFunds) - ) | ( - TransactionExpectedException::PriorityGreaterThanMaxFeePerGas, - VMError::TxValidation(TxValidationError::PriorityGreaterThanMaxFeePerGas { - priority_fee: _, - max_fee_per_gas: _ - }) - ) | ( - TransactionExpectedException::GasLimitPriceProductOverflow, - VMError::TxValidation(TxValidationError::GasLimitPriceProductOverflow) - ) | ( - TransactionExpectedException::SenderNotEoa, - VMError::TxValidation(TxValidationError::SenderNotEOA(_)) - ) | ( - TransactionExpectedException::InsufficientMaxFeePerGas, - VMError::TxValidation(TxValidationError::InsufficientMaxFeePerGas) - ) | ( - TransactionExpectedException::NonceIsMax, - VMError::TxValidation(TxValidationError::NonceIsMax) - ) | ( - TransactionExpectedException::GasAllowanceExceeded, - VMError::TxValidation(TxValidationError::GasAllowanceExceeded { - block_gas_limit: _, - tx_gas_limit: _ - }) - ) | ( - TransactionExpectedException::Type3TxPreFork, - VMError::TxValidation(TxValidationError::Type3TxPreFork) - ) | ( - TransactionExpectedException::Type3TxBlobCountExceeded, - VMError::TxValidation(TxValidationError::Type3TxBlobCountExceeded { - max_blob_count: _, - actual_blob_count: _ - }) - ) | ( - TransactionExpectedException::Type3TxZeroBlobs, - VMError::TxValidation(TxValidationError::Type3TxZeroBlobs) - ) | ( - TransactionExpectedException::Type3TxContractCreation, - VMError::TxValidation(TxValidationError::Type3TxContractCreation) - ) | ( - TransactionExpectedException::Type3TxInvalidBlobVersionedHash, - VMError::TxValidation(TxValidationError::Type3TxInvalidBlobVersionedHash) - ) | ( - TransactionExpectedException::InsufficientMaxFeePerBlobGas, - VMError::TxValidation(TxValidationError::InsufficientMaxFeePerBlobGas { - base_fee_per_blob_gas: _, - tx_max_fee_per_blob_gas: _ - }) - ) | ( - TransactionExpectedException::InitcodeSizeExceeded, - VMError::TxValidation(TxValidationError::InitcodeSizeExceeded { - max_size: _, - actual_size: _ - }) - ) | ( - TransactionExpectedException::Type4TxContractCreation, - VMError::TxValidation(TxValidationError::Type4TxContractCreation) - ) | ( - TransactionExpectedException::Other, - VMError::TxValidation(_) //TODO: Decide whether to support more specific errors, I think this is enough. - ) - ) - }) -} - -/// Verifies the hash of the output logs is the one expected. -pub fn check_logs( - test_case: &TestCase, - execution_report: &ExecutionReport, - checks_result: &mut PostCheckResult, -) { - let mut encoded_logs = Vec::new(); - execution_report.logs.encode(&mut encoded_logs); - let hashed_logs = keccak(encoded_logs); - if test_case.post.logs != hashed_logs { - checks_result.passed = false; - checks_result.logs_diff = Some((test_case.post.logs, hashed_logs)); - } -} - -/// If the test case provides a `state` field in the post section, check account by account -/// its state is the one expected after the execution of the transaction. -pub fn check_accounts_state( - db: &mut GeneralizedDatabase, - test_case: &TestCase, - check_result: &mut PostCheckResult, - genesis: Genesis, -) { - // In this case, the test in the .json file does not have post account details to verify. - let Some(expected_accounts_state) = test_case.post.state.clone() else { - return; - }; - let mut accounts_diff: Vec = Vec::new(); - - // For every account in the test case expected post state, compare it to the actual state. - for (addr, expected_account) in expected_accounts_state { - let acc_genesis_state = genesis.alloc.get(&addr); - // First we check if the address appears in the `current_accounts_state`, which stores accounts modified by the tx. - let account: &mut LevmAccount = if let Some(account) = - db.current_accounts_state.get_mut(&addr) - && account.storage.is_empty() - && acc_genesis_state.is_some() - { - account.storage = acc_genesis_state - .unwrap() - .storage - .iter() - .map(|(k, v)| (H256::from(k.to_big_endian()), *v)) - .collect(); - account - } else { - // Else, we take its info from the Genesis state, assuming it has not changed. - if let Some(account) = acc_genesis_state { - &mut Into::::into(account.clone()) - } else { - // If we can't find it in any of the previous mappings, we provide a default account that will not pass - // the comparisons checks. - &mut LevmAccount::default() - } - }; - - // We verify if the account matches the expected post state. - let account_mismatch = verify_matching_accounts(addr, account, &expected_account); - if let Some(mismatch) = account_mismatch { - accounts_diff.push(mismatch); - } - } - - // If any of the accounts comparisons produced a mismatch, register it in the checks result. - // If we got to this point, root did not match, therefore the test has already been registered - // as failing, we do not need to set it again and just add the account diff. - if !accounts_diff.is_empty() { - check_result.accounts_diff = Some(accounts_diff); - } -} - -/// Compare every field of the expected account vs. the actual obtained account state. -fn verify_matching_accounts( - addr: Address, - actual_account: &LevmAccount, - expected_account: &AccountState, -) -> Option { - let mut formatted_expected_storage = FxHashMap::default(); - for (key, value) in &expected_account.storage { - let formatted_key = H256::from(key.to_big_endian()); - formatted_expected_storage.insert(formatted_key, *value); - } - let mut account_mismatch = AccountMismatch::default(); - let code_matches = actual_account.info.code_hash == keccak(&expected_account.code); - let balance_matches = actual_account.info.balance == expected_account.balance; - let nonce_matches = actual_account.info.nonce == expected_account.nonce; - let storage_matches = formatted_expected_storage == actual_account.storage; - - if !code_matches { - account_mismatch.code_diff = Some(( - code_hash(&expected_account.code, ðrex_crypto::NativeCrypto), - actual_account.info.code_hash, - )); - } - if !balance_matches { - account_mismatch.balance_diff = - Some((expected_account.balance, actual_account.info.balance)); - } - if !nonce_matches { - account_mismatch.nonce_diff = Some((expected_account.nonce, actual_account.info.nonce)); - } - if !storage_matches { - account_mismatch.storage_diff = - Some((formatted_expected_storage, actual_account.storage.clone())); - } - - if account_mismatch != AccountMismatch::default() { - account_mismatch.address = addr; - Some(account_mismatch) - } else { - None - } -} diff --git a/tooling/ef_tests/state_v2/src/modules/runner.rs b/tooling/ef_tests/state_v2/src/modules/runner.rs deleted file mode 100644 index 9d9e2ce1c14..00000000000 --- a/tooling/ef_tests/state_v2/src/modules/runner.rs +++ /dev/null @@ -1,264 +0,0 @@ -use colored::Colorize; -use ethrex_l2_rpc::signer::{LocalSigner, Signable, Signer}; -use secp256k1::SecretKey; - -use ethrex_common::{ - U256, - types::{ - EIP1559Transaction, EIP2930Transaction, EIP4844Transaction, EIP7702Transaction, - LegacyTransaction, Transaction, TxKind, - }, -}; -use ethrex_crypto::NativeCrypto; -use ethrex_levm::{ - EVMConfig, Environment, tracing::LevmCallTracer, utils::get_base_fee_per_blob_gas, vm::VM, - vm::VMType, -}; - -use crate::modules::{ - error::RunnerError, - report::{add_test_to_report, ensure_reports_dir}, - result_check::check_test_case_results, - types::{Env, Test, TestCase}, - utils::{effective_gas_price, load_initial_state}, -}; - -/// Runs all the tests that have been parsed. -pub async fn run_tests(tests: Vec) -> Result<(), RunnerError> { - // Ensure reports directory exists - ensure_reports_dir()?; - let mut passing_tests = 0; - let mut failing_tests = 0; - let mut total_run = 0; - - //Test with the Fusaka tests that should pass. TODO: Once we've implemented all the Fusaka EIPs this should be removed - //EIPs should be added as strings in the format 'eip-XXXX' - let fusaka_eips_to_test: Vec<&str> = - vec!["eip-7594", "eip-7939", "eip-7918", "eip-7892", "eip-7883"]; - - for test in tests { - let test_eip = test._info.clone().reference_spec.unwrap_or_default(); - - if test.path.to_str().unwrap().contains("osaka") - && !fusaka_eips_to_test.iter().any(|eip| test_eip.contains(eip)) - { - continue; - } - run_test( - &test, - &mut passing_tests, - &mut failing_tests, - &mut total_run, - ) - .await?; - } - Ok(()) -} - -/// Runs each individual test case (combination of ) of a specific test. -pub async fn run_test( - test: &Test, - passing_tests: &mut usize, - failing_tests: &mut usize, - total_run: &mut usize, -) -> Result<(), RunnerError> { - let mut failing_test_cases = Vec::new(); - for test_case in &test.test_cases { - // Setup VM for transaction. - let (mut db, initial_block_hash, storage, genesis) = - load_initial_state(test, &test_case.fork).await; - let env = get_vm_env_for_test(test.env, test_case)?; - let tx = get_tx_from_test_case(test_case).await?; - let tracer = LevmCallTracer::disabled(); - let mut vm = VM::new(env, &mut db, &tx, tracer, VMType::L1, &NativeCrypto) - .map_err(RunnerError::VMError)?; - - // Execute transaction with VM. - let execution_result = vm.execute(); - - // Verify transaction execution results where the ones expected by the test case. - let checks_result = check_test_case_results( - &mut vm, - initial_block_hash, - storage, - test_case, - execution_result, - genesis, - ) - .await?; - - // If test case did not pass the checks, add it to failing test cases record (for future reporting) - if !checks_result.passed { - failing_test_cases.push(checks_result); - *failing_tests += 1; - } else { - *passing_tests += 1; - } - *total_run += 1; - - print!( - "\rTotal tests ran: {} - Total passed: {} - Total failed: {}", - format!("{}", total_run).blue(), - format!("{}", passing_tests).green(), - format!("{}", failing_tests).red() - ); - } - add_test_to_report((test, failing_test_cases))?; - - Ok(()) -} - -/// Gets the enviroment needed to prepare the VM for a transaction. -pub fn get_vm_env_for_test( - test_env: Env, - test_case: &TestCase, -) -> Result { - let blob_schedule = EVMConfig::canonical_values(test_case.fork); - let config = EVMConfig::new(test_case.fork, blob_schedule); - let gas_price = effective_gas_price(&test_env, test_case)?; - let base_blob_fee_per_gas = get_base_fee_per_blob_gas( - test_env - .current_excess_blob_gas - .map(|x| x.try_into().unwrap()), - &config, - ) - .map_err(|e| RunnerError::Custom(format!("Failed to get blob base fee: {e}")))?; - Ok(Environment { - origin: test_case.sender, - gas_limit: test_case.gas, - config, - block_number: test_env.current_number.try_into().unwrap(), - coinbase: test_env.current_coinbase, - timestamp: test_env.current_timestamp.try_into().unwrap(), - prev_randao: test_env.current_random, - difficulty: test_env.current_difficulty, - slot_number: test_env.slot_number.unwrap_or_default(), - chain_id: U256::from(1), - base_fee_per_gas: test_env.current_base_fee.unwrap_or_default(), - base_blob_fee_per_gas, - gas_price, - block_excess_blob_gas: test_env - .current_excess_blob_gas - .map(|x| x.try_into().unwrap()), - block_blob_gas_used: None, - tx_blob_hashes: test_case.blob_versioned_hashes.clone(), - tx_max_priority_fee_per_gas: test_case.max_priority_fee_per_gas, - tx_max_fee_per_gas: test_case.max_fee_per_gas, - tx_max_fee_per_blob_gas: test_case.max_fee_per_blob_gas, - tx_nonce: test_case.nonce, - block_gas_limit: test_env.current_gas_limit, - is_privileged: false, - fee_token: None, - disable_balance_check: false, - }) -} - -/// Constructs the transaction that will be executed in a specific test case. -pub async fn get_tx_from_test_case(test_case: &TestCase) -> Result { - let value = test_case.value; - let data = test_case.data.clone(); - let nonce = test_case.nonce; - let to = test_case.to.clone(); - let chain_id = 1; // It's actually in the test config but it's always 1 I believe. - let access_list = test_case - .access_list - .iter() - .map(|list_item| (list_item.address, list_item.storage_keys.clone())) - .collect(); - - let mut tx = if let Some(list) = &test_case.authorization_list { - Transaction::EIP7702Transaction(EIP7702Transaction { - to: match to { - TxKind::Call(to) => to, - TxKind::Create => return Err(RunnerError::EIP7702ShouldNotBeCreateType), - }, - value, - data, - access_list, - authorization_list: list - .iter() - .map(|auth_tuple| auth_tuple.clone().into_authorization_tuple()) - .collect(), - chain_id, - nonce, - max_priority_fee_per_gas: test_case - .max_priority_fee_per_gas - .unwrap() - .try_into() - .unwrap(), - max_fee_per_gas: test_case.max_fee_per_gas.unwrap().try_into().unwrap(), - gas_limit: test_case.gas, - ..Default::default() - }) - } else if test_case.max_fee_per_blob_gas.is_some() { - Transaction::EIP4844Transaction(EIP4844Transaction { - chain_id, - nonce, - max_priority_fee_per_gas: test_case - .max_priority_fee_per_gas - .unwrap() - .try_into() - .unwrap(), - max_fee_per_gas: test_case.max_fee_per_gas.unwrap().try_into().unwrap(), - gas: test_case.gas, - to: match to { - TxKind::Call(to) => to, - TxKind::Create => return Err(RunnerError::EIP7702ShouldNotBeCreateType), //TODO: See what to do with this. Maybe we want to get rid of the error and skip the test. - }, - value, - data, - access_list, - max_fee_per_blob_gas: test_case.max_fee_per_blob_gas.unwrap(), - blob_versioned_hashes: test_case.blob_versioned_hashes.clone(), - ..Default::default() - }) - } else if test_case.max_priority_fee_per_gas.is_some() && test_case.max_fee_per_gas.is_some() { - Transaction::EIP1559Transaction(EIP1559Transaction { - chain_id, - nonce, - max_priority_fee_per_gas: test_case - .max_priority_fee_per_gas - .unwrap() - .try_into() - .unwrap(), - max_fee_per_gas: test_case.max_fee_per_gas.unwrap().try_into().unwrap(), - gas_limit: test_case.gas, - to, - value, - data, - access_list, - ..Default::default() - }) - } else if !test_case.access_list.is_empty() { - // TODO: This will work, ideally Vec should be Option> so that we can tell if the field exists or not... - Transaction::EIP2930Transaction(EIP2930Transaction { - chain_id, - nonce, - gas_price: test_case.gas_price.unwrap(), - gas_limit: test_case.gas, - to, - value, - data, - access_list, - ..Default::default() - }) - } else { - Transaction::LegacyTransaction(LegacyTransaction { - nonce, - gas_price: test_case.gas_price.unwrap(), - gas: test_case.gas, - to, - value, - data, - ..Default::default() - }) - }; - - // Sign transaction using sender's private key. - let sk = SecretKey::from_slice(test_case.secret_key.as_bytes()).unwrap(); - let signer = Signer::Local(LocalSigner::new(sk)); - tx.sign_inplace(&signer) - .await - .expect("Signing shouldn't fail"); - Ok(tx) -} diff --git a/tooling/ef_tests/state_v2/src/modules/types.rs b/tooling/ef_tests/state_v2/src/modules/types.rs deleted file mode 100644 index 1fa0339e8aa..00000000000 --- a/tooling/ef_tests/state_v2/src/modules/types.rs +++ /dev/null @@ -1,562 +0,0 @@ -use crate::modules::{ - deserialize::{ - deserialize_access_lists, deserialize_authorization_lists, - deserialize_ef_post_value_indexes, deserialize_post, - deserialize_transaction_expected_exception, - }, - error::RunnerError, -}; -use std::str::FromStr; - -use ethrex_common::{ - Address, Bytes, H160, H256, U256, - constants::GAS_PER_BLOB, - types::{ - AuthorizationTuple, BASE_FEE_MAX_CHANGE_DENOMINATOR, Fork, Genesis, GenesisAccount, TxKind, - }, -}; -use ethrex_common::{ - serde_utils::{bytes, u64, u256}, - types::{BlobSchedule, ChainConfig}, -}; - -use serde::Deserialize; -use serde_json::Value; -use std::{ - collections::{BTreeMap, HashMap}, - path::PathBuf, -}; - -const DEFAULT_FORKS: [&str; 5] = ["Merge", "Shanghai", "Cancun", "Prague", "Amsterdam"]; - -/// `Tests` structure is the result of parsing a whole `.json` file from the EF tests. This file includes at -/// least one general test enviroment and different test cases inside each enviroment. -#[derive(Debug)] -pub struct Tests(pub Vec); - -/// Custom deserialize function for a `.json file`. -impl<'de> Deserialize<'de> for Tests { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - // A single .json file can contain more than one Test. - let mut ef_tests = Vec::new(); - // This will get a HashMap where the first String key is the name of the test in the file - // and the String key in the inner HashMap represents the name of a particular field inside - // the test. - let test_file: HashMap> = - HashMap::deserialize(deserializer)?; - - // Every test object that appears (identified with a String key, its name) will end up represented - // by a `Test`. - for test_name in test_file.keys() { - let test_data = test_file - .get(test_name) - .ok_or(serde::de::Error::missing_field("test data value"))?; - - // Obtain the value of the `transaction` field in the JSON. - let tx_field = test_data - .get("transaction") - .ok_or(serde::de::Error::missing_field("transaction"))? - .clone(); - // Parse the field value as a `RawTransaction`. - let raw_tx: RawTransaction = serde_json::from_value(tx_field).map_err(|err| { - serde::de::Error::custom(format!( - "Failed to deserialize `transaction` field in test {}. Serde error: {}", - test_name, err - )) - })?; - // Obtain the value of the `post` field in the JSON. - let post_field = test_data - .get("post") - .ok_or(serde::de::Error::missing_field("post"))? - .clone(); - // Parse the field value as a `RawPost`. - let post: RawPost = serde_json::from_value(post_field).map_err(|err| { - serde::de::Error::custom(format!( - "Failed to deserialize `post` field in test {}. Serde error: {}", - test_name, err - )) - })?; - - let mut test_cases: Vec = Vec::new(); - // For every pair included in this test create a `TestCase`. - // One fork can be used to execute more than one transaction, that will be given by the - // different combinations of data, value and gas limit. - for fork in post.forks.keys() { - // We make sure we parse only the forks Ethrex supports (post Merge). - if !DEFAULT_FORKS.contains(&(*fork).into()) { - continue; - } - let fork_test_cases = post.forks.get(fork).ok_or(serde::de::Error::custom( - "Failed to find fork in test post value", - ))?; - for case in fork_test_cases { - let test_case = Self::build_test_case(&raw_tx, fork, case) - .map_err(|e| serde::de::Error::custom(format!("{:?}", e)))?; - test_cases.push(test_case); - } - } - // After we have obtained all the possible combinations of into test cases, - // a `Test` is created, that includes the shared enviroment and all of the test cases that will be - // executed under the same pre-conditions. - let test = Self::build_test(test_name, test_data, test_cases) - .map_err(|err| serde::de::Error::custom(err.to_string()))?; - ef_tests.push(test); - } - Ok(Self(ef_tests)) - } -} - -impl Tests { - /// Returns a `Test` structure from the `.json` parsed data and the previously built - /// test cases (). - fn build_test( - test_name: &str, - test_data: &HashMap, - test_cases: Vec, - ) -> Result { - // Obtain the value of the `info` field in the JSON. - let info_field = test_data - .get("_info") - .ok_or(serde::de::Error::missing_field("_info"))?; - // Parse the field value as `Info`. - let test_info = serde_json::from_value(info_field.clone()).map_err(|err| { - serde::de::Error::custom(format!( - "Failed to deserialize `info` field in test {}. Serde error: {}", - test_name, err - )) - })?; - // Obtain the value of the `env` field in the JSON. - let env_field = test_data - .get("env") - .ok_or(serde::de::Error::missing_field("env"))?; - // Parse the field value as `Env`. - let test_env = serde_json::from_value(env_field.clone()).map_err(|err| { - serde::de::Error::custom(format!( - "Failed to deserialize `env` field in test {}. Serde error: {}", - test_name, err - )) - })?; - // Obtain the value of the `pre` field in the JSON. - let pre_field = test_data - .get("pre") - .ok_or(serde::de::Error::missing_field("pre"))?; - // Parse the field value as a `HashMap`. - let test_pre = serde_json::from_value(pre_field.clone()).map_err(|err| { - serde::de::Error::custom(format!( - "Failed to deserialize `pre` field in test {}. Serde error: {}", - test_name, err - )) - })?; - - let test = Test { - name: test_name.to_string(), - path: PathBuf::default(), // Test file path gets updated afterwards, cannot be known from here. - _info: test_info, - env: test_env, - pre: test_pre, - test_cases, - }; - Ok(test) - } - - /// Builds a `TestCase` struct from previously parsed `.json` data. - fn build_test_case( - raw_tx: &RawTransaction, - fork: &Fork, - raw_post: &RawPostValue, - ) -> Result { - let data_index: usize = (*raw_post - .indexes - .get("data") - .ok_or(RunnerError::FailedToGetIndexValue("value".to_string()))?) - .try_into() - .unwrap(); - let value_index: usize = (*raw_post - .indexes - .get("value") - .ok_or(RunnerError::FailedToGetIndexValue("value".to_string()))?) - .try_into() - .unwrap(); - let gas_index: usize = (*raw_post - .indexes - .get("gas") - .ok_or(RunnerError::FailedToGetIndexValue("value".to_string()))?) - .try_into() - .unwrap(); - let access_list_raw = raw_tx.access_lists.clone().unwrap_or_default(); - let mut access_list = Vec::new(); - if !access_list_raw.is_empty() { - access_list = access_list_raw[data_index].clone(); - } - let test_case = TestCase { - vector: (data_index, value_index, gas_index), - data: raw_tx.data[data_index].clone(), - value: raw_tx.value[value_index], - gas: raw_tx.gas_limit[gas_index], - tx_bytes: raw_post.txbytes.clone(), - gas_price: raw_tx.gas_price, - nonce: raw_tx.nonce, - secret_key: raw_tx.secret_key, - sender: raw_tx.sender, - max_fee_per_blob_gas: raw_tx.max_fee_per_blob_gas, - max_fee_per_gas: raw_tx.max_fee_per_gas, - max_priority_fee_per_gas: raw_tx.max_priority_fee_per_gas, - to: raw_tx.to.clone(), - fork: *fork, - authorization_list: raw_tx.authorization_list.clone(), - access_list, - blob_versioned_hashes: raw_tx.blob_versioned_hashes.clone().unwrap_or_default(), - post: Post { - hash: raw_post.hash, - logs: raw_post.logs, - state: raw_post.state.clone(), - expected_exceptions: raw_post.expect_exception.clone(), - }, - }; - Ok(test_case) - } -} - -/// This structure represents the general enviroment for a set of specific test cases. It includes all the -/// information that is shared among the test cases. -#[derive(Debug, Clone)] -pub struct Test { - pub name: String, // The name of the test object inside the .json file. - pub path: PathBuf, // The path of the .json file the Test can be found at. - pub _info: Info, // General information about the test. - pub env: Env, // The block enviroment before the test transaction happens. - pub pre: HashMap, // The accounts state previous to the test transaction. - pub test_cases: Vec, // A vector of specific cases to be tested under these conditions (transactions). -} - -fn get_chain_config_from_fork(fork: &Fork) -> ChainConfig { - let mut basic_chain_config = ChainConfig { - chain_id: 1, - homestead_block: Some(0), - dao_fork_block: Some(0), - dao_fork_support: true, - eip150_block: Some(0), - eip155_block: Some(0), - eip158_block: Some(0), - byzantium_block: Some(0), - constantinople_block: Some(0), - petersburg_block: Some(0), - istanbul_block: Some(0), - muir_glacier_block: Some(0), - berlin_block: Some(0), - london_block: Some(0), - arrow_glacier_block: Some(0), - gray_glacier_block: Some(0), - merge_netsplit_block: Some(0), - shanghai_time: None, // Time for forks will be set. - cancun_time: None, - prague_time: None, - verkle_time: None, - osaka_time: None, - terminal_total_difficulty: Some(0), - terminal_total_difficulty_passed: true, - blob_schedule: BlobSchedule::default(), - deposit_contract_address: H160::from_str("0x4242424242424242424242424242424242424242") - .unwrap(), // Doesn't matter - ..Default::default() - }; - - if *fork < Fork::Paris { - panic!("We don't support pre Merge forks, what are you doing here?"); - } - if *fork >= Fork::Shanghai { - basic_chain_config.shanghai_time = Some(0); - } - if *fork >= Fork::Cancun { - basic_chain_config.cancun_time = Some(0); - } - if *fork >= Fork::Prague { - basic_chain_config.prague_time = Some(0); - } - if *fork >= Fork::Osaka { - basic_chain_config.osaka_time = Some(0); - } - if *fork >= Fork::Amsterdam { - basic_chain_config.amsterdam_time = Some(0); - } - - basic_chain_config -} - -pub fn genesis_from_test_and_fork(test: &Test, fork: &Fork) -> Genesis { - let chain_config = get_chain_config_from_fork(fork); - - let genesis_excess_blob_gas = if let Some(excess_blob_gas) = test.env.current_excess_blob_gas { - let schedule = BlobSchedule::default(); - let target = if *fork == Fork::Cancun { - schedule.cancun.target - } else if *fork == Fork::Prague { - schedule.prague.target - } else { - 0 - }; - - Some(u64::try_from(excess_blob_gas).unwrap() + target as u64 * GAS_PER_BLOB as u64) - } else { - None - }; - - Genesis { - alloc: { - let mut alloc = BTreeMap::new(); - for (account, account_state) in &test.pre { - alloc.insert(*account, GenesisAccount::from(account_state)); - } - alloc - }, - coinbase: test.env.current_coinbase, - difficulty: U256::zero(), - timestamp: 0, - config: chain_config, - gas_limit: test.env.current_gas_limit, - base_fee_per_gas: (u64::try_from(test.env.current_base_fee.unwrap()).unwrap() - * BASE_FEE_MAX_CHANGE_DENOMINATOR as u64) - .checked_div(7), // This was VERY carefully calculated so that the header passes validations in calculate_base_fee_per_gas - excess_blob_gas: genesis_excess_blob_gas, - ..Default::default() - } -} - -/// General information about the test. Matches the `_info` field in the `.json` file. -#[derive(Debug, Deserialize, Clone)] -pub struct Info { - #[serde(default)] - pub comment: Option, - #[serde(rename = "filling-rpc-server", default)] - pub filling_rpc_server: Option, - #[serde(rename = "filling-tool-version", default)] - pub filling_tool_version: Option, - #[serde(rename = "generatedTestHash", default)] - pub generated_test_hash: Option, - #[serde(default)] - pub labels: Option>, - #[serde(default)] - pub lllcversion: Option, - #[serde(default)] - pub solidity: Option, - #[serde(default)] - pub source: Option, - #[serde(rename = "sourceHash", default)] - pub source_hash: Option, - // These fields are implemented in the new version of the test vectors (Prague). - #[serde(rename = "hash", default)] - pub hash: Option, - #[serde(rename = "filling-transition-tool", default)] - pub filling_transition_tool: Option, - #[serde(default)] - pub description: Option, - #[serde(default)] - pub url: Option, - #[serde(rename = "fixture_format", default)] - pub fixture_format: Option, - #[serde(rename = "reference-spec", default)] - pub reference_spec: Option, - #[serde(rename = "reference-spec-version", default)] - pub reference_spec_version: Option, -} - -/// Block enviroment previous to the execution of the transaction. Matches the `env` field in the -/// `.json` file. -#[derive(Debug, Deserialize, Clone, Copy)] -#[serde(rename_all = "camelCase")] -pub struct Env { - #[serde(default, deserialize_with = "u256::deser_hex_str_opt")] - pub current_base_fee: Option, - pub current_coinbase: Address, - #[serde(deserialize_with = "u256::deser_hex_str")] - pub current_difficulty: U256, - #[serde(default, deserialize_with = "u256::deser_hex_str_opt")] - pub current_excess_blob_gas: Option, - #[serde(with = "u64::hex_str")] - pub current_gas_limit: u64, - #[serde(deserialize_with = "u256::deser_hex_str")] - pub current_number: U256, - pub current_random: Option, - #[serde(deserialize_with = "u256::deser_hex_str")] - pub current_timestamp: U256, - #[serde(default, deserialize_with = "u256::deser_hex_str_opt")] - pub slot_number: Option, -} - -/// This structure represents a specific test case under general test conditions (`Test` struct). It is mainly -/// composed of a particular transaction combined with a particular fork. It includes the expected post state -/// after the transaction is executed. -#[derive(Deserialize, Debug, Clone)] -pub struct TestCase { - pub vector: (usize, usize, usize), - pub data: Bytes, - pub gas: u64, - pub value: U256, - pub tx_bytes: Bytes, - pub gas_price: Option, - pub max_fee_per_gas: Option, - pub max_priority_fee_per_gas: Option, - pub max_fee_per_blob_gas: Option, - pub nonce: u64, - pub secret_key: H256, - pub sender: Address, - pub to: TxKind, - pub fork: Fork, - pub post: Post, - pub blob_versioned_hashes: Vec, - pub access_list: Vec, - pub authorization_list: Option>, -} - -/// Indicates the expected post state that should be obtained after executing a test case. -#[derive(Debug, Deserialize, Clone)] -pub struct Post { - pub hash: H256, // Expected post root hash. - pub logs: H256, // Expected output logs. - pub state: Option>, // For new tests, the state field indicates the expected state of the involved accounts after executing the transaction. - pub expected_exceptions: Option>, // Expected exceptions. The output exception should match one of these. -} - -/// The state an involved account is expected to have after executing the test case transaction. -#[derive(Debug, Deserialize, Clone)] -pub struct AccountState { - #[serde(deserialize_with = "u256::deser_hex_str")] - pub balance: U256, - #[serde(with = "bytes")] - pub code: Bytes, - #[serde(with = "u64::hex_str")] - pub nonce: u64, - #[serde(with = "u256::hashmap")] - pub storage: HashMap, -} - -impl From<&AccountState> for GenesisAccount { - fn from(value: &AccountState) -> Self { - Self { - code: value.code.clone(), - storage: value.storage.iter().map(|(&k, &v)| (k, v)).collect(), - balance: value.balance, - nonce: value.nonce, - } - } -} - -#[derive(Debug, Deserialize, Clone)] -pub enum TransactionExpectedException { - InitcodeSizeExceeded, - NonceIsMax, - Type3TxBlobCountExceeded, - Type3TxZeroBlobs, - Type3TxContractCreation, - Type3TxInvalidBlobVersionedHash, - Type4TxContractCreation, - IntrinsicGasTooLow, - IntrinsicGasBelowFloorGasCost, - InsufficientAccountFunds, - SenderNotEoa, - PriorityGreaterThanMaxFeePerGas, - GasAllowanceExceeded, - InsufficientMaxFeePerGas, - RlpInvalidValue, - GasLimitPriceProductOverflow, - Type3TxPreFork, - InsufficientMaxFeePerBlobGas, - Other, -} - -#[derive(Debug, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct AccessListItem { - pub address: Address, - pub storage_keys: Vec, -} - -#[derive(Debug, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct AuthorizationListTuple { - #[serde(deserialize_with = "u256::deser_hex_str")] - pub chain_id: U256, - pub address: Address, - #[serde(with = "u64::hex_str")] - pub nonce: u64, - #[serde(deserialize_with = "u256::deser_hex_str")] - pub v: U256, - #[serde(deserialize_with = "u256::deser_hex_str")] - pub r: U256, - #[serde(deserialize_with = "u256::deser_hex_str")] - pub s: U256, - pub signer: Option
, -} -impl AuthorizationListTuple { - pub fn into_authorization_tuple(self) -> AuthorizationTuple { - AuthorizationTuple { - chain_id: self.chain_id, - address: self.address, - nonce: self.nonce, - y_parity: self.v, - r_signature: self.r, - s_signature: self.s, - } - } -} - -// ---- Raw structures ---- -// Exactly as they are defined in the .json test files, mainly used for parsing purposes or -// as intermediate structures. - -#[derive(Debug, Deserialize)] -pub struct RawPost { - #[serde(flatten)] - #[serde(deserialize_with = "deserialize_post")] - pub forks: HashMap>, -} - -#[derive(Debug, Deserialize, Clone)] -pub struct RawPostValue { - #[serde( - rename = "expectException", - default, - deserialize_with = "deserialize_transaction_expected_exception" - )] - pub expect_exception: Option>, - pub hash: H256, - #[serde(deserialize_with = "deserialize_ef_post_value_indexes")] - pub indexes: HashMap, - pub logs: H256, - // we add the default because some tests don't have this field - #[serde(default, with = "bytes")] - pub txbytes: Bytes, - pub state: Option>, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct RawTransaction { - #[serde(with = "bytes::vec")] - pub data: Vec, - #[serde(deserialize_with = "u64::hex_str::deser_vec")] - pub gas_limit: Vec, - #[serde(default, deserialize_with = "u256::deser_hex_str_opt")] - pub gas_price: Option, - #[serde(with = "u64::hex_str")] - pub nonce: u64, - pub secret_key: H256, - pub sender: Address, - pub to: TxKind, - #[serde(with = "u256::vec")] - pub value: Vec, - #[serde(default, deserialize_with = "u256::deser_hex_str_opt")] - pub max_fee_per_gas: Option, - #[serde(default, deserialize_with = "u256::deser_hex_str_opt")] - pub max_priority_fee_per_gas: Option, - #[serde(default, deserialize_with = "u256::deser_hex_str_opt")] - pub max_fee_per_blob_gas: Option, - pub blob_versioned_hashes: Option>, - #[serde(default, deserialize_with = "deserialize_access_lists")] - pub access_lists: Option>>, - #[serde(default, deserialize_with = "deserialize_authorization_lists")] - pub authorization_list: Option>, -} diff --git a/tooling/ef_tests/state_v2/src/modules/utils.rs b/tooling/ef_tests/state_v2/src/modules/utils.rs deleted file mode 100644 index 1b05c266d41..00000000000 --- a/tooling/ef_tests/state_v2/src/modules/utils.rs +++ /dev/null @@ -1,58 +0,0 @@ -use ethrex_blockchain::vm::StoreVmDatabase; -use ethrex_common::H256; -use ethrex_common::{ - U256, - types::{Fork, Genesis}, -}; -use ethrex_levm::db::gen_db::GeneralizedDatabase; -use ethrex_storage::{EngineType, Store}; -use ethrex_vm::DynVmDatabase; - -use std::sync::Arc; - -use crate::modules::{ - error::RunnerError, - types::{Env, Test, TestCase, genesis_from_test_and_fork}, -}; - -/// Calculates the price of the gas based on the fields the test case has. For transaction types -/// previous to EIP1559, the gas_price is explicit in the test. For later transaction types, it requires -/// to be calculated based on `current_base_fee`, `priority_fee` and `max_fee_per_gas` values. -pub fn effective_gas_price(test_env: &Env, test_case: &TestCase) -> Result { - match test_case.gas_price { - None => { - let current_base_fee = test_env.current_base_fee.unwrap(); - let priority_fee = test_case.max_priority_fee_per_gas.unwrap(); - let max_fee_per_gas = test_case.max_fee_per_gas.unwrap(); - - Ok(std::cmp::min( - max_fee_per_gas, - current_base_fee + priority_fee, - )) - } - Some(price) => Ok(price), - } -} - -/// Loads the pre state of the test (the initial state of specific accounts) into the Genesis. -pub async fn load_initial_state( - test: &Test, - fork: &Fork, -) -> (GeneralizedDatabase, H256, Store, Genesis) { - let genesis = genesis_from_test_and_fork(test, fork); - let mut storage = Store::new("./temp", EngineType::InMemory).expect("Failed to create Store"); - - storage.add_initial_state(genesis.clone()).await.unwrap(); - - let block_hash = genesis.get_block().hash(); - let store: DynVmDatabase = - Box::new(StoreVmDatabase::new(storage.clone(), genesis.get_block().header).unwrap()); - - // We return some values that will be needed to calculate the post execution checks (original storage, genesis and blockhash) - ( - GeneralizedDatabase::new(Arc::new(store)), - block_hash, - storage, - genesis, - ) -} diff --git a/tooling/hardhat/.gitignore b/tooling/hardhat/.gitignore deleted file mode 100644 index 1c1199b4eaf..00000000000 --- a/tooling/hardhat/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -node_modules -cache -artifacts -coverage -typechain diff --git a/tooling/hardhat/README.md b/tooling/hardhat/README.md deleted file mode 100644 index 8b6b44b8ea3..00000000000 --- a/tooling/hardhat/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Hardhat (ethrex) - -This folder wires Hardhat to the Solidity sources in `crates/l2/contracts/src`. - -## Setup (matches CI) - -From the repo root: - -```sh -npm install -``` - -Then install the Hardhat workspace dependencies and compile: - -```sh -cd tooling/hardhat -npm install -npm run compile -``` - -If you want to avoid Hardhat downloading solc, install solc 0.8.31 and set: - -```sh -export HARDHAT_USE_NATIVE_SOLC=true -``` - -## Run locally - -Run all Hardhat tests (currently the local upgradeability example): - -```sh -npm test -``` - -Run only the upgradeability example: - -```sh -npm run test:upgrade -``` - -This uses the dummy Box contracts and expects the incompatible upgrade to be rejected. - -CI-style upgrade comparison against a reference build-info directory: - -```sh -UPGRADE_REFERENCE_BUILD_INFO_DIR=/path/to/build-info-ref npm run validate:upgrades -``` - -## CI - -The workflow in `.github/workflows/pr_upgradeability.yaml` runs on PRs and compares -upgradeable contracts against `main`. It compiles Hardhat in the PR and in a -`main` worktree, copies the `build-info` from `main`, and runs -`npm run validate:upgrades` to check storage layout compatibility. - -## Environment overrides - -- `HARDHAT_USE_NATIVE_SOLC=true` (skip solc downloads if you have solc 0.8.31 installed) -- `UPGRADE_REFERENCE_BUILD_INFO_DIR=/path/to/build-info-ref` (used by `npm run validate:upgrades`) diff --git a/tooling/hardhat/hardhat.config.js b/tooling/hardhat/hardhat.config.js deleted file mode 100644 index 733610b8c10..00000000000 --- a/tooling/hardhat/hardhat.config.js +++ /dev/null @@ -1,68 +0,0 @@ -require("@nomicfoundation/hardhat-ethers"); -require("@nomicfoundation/hardhat-chai-matchers"); -require("@openzeppelin/hardhat-upgrades"); - -const fs = require("fs"); -const path = require("path"); -const { subtask } = require("hardhat/config"); -const { - TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS -} = require("hardhat/builtin-tasks/task-names"); - -const ROOT = path.join(__dirname, "../.."); -const UPGRADE_FIXTURES_DIR = path.join( - ROOT, - "fixtures/contracts/upgradeability" -); - -function collectSolFiles(dir, files = []) { - for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { - const fullPath = path.join(dir, entry.name); - if (entry.isDirectory()) { - collectSolFiles(fullPath, files); - } else if (entry.isFile() && entry.name.endsWith(".sol")) { - files.push(fullPath); - } - } - return files; -} - -subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction( - async (taskArgs, hre, runSuper) => { - const sources = await runSuper(taskArgs); - if (!fs.existsSync(UPGRADE_FIXTURES_DIR)) { - return sources; - } - - const extraSources = collectSolFiles(UPGRADE_FIXTURES_DIR); - if (extraSources.length === 0) { - return sources; - } - - return Array.from(new Set([...sources, ...extraSources])); - } -); - -module.exports = { - paths: { - root: ROOT, - sources: path.join(ROOT, "crates/l2/contracts/src"), - tests: path.join(__dirname, "test"), - cache: path.join(__dirname, "cache"), - artifacts: path.join(__dirname, "artifacts") - }, - solidity: { - version: "0.8.31", - settings: { - evmVersion: "cancun", - viaIR: true, - optimizer: { - enabled: true, - runs: 999999 - }, - metadata: { - bytecodeHash: "none" - } - } - } -}; diff --git a/tooling/hardhat/package-lock.json b/tooling/hardhat/package-lock.json deleted file mode 100644 index 3fa3da57197..00000000000 --- a/tooling/hardhat/package-lock.json +++ /dev/null @@ -1,6039 +0,0 @@ -{ - "name": "ethrex-hardhat", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "ethrex-hardhat", - "version": "0.0.0", - "devDependencies": { - "@nomicfoundation/hardhat-chai-matchers": "^2.0.6", - "@nomicfoundation/hardhat-ethers": "^3.0.4", - "@openzeppelin/contracts": "5.4.0", - "@openzeppelin/contracts-upgradeable": "5.4.0", - "@openzeppelin/hardhat-upgrades": "^3.2.1", - "chai": "^4.4.1", - "ethers": "^6.14.0", - "hardhat": "^2.24.0" - } - }, - "node_modules/@adraffy/ens-normalize": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", - "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-lambda": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.969.0.tgz", - "integrity": "sha512-m3V4bql3YsqmQBYDWz701zhFhIG2q2kawzYBKFGG9K7iaykRcZLL2oeDxwe8oaPxXEdksKCBuXsbBr+UKBTaxw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.969.0", - "@aws-sdk/credential-provider-node": "3.969.0", - "@aws-sdk/middleware-host-header": "3.969.0", - "@aws-sdk/middleware-logger": "3.969.0", - "@aws-sdk/middleware-recursion-detection": "3.969.0", - "@aws-sdk/middleware-user-agent": "3.969.0", - "@aws-sdk/region-config-resolver": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@aws-sdk/util-endpoints": "3.969.0", - "@aws-sdk/util-user-agent-browser": "3.969.0", - "@aws-sdk/util-user-agent-node": "3.969.0", - "@smithy/config-resolver": "^4.4.6", - "@smithy/core": "^3.20.5", - "@smithy/eventstream-serde-browser": "^4.2.8", - "@smithy/eventstream-serde-config-resolver": "^4.3.8", - "@smithy/eventstream-serde-node": "^4.2.8", - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/hash-node": "^4.2.8", - "@smithy/invalid-dependency": "^4.2.8", - "@smithy/middleware-content-length": "^4.2.8", - "@smithy/middleware-endpoint": "^4.4.6", - "@smithy/middleware-retry": "^4.4.22", - "@smithy/middleware-serde": "^4.2.9", - "@smithy/middleware-stack": "^4.2.8", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/node-http-handler": "^4.4.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.10.7", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.21", - "@smithy/util-defaults-mode-node": "^4.2.24", - "@smithy/util-endpoints": "^3.2.8", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-retry": "^4.2.8", - "@smithy/util-stream": "^4.5.10", - "@smithy/util-utf8": "^4.2.0", - "@smithy/util-waiter": "^4.2.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.969.0.tgz", - "integrity": "sha512-Qn0Uz6o15q2S+1E6OpwRKmaAMoT4LktEn+Oibk28qb2Mne+emaDawhZXahOJb/wFw5lN2FEH7XoiSNenNNUmCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.969.0", - "@aws-sdk/middleware-host-header": "3.969.0", - "@aws-sdk/middleware-logger": "3.969.0", - "@aws-sdk/middleware-recursion-detection": "3.969.0", - "@aws-sdk/middleware-user-agent": "3.969.0", - "@aws-sdk/region-config-resolver": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@aws-sdk/util-endpoints": "3.969.0", - "@aws-sdk/util-user-agent-browser": "3.969.0", - "@aws-sdk/util-user-agent-node": "3.969.0", - "@smithy/config-resolver": "^4.4.6", - "@smithy/core": "^3.20.5", - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/hash-node": "^4.2.8", - "@smithy/invalid-dependency": "^4.2.8", - "@smithy/middleware-content-length": "^4.2.8", - "@smithy/middleware-endpoint": "^4.4.6", - "@smithy/middleware-retry": "^4.4.22", - "@smithy/middleware-serde": "^4.2.9", - "@smithy/middleware-stack": "^4.2.8", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/node-http-handler": "^4.4.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.10.7", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.21", - "@smithy/util-defaults-mode-node": "^4.2.24", - "@smithy/util-endpoints": "^3.2.8", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-retry": "^4.2.8", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.969.0.tgz", - "integrity": "sha512-qqmQt4z5rEK1OYVkVkboWgy/58CC5QaQ7oy0tvLe3iri/mfZbgJkA+pkwQyRP827DfCBZ3W7Ki9iwSa+B2U7uQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.969.0", - "@aws-sdk/xml-builder": "3.969.0", - "@smithy/core": "^3.20.5", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/signature-v4": "^5.3.8", - "@smithy/smithy-client": "^4.10.7", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.969.0.tgz", - "integrity": "sha512-yS96heH5XDUqS3qQNcdObKKMOqZaivuNInMVRpRli48aXW8fX1M3fY67K/Onlqa3Wxu6WfDc3ZGF52SywdLvbg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@smithy/property-provider": "^4.2.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.969.0.tgz", - "integrity": "sha512-QCEFxBiUYFUW5VG6k8jKhT4luZndpC7uUY4u1olwt+OnJrl3N2yC7oS34isVBa3ioXZ4A0YagbXTa/3mXUhlAA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/node-http-handler": "^4.4.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.10.7", - "@smithy/types": "^4.12.0", - "@smithy/util-stream": "^4.5.10", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.969.0.tgz", - "integrity": "sha512-lsXyTDkUrZPxjr0XruZrqdcHY9zHcIuoY3TOCQEm23VTc8Np2BenTtjGAIexkL3ar69K4u3FVLQroLpmFxeXqA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.969.0", - "@aws-sdk/credential-provider-env": "3.969.0", - "@aws-sdk/credential-provider-http": "3.969.0", - "@aws-sdk/credential-provider-login": "3.969.0", - "@aws-sdk/credential-provider-process": "3.969.0", - "@aws-sdk/credential-provider-sso": "3.969.0", - "@aws-sdk/credential-provider-web-identity": "3.969.0", - "@aws-sdk/nested-clients": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@smithy/credential-provider-imds": "^4.2.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.969.0.tgz", - "integrity": "sha512-bIRFDf54qIUFFLTZNYt40d6EseNeK9w80dHEs7BVEAWoS23c9+MSqkdg/LJBBK9Kgy01vRmjiedfBZN+jGypLw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.969.0", - "@aws-sdk/nested-clients": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@smithy/property-provider": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.969.0.tgz", - "integrity": "sha512-lImMjcy/5SGDIBk7PFJCqFO4rFuapKCvo1z2PidD3Cbz2D7wsJnyqUNQIp5Ix0Xc3/uAYG9zXI9kgaMf1dspIQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.969.0", - "@aws-sdk/credential-provider-http": "3.969.0", - "@aws-sdk/credential-provider-ini": "3.969.0", - "@aws-sdk/credential-provider-process": "3.969.0", - "@aws-sdk/credential-provider-sso": "3.969.0", - "@aws-sdk/credential-provider-web-identity": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@smithy/credential-provider-imds": "^4.2.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.969.0.tgz", - "integrity": "sha512-2qQkM0rwd8Hl9nIHtUaqT8Z/djrulovqx/wBHsbRKaISwc2fiT3De1Lk1jx34Jzrz/dTHAMJJi+cML1N4Lk3kw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.969.0.tgz", - "integrity": "sha512-JHqXw9Ct3dtZB86/zGFJYWyodr961GyIrqTBhV0brrZFPvcinM9abDSK58jt6GNBM2lqfMCvXL6I4ahNsMdkrg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.969.0", - "@aws-sdk/core": "3.969.0", - "@aws-sdk/token-providers": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.969.0.tgz", - "integrity": "sha512-mKCZtqrs3ts3YmIjT4NFlYgT2Oe6syW0nX5m2l7iyrFrLXw26Zo3rx29DjGzycPdJHZZvsIy5y6yqChDuF65ng==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.969.0", - "@aws-sdk/nested-clients": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.969.0.tgz", - "integrity": "sha512-AWa4rVsAfBR4xqm7pybQ8sUNJYnjyP/bJjfAw34qPuh3M9XrfGbAHG0aiAfQGrBnmS28jlO6Kz69o+c6PRw1dw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.969.0", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.969.0.tgz", - "integrity": "sha512-xwrxfip7Y2iTtCMJ+iifN1E1XMOuhxIHY9DreMCvgdl4r7+48x2S1bCYPWH3eNY85/7CapBWdJ8cerpEl12sQQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.969.0", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.969.0.tgz", - "integrity": "sha512-2r3PuNquU3CcS1Am4vn/KHFwLi8QFjMdA/R+CRDXT4AFO/0qxevF/YStW3gAKntQIgWgQV8ZdEtKAoJvLI4UWg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.969.0", - "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.969.0.tgz", - "integrity": "sha512-Y6WkW8QQ2X9jG9HNBWyzp5KlJOCtLqX8VIvGLoGc2wXdZH7dgOy62uFhkfnHbgfiel6fkNYaycjGx/yyxi0JLQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@aws-sdk/util-endpoints": "3.969.0", - "@smithy/core": "^3.20.5", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.969.0.tgz", - "integrity": "sha512-MJrejgODxVYZjQjSpPLJkVuxnbrue1x1R8+as3anT5V/wk9Qc/Pf5B1IFjM3Ak6uOtzuRYNY4auOvcg4U8twDA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.969.0", - "@aws-sdk/middleware-host-header": "3.969.0", - "@aws-sdk/middleware-logger": "3.969.0", - "@aws-sdk/middleware-recursion-detection": "3.969.0", - "@aws-sdk/middleware-user-agent": "3.969.0", - "@aws-sdk/region-config-resolver": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@aws-sdk/util-endpoints": "3.969.0", - "@aws-sdk/util-user-agent-browser": "3.969.0", - "@aws-sdk/util-user-agent-node": "3.969.0", - "@smithy/config-resolver": "^4.4.6", - "@smithy/core": "^3.20.5", - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/hash-node": "^4.2.8", - "@smithy/invalid-dependency": "^4.2.8", - "@smithy/middleware-content-length": "^4.2.8", - "@smithy/middleware-endpoint": "^4.4.6", - "@smithy/middleware-retry": "^4.4.22", - "@smithy/middleware-serde": "^4.2.9", - "@smithy/middleware-stack": "^4.2.8", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/node-http-handler": "^4.4.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/smithy-client": "^4.10.7", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-body-length-node": "^4.2.1", - "@smithy/util-defaults-mode-browser": "^4.3.21", - "@smithy/util-defaults-mode-node": "^4.2.24", - "@smithy/util-endpoints": "^3.2.8", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-retry": "^4.2.8", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.969.0.tgz", - "integrity": "sha512-scj9OXqKpcjJ4jsFLtqYWz3IaNvNOQTFFvEY8XMJXTv+3qF5I7/x9SJtKzTRJEBF3spjzBUYPtGFbs9sj4fisQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.969.0", - "@smithy/config-resolver": "^4.4.6", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.969.0.tgz", - "integrity": "sha512-ucs6QczPkvGinbGmhMlPCQnagGJ+xsM6itsSWlJzxo9YsP6jR75cBU8pRdaM7nEbtCDnrUHf8W9g3D2Hd9mgVA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.969.0", - "@aws-sdk/nested-clients": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.969.0.tgz", - "integrity": "sha512-7IIzM5TdiXn+VtgPdVLjmE6uUBUtnga0f4RiSEI1WW10RPuNvZ9U+pL3SwDiRDAdoGrOF9tSLJOFZmfuwYuVYQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.969.0.tgz", - "integrity": "sha512-H2x2UwYiA1pHg40jE+OCSc668W9GXRShTiCWy1UPKtZKREbQ63Mgd7NAj+bEMsZUSCdHywqmSsLqKM9IcqQ3Bg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.969.0", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-endpoints": "^3.2.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.965.2", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.2.tgz", - "integrity": "sha512-qKgO7wAYsXzhwCHhdbaKFyxd83Fgs8/1Ka+jjSPrv2Ll7mB55Wbwlo0kkfMLh993/yEc8aoDIAc1Fz9h4Spi4Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.969.0.tgz", - "integrity": "sha512-bpJGjuKmFr0rA6UKUCmN8D19HQFMLXMx5hKBXqBlPFdalMhxJSjcxzX9DbQh0Fn6bJtxCguFmRGOBdQqNOt49g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.969.0", - "@smithy/types": "^4.12.0", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.969.0.tgz", - "integrity": "sha512-D11ZuXNXdUMv8XTthMx+LPzkYNQAeQ68FnCTGnFLgLpnR8hVTeZMBBKjQ77wYGzWDk/csHKdCy697gU1On5KjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.969.0", - "@aws-sdk/types": "3.969.0", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/util-utf8-browser": { - "version": "3.259.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", - "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.3.1" - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.969.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.969.0.tgz", - "integrity": "sha512-BSe4Lx/qdRQQdX8cSSI7Et20vqBspzAjBy8ZmXVoyLkol3y4sXBXzn+BiLtR+oh60ExQn6o2DU4QjdOZbXaKIQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws/lambda-invoke-store": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.3.tgz", - "integrity": "sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@bytecodealliance/preview2-shim": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@bytecodealliance/preview2-shim/-/preview2-shim-0.17.0.tgz", - "integrity": "sha512-JorcEwe4ud0x5BS/Ar2aQWOQoFzjq/7jcnxYXCvSMh0oRm0dQXzOA+hqLDBnOMks1LLBA7dmiLLsEBl09Yd6iQ==", - "dev": true, - "license": "(Apache-2.0 WITH LLVM-exception)" - }, - "node_modules/@ethereumjs/rlp": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-5.0.2.tgz", - "integrity": "sha512-DziebCdg4JpGlEqEdGgXmjqcFoJi+JGulUXwEjsZGAscAQ7MyD/7LE/GVCP29vEQxKc7AAwjT3A2ywHp2xfoCA==", - "dev": true, - "license": "MPL-2.0", - "bin": { - "rlp": "bin/rlp.cjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ethereumjs/util": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", - "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "@ethereumjs/rlp": "^5.0.2", - "ethereum-cryptography": "^2.2.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ethereumjs/util/node_modules/@noble/curves": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", - "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.4.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@ethereumjs/util/node_modules/@scure/bip32": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", - "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.4.0", - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@ethereumjs/util/node_modules/@scure/bip39": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", - "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.4.0", - "@scure/base": "~1.1.6" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", - "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "1.4.2", - "@noble/hashes": "1.4.0", - "@scure/bip32": "1.4.0", - "@scure/bip39": "1.3.0" - } - }, - "node_modules/@ethersproject/abi": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz", - "integrity": "sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/address": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/hash": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@ethersproject/abstract-provider": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz", - "integrity": "sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/networks": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "@ethersproject/web": "^5.8.0" - } - }, - "node_modules/@ethersproject/abstract-signer": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz", - "integrity": "sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-provider": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0" - } - }, - "node_modules/@ethersproject/address": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz", - "integrity": "sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/rlp": "^5.8.0" - } - }, - "node_modules/@ethersproject/base64": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz", - "integrity": "sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0" - } - }, - "node_modules/@ethersproject/bignumber": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz", - "integrity": "sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "bn.js": "^5.2.1" - } - }, - "node_modules/@ethersproject/bytes": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", - "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/constants": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz", - "integrity": "sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0" - } - }, - "node_modules/@ethersproject/hash": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz", - "integrity": "sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/base64": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@ethersproject/keccak256": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz", - "integrity": "sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "js-sha3": "0.8.0" - } - }, - "node_modules/@ethersproject/logger": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", - "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT" - }, - "node_modules/@ethersproject/networks": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz", - "integrity": "sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/properties": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", - "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/rlp": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz", - "integrity": "sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/signing-key": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", - "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "bn.js": "^5.2.1", - "elliptic": "6.6.1", - "hash.js": "1.1.7" - } - }, - "node_modules/@ethersproject/strings": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz", - "integrity": "sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/transactions": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz", - "integrity": "sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/address": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/rlp": "^5.8.0", - "@ethersproject/signing-key": "^5.8.0" - } - }, - "node_modules/@ethersproject/web": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz", - "integrity": "sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/base64": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/@noble/curves": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", - "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.3.2" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", - "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/secp256k1": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", - "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" - }, - "node_modules/@nomicfoundation/edr": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr/-/edr-0.12.0-next.22.tgz", - "integrity": "sha512-JigYWf2stjpDxSndBsxRoobQHK8kz4SAVaHtTIKQLIHbsBwymE8i120Ejne6Jk+Ndc5CsNINXB8/bK6vLPe9jA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nomicfoundation/edr-darwin-arm64": "0.12.0-next.22", - "@nomicfoundation/edr-darwin-x64": "0.12.0-next.22", - "@nomicfoundation/edr-linux-arm64-gnu": "0.12.0-next.22", - "@nomicfoundation/edr-linux-arm64-musl": "0.12.0-next.22", - "@nomicfoundation/edr-linux-x64-gnu": "0.12.0-next.22", - "@nomicfoundation/edr-linux-x64-musl": "0.12.0-next.22", - "@nomicfoundation/edr-win32-x64-msvc": "0.12.0-next.22" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@nomicfoundation/edr-darwin-arm64": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.12.0-next.22.tgz", - "integrity": "sha512-TpEBSKyMZJEPvYwBPYclC2b+qobKjn1YhVa7aJ1R7RMPy5dJ/PqsrUK5UuUFFybBqoIorru5NTcsyCMWP5T/Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@nomicfoundation/edr-darwin-x64": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.12.0-next.22.tgz", - "integrity": "sha512-aK/+m8xUkR4u+czTVGU06nSFVH43AY6XCBoR2YjO8SglAAjCSTWK3WAfVb6FcsriMmKv4PrvoyHLMbMP+fXcGA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@nomicfoundation/edr-linux-arm64-gnu": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.12.0-next.22.tgz", - "integrity": "sha512-W5vXMleG14hVzRYGPEwlHLJ6iiQE8Qh63Uj538nAz4YUI6wWSgUOZE7K2Gt1EdujZGnrt7kfDslgJ96n4nKQZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@nomicfoundation/edr-linux-arm64-musl": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.12.0-next.22.tgz", - "integrity": "sha512-VDp7EB3iY8MH/fFVcgEzLDGYmtS6j2honNc0RNUCFECKPrdsngGrTG8p+YFxyVjq2m5GEsdyKo4e+BKhaUNPdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@nomicfoundation/edr-linux-x64-gnu": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.12.0-next.22.tgz", - "integrity": "sha512-XL6oA3ymRSQYyvg6hF1KIax6V/9vlWr5gJ8GPHVVODk1a/YfuEEY1osN5Zmo6aztUkSGKwSuac/3Ax7rfDDiSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@nomicfoundation/edr-linux-x64-musl": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.12.0-next.22.tgz", - "integrity": "sha512-hmkRIXxWa9P0PwfXOAO6WUw11GyV5gpxcMunqWBTkwZ4QW/hi/CkXmlLo6VHd6ceCwpUNLhCGndBtrOPrNRi4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@nomicfoundation/edr-win32-x64-msvc": { - "version": "0.12.0-next.22", - "resolved": "https://registry.npmjs.org/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.12.0-next.22.tgz", - "integrity": "sha512-X7f+7KUMm00trsXAHCHJa+x1fc3QAbk2sBctyOgpET+GLrfCXbxqrccKi7op8f0zTweAVGg1Hsc8SjjC7kwFLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@nomicfoundation/hardhat-chai-matchers": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.1.0.tgz", - "integrity": "sha512-GPhBNafh1fCnVD9Y7BYvoLnblnvfcq3j8YDbO1gGe/1nOFWzGmV7gFu5DkwFXF+IpYsS+t96o9qc/mPu3V3Vfw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai-as-promised": "^7.1.3", - "chai-as-promised": "^7.1.1", - "deep-eql": "^4.0.1", - "ordinal": "^1.0.3" - }, - "peerDependencies": { - "@nomicfoundation/hardhat-ethers": "^3.1.0", - "chai": "^4.2.0", - "ethers": "^6.14.0", - "hardhat": "^2.26.0" - } - }, - "node_modules/@nomicfoundation/hardhat-ethers": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.1.3.tgz", - "integrity": "sha512-208JcDeVIl+7Wu3MhFUUtiA8TJ7r2Rn3Wr+lSx9PfsDTKkbsAsWPY6N6wQ4mtzDv0/pB9nIbJhkjoHe1EsgNsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "lodash.isequal": "^4.5.0" - }, - "peerDependencies": { - "ethers": "^6.14.0", - "hardhat": "^2.28.0" - } - }, - "node_modules/@nomicfoundation/slang": { - "version": "0.18.3", - "resolved": "https://registry.npmjs.org/@nomicfoundation/slang/-/slang-0.18.3.tgz", - "integrity": "sha512-YqAWgckqbHM0/CZxi9Nlf4hjk9wUNLC9ngWCWBiqMxPIZmzsVKYuChdlrfeBPQyvQQBoOhbx+7C1005kLVQDZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bytecodealliance/preview2-shim": "0.17.0" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz", - "integrity": "sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - }, - "optionalDependencies": { - "@nomicfoundation/solidity-analyzer-darwin-arm64": "0.1.2", - "@nomicfoundation/solidity-analyzer-darwin-x64": "0.1.2", - "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": "0.1.2", - "@nomicfoundation/solidity-analyzer-linux-arm64-musl": "0.1.2", - "@nomicfoundation/solidity-analyzer-linux-x64-gnu": "0.1.2", - "@nomicfoundation/solidity-analyzer-linux-x64-musl": "0.1.2", - "@nomicfoundation/solidity-analyzer-win32-x64-msvc": "0.1.2" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz", - "integrity": "sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz", - "integrity": "sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz", - "integrity": "sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz", - "integrity": "sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz", - "integrity": "sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz", - "integrity": "sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz", - "integrity": "sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/@openzeppelin/contracts": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.4.0.tgz", - "integrity": "sha512-eCYgWnLg6WO+X52I16TZt8uEjbtdkgLC0SUX/xnAksjjrQI4Xfn4iBRoI5j55dmlOhDv1Y7BoR3cU7e3WWhC6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@openzeppelin/contracts-upgradeable": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-5.4.0.tgz", - "integrity": "sha512-STJKyDzUcYuB35Zub1JpWW58JxvrFFVgQ+Ykdr8A9PGXgtq/obF5uoh07k2XmFyPxfnZdPdBdhkJ/n2YxJ87HQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@openzeppelin/contracts": "5.4.0" - } - }, - "node_modules/@openzeppelin/defender-sdk-base-client": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-base-client/-/defender-sdk-base-client-2.7.0.tgz", - "integrity": "sha512-J5IpvbFfdIJM4IadBcXfhCXVdX2yEpaZtRR1ecq87d8CdkmmEpniYfef/yVlG98yekvu125LaIRg0yXQOt9Bdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@aws-sdk/client-lambda": "^3.563.0", - "amazon-cognito-identity-js": "^6.3.6", - "async-retry": "^1.3.3" - } - }, - "node_modules/@openzeppelin/defender-sdk-deploy-client": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-deploy-client/-/defender-sdk-deploy-client-2.7.0.tgz", - "integrity": "sha512-YOHZmnHmM1y6uSqXWGfk2/5/ae4zZJE6xG92yFEAIOy8vqh1dxznWMsoCcAXRXTCWc8RdCDpFdMfEy4SBTyYtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@openzeppelin/defender-sdk-base-client": "^2.7.0", - "axios": "^1.7.4", - "lodash": "^4.17.21" - } - }, - "node_modules/@openzeppelin/defender-sdk-network-client": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-network-client/-/defender-sdk-network-client-2.7.0.tgz", - "integrity": "sha512-4CYWPa9+kSjojE5KS7kRmP161qsBATdp97TCrzyDdGoVahj0GyqgafRL9AAjm0eHZOM1c7EIYEpbvYRtFi8vyA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@openzeppelin/defender-sdk-base-client": "^2.7.0", - "axios": "^1.7.4", - "lodash": "^4.17.21" - } - }, - "node_modules/@openzeppelin/hardhat-upgrades": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-3.9.1.tgz", - "integrity": "sha512-pSDjlOnIpP+PqaJVe144dK6VVKZw2v6YQusyt0OOLiCsl+WUzfo4D0kylax7zjrOxqy41EK2ipQeIF4T+cCn2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@openzeppelin/defender-sdk-base-client": "^2.1.0", - "@openzeppelin/defender-sdk-deploy-client": "^2.1.0", - "@openzeppelin/defender-sdk-network-client": "^2.1.0", - "@openzeppelin/upgrades-core": "^1.41.0", - "chalk": "^4.1.0", - "debug": "^4.1.1", - "ethereumjs-util": "^7.1.5", - "proper-lockfile": "^4.1.1", - "undici": "^6.11.1" - }, - "bin": { - "migrate-oz-cli-project": "dist/scripts/migrate-oz-cli-project.js" - }, - "peerDependencies": { - "@nomicfoundation/hardhat-ethers": "^3.0.6", - "@nomicfoundation/hardhat-verify": "^2.0.14", - "ethers": "^6.6.0", - "hardhat": "^2.24.1" - }, - "peerDependenciesMeta": { - "@nomicfoundation/hardhat-verify": { - "optional": true - } - } - }, - "node_modules/@openzeppelin/hardhat-upgrades/node_modules/undici": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", - "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.17" - } - }, - "node_modules/@openzeppelin/upgrades-core": { - "version": "1.44.2", - "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.44.2.tgz", - "integrity": "sha512-m6iorjyhPK9ow5/trNs7qsBC/SOzJCO51pvvAF2W9nOiZ1t0RtCd+rlRmRmlWTv4M33V0wzIUeamJ2BPbzgUXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nomicfoundation/slang": "^0.18.3", - "bignumber.js": "^9.1.2", - "cbor": "^10.0.0", - "chalk": "^4.1.0", - "compare-versions": "^6.0.0", - "debug": "^4.1.1", - "ethereumjs-util": "^7.0.3", - "minimatch": "^9.0.5", - "minimist": "^1.2.7", - "proper-lockfile": "^4.1.1", - "solidity-ast": "^0.4.60" - }, - "bin": { - "openzeppelin-upgrades-core": "dist/cli/cli.js" - } - }, - "node_modules/@openzeppelin/upgrades-core/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@scure/base": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", - "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@scure/bip32": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", - "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.2.0", - "@noble/secp256k1": "~1.7.0", - "@scure/base": "~1.1.0" - } - }, - "node_modules/@scure/bip32/node_modules/@noble/hashes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", - "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" - }, - "node_modules/@scure/bip39": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", - "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT", - "dependencies": { - "@noble/hashes": "~1.2.0", - "@scure/base": "~1.1.0" - } - }, - "node_modules/@scure/bip39/node_modules/@noble/hashes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", - "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" - }, - "node_modules/@sentry/core": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", - "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/hub": "5.30.0", - "@sentry/minimal": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/core/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@sentry/hub": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", - "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/hub/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@sentry/minimal": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", - "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/hub": "5.30.0", - "@sentry/types": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/minimal/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@sentry/node": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", - "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/core": "5.30.0", - "@sentry/hub": "5.30.0", - "@sentry/tracing": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "cookie": "^0.4.1", - "https-proxy-agent": "^5.0.0", - "lru_map": "^0.3.3", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/node/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@sentry/tracing": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", - "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sentry/hub": "5.30.0", - "@sentry/minimal": "5.30.0", - "@sentry/types": "5.30.0", - "@sentry/utils": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/tracing/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@sentry/types": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", - "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/utils": { - "version": "5.30.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", - "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sentry/types": "5.30.0", - "tslib": "^1.9.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@sentry/utils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@smithy/abort-controller": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.8.tgz", - "integrity": "sha512-peuVfkYHAmS5ybKxWcfraK7WBBP0J+rkfUcbHJJKQ4ir3UAUNQI+Y4Vt/PqSzGqgloJ5O1dk7+WzNL8wcCSXbw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.6.tgz", - "integrity": "sha512-qJpzYC64kaj3S0fueiu3kXm8xPrR3PcXDPEgnaNMRn0EjNSZFoFjvbUp0YUDsRhN1CB90EnHJtbxWKevnH99UQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-endpoints": "^3.2.8", - "@smithy/util-middleware": "^4.2.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "3.20.6", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.20.6.tgz", - "integrity": "sha512-BpAffW1mIyRZongoKBbh3RgHG+JDHJek/8hjA/9LnPunM+ejorO6axkxCgwxCe4K//g/JdPeR9vROHDYr/hfnQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^4.2.9", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-body-length-browser": "^4.2.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-stream": "^4.5.10", - "@smithy/util-utf8": "^4.2.0", - "@smithy/uuid": "^1.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.8.tgz", - "integrity": "sha512-FNT0xHS1c/CPN8upqbMFP83+ul5YgdisfCfkZ86Jh2NSmnqw/AJ6x5pEogVCTVvSm7j9MopRU89bmDelxuDMYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.8.tgz", - "integrity": "sha512-jS/O5Q14UsufqoGhov7dHLOPCzkYJl9QDzusI2Psh4wyYx/izhzvX9P4D69aTxcdfVhEPhjK+wYyn/PzLjKbbw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.12.0", - "@smithy/util-hex-encoding": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.8.tgz", - "integrity": "sha512-MTfQT/CRQz5g24ayXdjg53V0mhucZth4PESoA5IhvaWVDTOQLfo8qI9vzqHcPsdd2v6sqfTYqF5L/l+pea5Uyw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.2.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.8.tgz", - "integrity": "sha512-ah12+luBiDGzBruhu3efNy1IlbwSEdNiw8fOZksoKoWW1ZHvO/04MQsdnws/9Aj+5b0YXSSN2JXKy/ClIsW8MQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.8.tgz", - "integrity": "sha512-cYpCpp29z6EJHa5T9WL0KAlq3SOKUQkcgSoeRfRVwjGgSFl7Uh32eYGt7IDYCX20skiEdRffyDpvF2efEZPC0A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.2.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.8.tgz", - "integrity": "sha512-iJ6YNJd0bntJYnX6s52NC4WFYcZeKrPUr1Kmmr5AwZcwCSzVpS7oavAmxMR7pMq7V+D1G4s9F5NJK0xwOsKAlQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-codec": "^4.2.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.9.tgz", - "integrity": "sha512-I4UhmcTYXBrct03rwzQX1Y/iqQlzVQaPxWjCjula++5EmWq9YGBrx6bbGqluGc1f0XEfhSkiY4jhLgbsJUMKRA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.8", - "@smithy/querystring-builder": "^4.2.8", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.8.tgz", - "integrity": "sha512-7ZIlPbmaDGxVoxErDZnuFG18WekhbA/g2/i97wGj+wUBeS6pcUeAym8u4BXh/75RXWhgIJhyC11hBzig6MljwA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.8.tgz", - "integrity": "sha512-N9iozRybwAQ2dn9Fot9kI6/w9vos2oTXLhtK7ovGqwZjlOcxu6XhPlpLpC+INsxktqHinn5gS2DXDjDF2kG5sQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.0.tgz", - "integrity": "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.8.tgz", - "integrity": "sha512-RO0jeoaYAB1qBRhfVyq0pMgBoUK34YEJxVxyjOWYZiOKOq2yMZ4MnVXMZCUDenpozHue207+9P5ilTV1zeda0A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.4.7", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.7.tgz", - "integrity": "sha512-SCmhUG1UwtnEhF5Sxd8qk7bJwkj1BpFzFlHkXqKCEmDPLrRjJyTGM0EhqT7XBtDaDJjCfjRJQodgZcKDR843qg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.20.6", - "@smithy/middleware-serde": "^4.2.9", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "@smithy/url-parser": "^4.2.8", - "@smithy/util-middleware": "^4.2.8", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "4.4.23", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.23.tgz", - "integrity": "sha512-lLEmkQj7I7oKfvZ1wsnToGJouLOtfkMXDKRA1Hi6F+mMp5O1N8GcVWmVeNgTtgZtd0OTXDTI2vpVQmeutydGew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/service-error-classification": "^4.2.8", - "@smithy/smithy-client": "^4.10.8", - "@smithy/types": "^4.12.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-retry": "^4.2.8", - "@smithy/uuid": "^1.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "4.2.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.9.tgz", - "integrity": "sha512-eMNiej0u/snzDvlqRGSN3Vl0ESn3838+nKyVfF2FKNXFbi4SERYT6PR392D39iczngbqqGG0Jl1DlCnp7tBbXQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.8.tgz", - "integrity": "sha512-w6LCfOviTYQjBctOKSwy6A8FIkQy7ICvglrZFl6Bw4FmcQ1Z420fUtIhxaUZZshRe0VCq4kvDiPiXrPZAe8oRA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "4.3.8", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.8.tgz", - "integrity": "sha512-aFP1ai4lrbVlWjfpAfRSL8KFcnJQYfTl5QxLJXY32vghJrDuFyPZ6LtUL+JEGYiFRG1PfPLHLoxj107ulncLIg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.2.8", - "@smithy/shared-ini-file-loader": "^4.4.3", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.4.8", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.8.tgz", - "integrity": "sha512-q9u+MSbJVIJ1QmJ4+1u+cERXkrhuILCBDsJUBAW1MPE6sFonbCNaegFuwW9ll8kh5UdyY3jOkoOGlc7BesoLpg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/querystring-builder": "^4.2.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.8.tgz", - "integrity": "sha512-EtCTbyIveCKeOXDSWSdze3k612yCPq1YbXsbqX3UHhkOSW8zKsM9NOJG5gTIya0vbY2DIaieG8pKo1rITHYL0w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.8.tgz", - "integrity": "sha512-QNINVDhxpZ5QnP3aviNHQFlRogQZDfYlCkQT+7tJnErPQbDhysondEjhikuANxgMsZrkGeiAxXy4jguEGsDrWQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.8.tgz", - "integrity": "sha512-Xr83r31+DrE8CP3MqPgMJl+pQlLLmOfiEUnoyAlGzzJIrEsbKsPy1hqH0qySaQm4oWrCBlUqRt+idEgunKB+iw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "@smithy/util-uri-escape": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.8.tgz", - "integrity": "sha512-vUurovluVy50CUlazOiXkPq40KGvGWSdmusa3130MwrR1UNnNgKAlj58wlOe61XSHRpUfIIh6cE0zZ8mzKaDPA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.8.tgz", - "integrity": "sha512-mZ5xddodpJhEt3RkCjbmUQuXUOaPNTkbMGR0bcS8FE0bJDLMZlhmpgrvPNCYglVw5rsYTpSnv19womw9WWXKQQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.3.tgz", - "integrity": "sha512-DfQjxXQnzC5UbCUPeC3Ie8u+rIWZTvuDPAGU/BxzrOGhRvgUanaP68kDZA+jaT3ZI+djOf+4dERGlm9mWfFDrg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "5.3.8", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.8.tgz", - "integrity": "sha512-6A4vdGj7qKNRF16UIcO8HhHjKW27thsxYci+5r/uVRkdcBEkOEiY8OMPuydLX4QHSrJqGHPJzPRwwVTqbLZJhg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^4.2.8", - "@smithy/util-uri-escape": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "4.10.8", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.8.tgz", - "integrity": "sha512-wcr3UEL26k7lLoyf9eVDZoD1nNY3Fa1gbNuOXvfxvVWLGkOVW+RYZgUUp/bXHryJfycIOQnBq9o1JAE00ax8HQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.20.6", - "@smithy/middleware-endpoint": "^4.4.7", - "@smithy/middleware-stack": "^4.2.8", - "@smithy/protocol-http": "^5.3.8", - "@smithy/types": "^4.12.0", - "@smithy/util-stream": "^4.5.10", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.12.0.tgz", - "integrity": "sha512-9YcuJVTOBDjg9LWo23Qp0lTQ3D7fQsQtwle0jVfpbUHy9qBwCEgKuVH4FqFB3VYu0nwdHKiEMA+oXz7oV8X1kw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.8.tgz", - "integrity": "sha512-NQho9U68TGMEU639YkXnVMV3GEFFULmmaWdlu1E9qzyIePOHsoSnagTGSDv1Zi8DCNN6btxOSdgmy5E/hsZwhA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^4.2.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.0.tgz", - "integrity": "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.0.tgz", - "integrity": "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.1.tgz", - "integrity": "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.0.tgz", - "integrity": "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.0.tgz", - "integrity": "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.22", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.22.tgz", - "integrity": "sha512-O2WXr6ZRqPnbyoepb7pKcLt1QL6uRfFzGYJ9sGb5hMJQi7v/4RjRmCQa9mNjA0YiXqsc5lBmLXqJPhjM1Vjv5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.2.8", - "@smithy/smithy-client": "^4.10.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.25", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.25.tgz", - "integrity": "sha512-7uMhppVNRbgNIpyUBVRfjGHxygP85wpXalRvn9DvUlCx4qgy1AB/uxOPSiDx/jFyrwD3/BypQhx1JK7f3yxrAw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^4.4.6", - "@smithy/credential-provider-imds": "^4.2.8", - "@smithy/node-config-provider": "^4.3.8", - "@smithy/property-provider": "^4.2.8", - "@smithy/smithy-client": "^4.10.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "3.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.8.tgz", - "integrity": "sha512-8JaVTn3pBDkhZgHQ8R0epwWt+BqPSLCjdjXXusK1onwJlRuN69fbvSK66aIKKO7SwVFM6x2J2ox5X8pOaWcUEw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz", - "integrity": "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.8.tgz", - "integrity": "sha512-PMqfeJxLcNPMDgvPbbLl/2Vpin+luxqTGPpW3NAQVLbRrFRzTa4rNAASYeIGjRV9Ytuhzny39SpyU04EQreF+A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.8.tgz", - "integrity": "sha512-CfJqwvoRY0kTGe5AkQokpURNCT1u/MkRzMTASWMPPo2hNSnKtF1D45dQl3DE2LKLr4m+PW9mCeBMJr5mCAVThg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^4.2.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "4.5.10", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.10.tgz", - "integrity": "sha512-jbqemy51UFSZSp2y0ZmRfckmrzuKww95zT9BYMmuJ8v3altGcqjwoV1tzpOwuHaKrwQrCjIzOib499ymr2f98g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^5.3.9", - "@smithy/node-http-handler": "^4.4.8", - "@smithy/types": "^4.12.0", - "@smithy/util-base64": "^4.3.0", - "@smithy/util-buffer-from": "^4.2.0", - "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-utf8": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz", - "integrity": "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.0.tgz", - "integrity": "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.8.tgz", - "integrity": "sha512-n+lahlMWk+aejGuax7DPWtqav8HYnWxQwR+LCG2BgCUmaGcTe9qZCFsmw8TMg9iG75HOwhrJCX9TCJRLH+Yzqg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.2.8", - "@smithy/types": "^4.12.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/uuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.0.tgz", - "integrity": "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@types/bn.js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", - "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } - }, - "node_modules/@types/chai-as-promised": { - "version": "7.1.8", - "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", - "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "*" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.7.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", - "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.19.2" - } - }, - "node_modules/@types/pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/secp256k1": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.7.tgz", - "integrity": "sha512-Rcvjl6vARGAKRO6jHeKMatGrvOMGrR/AR11N1x2LqintPCyDZ7NBhrh238Z2VZc7aM7KIwnFpFQ7fnfK4H/9Qw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/adm-zip": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", - "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.3.0" - } - }, - "node_modules/aes-js": { - "version": "4.0.0-beta.5", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", - "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/amazon-cognito-identity-js": { - "version": "6.3.16", - "resolved": "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.16.tgz", - "integrity": "sha512-HPGSBGD6Q36t99puWh0LnptxO/4icnk2kqIQ9cTJ2tFQo5NMUnWQIgtrTAk8nm+caqUbjDzXzG56GBjI2tS6jQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "1.2.2", - "buffer": "4.9.2", - "fast-base64-decode": "^1.0.0", - "isomorphic-unfetch": "^3.0.0", - "js-cookie": "^2.2.1" - } - }, - "node_modules/amazon-cognito-identity-js/node_modules/@aws-crypto/sha256-js": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz", - "integrity": "sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^1.2.2", - "@aws-sdk/types": "^3.1.0", - "tslib": "^1.11.1" - } - }, - "node_modules/amazon-cognito-identity-js/node_modules/@aws-crypto/util": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-1.2.2.tgz", - "integrity": "sha512-H8PjG5WJ4wz0UXAFXeJjWCW1vkvIJ3qUUD+rGRwJ2/hj+xT58Qle2MTql/2MGzkU+1JLAFuR6aJpLAjHwhmwwg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.1.0", - "@aws-sdk/util-utf8-browser": "^3.0.0", - "tslib": "^1.11.1" - } - }, - "node_modules/amazon-cognito-identity-js/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true, - "license": "0BSD" - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "retry": "0.13.1" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axios": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/base-x": { - "version": "3.0.11", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz", - "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/blakejs": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", - "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/bn.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", - "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/bowser": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.13.1.tgz", - "integrity": "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==", - "dev": true, - "license": "MIT" - }, - "node_modules/boxen": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", - "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.0", - "camelcase": "^6.2.0", - "chalk": "^4.1.0", - "cli-boxes": "^2.2.1", - "string-width": "^4.2.2", - "type-fest": "^0.20.2", - "widest-line": "^3.1.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boxen/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "dev": true, - "license": "MIT" - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true, - "license": "ISC" - }, - "node_modules/browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "base-x": "^3.0.2" - } - }, - "node_modules/bs58check": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", - "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs58": "^4.0.0", - "create-hash": "^1.1.0", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cbor": { - "version": "10.0.11", - "resolved": "https://registry.npmjs.org/cbor/-/cbor-10.0.11.tgz", - "integrity": "sha512-vIwORDd/WyB8Nc23o2zNN5RrtFGlR6Fca61TtjkUXueI3Jf2DOZDl1zsshvBntZ3wZHBM9ztjnkXSmzQDaq3WA==", - "dev": true, - "license": "MIT", - "dependencies": { - "nofilter": "^3.0.2" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chai-as-promised": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", - "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", - "dev": true, - "license": "WTFPL", - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 6" - } - }, - "node_modules/chai/node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cipher-base": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz", - "integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.4", - "safe-buffer": "^5.2.1", - "to-buffer": "^1.2.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-boxes": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", - "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/command-exists": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", - "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", - "dev": true, - "license": "MIT" - }, - "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/compare-versions": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", - "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "node_modules/create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/elliptic": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", - "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ethereum-cryptography": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", - "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.2.0", - "@noble/secp256k1": "1.7.1", - "@scure/bip32": "1.1.5", - "@scure/bip39": "1.1.1" - } - }, - "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", - "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "license": "MIT" - }, - "node_modules/ethereumjs-util": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", - "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "@types/bn.js": "^5.1.0", - "bn.js": "^5.1.2", - "create-hash": "^1.1.2", - "ethereum-cryptography": "^0.1.3", - "rlp": "^2.2.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/ethereumjs-util/node_modules/ethereum-cryptography": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", - "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/pbkdf2": "^3.0.0", - "@types/secp256k1": "^4.0.1", - "blakejs": "^1.1.0", - "browserify-aes": "^1.2.0", - "bs58check": "^2.1.2", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "hash.js": "^1.1.7", - "keccak": "^3.0.0", - "pbkdf2": "^3.0.17", - "randombytes": "^2.1.0", - "safe-buffer": "^5.1.2", - "scrypt-js": "^3.0.0", - "secp256k1": "^4.0.1", - "setimmediate": "^1.0.5" - } - }, - "node_modules/ethers": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz", - "integrity": "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/ethers-io/" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@adraffy/ens-normalize": "1.10.1", - "@noble/curves": "1.2.0", - "@noble/hashes": "1.3.2", - "@types/node": "22.7.5", - "aes-js": "4.0.0-beta.5", - "tslib": "2.7.0", - "ws": "8.17.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "node_modules/fast-base64-decode": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz", - "integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^2.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fp-ts": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", - "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==", - "dev": true, - "license": "MIT" - }, - "node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/hardhat": { - "version": "2.28.3", - "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.28.3.tgz", - "integrity": "sha512-f1WxpCJCXzxDc12MgIIxxkvB2QK40g/atsW4Az5WQFhUXpZx4VFoSfvwYBIRsRbq6xIrgxef+tXuWda5wTLlgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ethereumjs/util": "^9.1.0", - "@ethersproject/abi": "^5.1.2", - "@nomicfoundation/edr": "0.12.0-next.22", - "@nomicfoundation/solidity-analyzer": "^0.1.0", - "@sentry/node": "^5.18.1", - "adm-zip": "^0.4.16", - "aggregate-error": "^3.0.0", - "ansi-escapes": "^4.3.0", - "boxen": "^5.1.2", - "chokidar": "^4.0.0", - "ci-info": "^2.0.0", - "debug": "^4.1.1", - "enquirer": "^2.3.0", - "env-paths": "^2.2.0", - "ethereum-cryptography": "^1.0.3", - "find-up": "^5.0.0", - "fp-ts": "1.19.3", - "fs-extra": "^7.0.1", - "immutable": "^4.0.0-rc.12", - "io-ts": "1.10.4", - "json-stream-stringify": "^3.1.4", - "keccak": "^3.0.2", - "lodash": "^4.17.11", - "micro-eth-signer": "^0.14.0", - "mnemonist": "^0.38.0", - "mocha": "^10.0.0", - "p-map": "^4.0.0", - "picocolors": "^1.1.0", - "raw-body": "^2.4.1", - "resolve": "1.17.0", - "semver": "^6.3.0", - "solc": "0.8.26", - "source-map-support": "^0.5.13", - "stacktrace-parser": "^0.1.10", - "tinyglobby": "^0.2.6", - "tsort": "0.0.1", - "undici": "^5.14.0", - "uuid": "^8.3.2", - "ws": "^7.4.6" - }, - "bin": { - "hardhat": "internal/cli/bootstrap.js" - }, - "peerDependencies": { - "ts-node": "*", - "typescript": "*" - }, - "peerDependenciesMeta": { - "ts-node": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/hardhat/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hash-base": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz", - "integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.4", - "readable-stream": "^2.3.8", - "safe-buffer": "^5.2.1", - "to-buffer": "^1.2.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/hash-base/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hash-base/node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/hash-base/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/hash-base/node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/immutable": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz", - "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==", - "dev": true, - "license": "MIT" - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/io-ts": { - "version": "1.10.4", - "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", - "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fp-ts": "^1.0.0" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/isomorphic-unfetch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", - "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.1", - "unfetch": "^4.2.0" - } - }, - "node_modules/js-cookie": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", - "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-stream-stringify": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz", - "integrity": "sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=7.10.1" - } - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "license": "MIT", - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/keccak": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", - "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "dev": true, - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/lru_map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", - "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "node_modules/memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", - "dev": true, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/micro-eth-signer": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/micro-eth-signer/-/micro-eth-signer-0.14.0.tgz", - "integrity": "sha512-5PLLzHiVYPWClEvZIXXFu5yutzpadb73rnQCpUqIHu3No3coFuWQNfE5tkBQJ7djuLYl6aRLaS0MgWJYGoqiBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/curves": "~1.8.1", - "@noble/hashes": "~1.7.1", - "micro-packed": "~0.7.2" - } - }, - "node_modules/micro-eth-signer/node_modules/@noble/curves": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz", - "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "1.7.2" - }, - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/micro-eth-signer/node_modules/@noble/hashes": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz", - "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/micro-packed": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.7.3.tgz", - "integrity": "sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@scure/base": "~1.2.5" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/micro-packed/node_modules/@scure/base": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", - "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true, - "license": "ISC" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "dev": true, - "license": "MIT" - }, - "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mnemonist": { - "version": "0.38.5", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", - "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "obliterator": "^2.0.0" - } - }, - "node_modules/mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-colors": "^4.1.3", - "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^8.1.0", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/mocha/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/mocha/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mocha/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "dev": true, - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/nofilter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", - "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.19" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/obliterator": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", - "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", - "dev": true, - "license": "MIT" - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/ordinal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz", - "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/pbkdf2": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.5.tgz", - "integrity": "sha512-Q3CG/cYvCO1ye4QKkuH7EXxs3VC/rI1/trd+qX2+PolbaKG0H+bgcZzrTt96mMyRtejk+JMCiLUn3y29W8qmFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "ripemd160": "^2.0.3", - "safe-buffer": "^5.2.1", - "sha.js": "^2.4.12", - "to-buffer": "^1.2.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/proper-lockfile": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", - "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, - "node_modules/proper-lockfile/node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "dev": true, - "license": "MIT" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/ripemd160": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz", - "integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hash-base": "^3.1.2", - "inherits": "^2.0.4" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rlp": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", - "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "bn.js": "^5.2.0" - }, - "bin": { - "rlp": "bin/rlp" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", - "dev": true, - "license": "MIT" - }, - "node_modules/secp256k1": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz", - "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "elliptic": "^6.5.7", - "node-addon-api": "^5.0.0", - "node-gyp-build": "^4.2.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/secp256k1/node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", - "dev": true, - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true, - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, - "license": "ISC" - }, - "node_modules/sha.js": { - "version": "2.4.12", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", - "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", - "dev": true, - "license": "(MIT AND BSD-3-Clause)", - "dependencies": { - "inherits": "^2.0.4", - "safe-buffer": "^5.2.1", - "to-buffer": "^1.2.0" - }, - "bin": { - "sha.js": "bin.js" - }, - "engines": { - "node": ">= 0.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/solc": { - "version": "0.8.26", - "resolved": "https://registry.npmjs.org/solc/-/solc-0.8.26.tgz", - "integrity": "sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "command-exists": "^1.2.8", - "commander": "^8.1.0", - "follow-redirects": "^1.12.1", - "js-sha3": "0.8.0", - "memorystream": "^0.3.1", - "semver": "^5.5.0", - "tmp": "0.0.33" - }, - "bin": { - "solcjs": "solc.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/solc/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/solidity-ast": { - "version": "0.4.61", - "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.61.tgz", - "integrity": "sha512-OYBJYcYyG7gLV0VuXl9CUrvgJXjV/v0XnR4+1YomVe3q+QyENQXJJxAEASUz4vN6lMAl+C8RSRSr5MBAz09f6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/stacktrace-parser": { - "version": "0.1.11", - "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", - "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.7.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/stacktrace-parser/node_modules/type-fest": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", - "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strnum": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", - "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-buffer": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", - "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "isarray": "^2.0.5", - "safe-buffer": "^5.2.1", - "typed-array-buffer": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/to-buffer/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", - "dev": true, - "license": "0BSD" - }, - "node_modules/tsort": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", - "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==", - "dev": true, - "license": "MIT" - }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/undici": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, - "engines": { - "node": ">=14.0" - } - }, - "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", - "dev": true, - "license": "MIT" - }, - "node_modules/unfetch": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", - "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", - "dev": true, - "license": "MIT" - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", - "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/tooling/hardhat/package.json b/tooling/hardhat/package.json deleted file mode 100644 index befd6778991..00000000000 --- a/tooling/hardhat/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "ethrex-hardhat", - "private": true, - "version": "0.0.0", - "description": "Hardhat workspace for ethrex Solidity contracts", - "scripts": { - "compile": "hardhat compile", - "test": "hardhat test", - "test:upgrade": "hardhat test test/upgradeability.local.test.js", - "validate:upgrades": "node scripts/validate-upgrades.js" - }, - "devDependencies": { - "@nomicfoundation/hardhat-chai-matchers": "^2.0.6", - "@nomicfoundation/hardhat-ethers": "^3.0.4", - "@openzeppelin/hardhat-upgrades": "^3.2.1", - "@openzeppelin/contracts": "5.4.0", - "@openzeppelin/contracts-upgradeable": "5.4.0", - "chai": "^4.4.1", - "ethers": "^6.14.0", - "hardhat": "^2.24.0" - } -} diff --git a/tooling/hardhat/scripts/validate-upgrades.js b/tooling/hardhat/scripts/validate-upgrades.js deleted file mode 100644 index c0986bc6d7a..00000000000 --- a/tooling/hardhat/scripts/validate-upgrades.js +++ /dev/null @@ -1,122 +0,0 @@ -#!/usr/bin/env node -"use strict"; - -const path = require("path"); -const fs = require("fs"); -const { - validateUpgradeSafety, - ReferenceContractNotFound -} = require("@openzeppelin/upgrades-core"); - -function getArgValue(flag) { - const index = process.argv.indexOf(flag); - if (index === -1 || index + 1 >= process.argv.length) { - return undefined; - } - return process.argv[index + 1]; -} - -function ensureDirExists(dir, label) { - if (!fs.existsSync(dir)) { - throw new Error(`${label} directory not found: ${dir}`); - } -} - -async function main() { - const referenceDir = - getArgValue("--reference") || - getArgValue("-r") || - process.env.UPGRADE_REFERENCE_BUILD_INFO_DIR; - - if (!referenceDir) { - throw new Error( - "Missing reference build-info directory. Use --reference or set UPGRADE_REFERENCE_BUILD_INFO_DIR." - ); - } - - const buildInfoDir = path.join(__dirname, "..", "artifacts", "build-info"); - ensureDirExists(buildInfoDir, "Current build-info"); - ensureDirExists(referenceDir, "Reference build-info"); - - const referenceKey = path.basename(referenceDir); - - const exclude = [ - "crates/l2/contracts/src/example/**", - "fixtures/contracts/upgradeability/**", - "@openzeppelin/**", - "**/node_modules/**" - ]; - - const discoveryReport = await validateUpgradeSafety( - buildInfoDir, - undefined, - undefined, - {}, - [referenceDir], - exclude - ); - - const upgradeable = discoveryReport.upgradeableContractReports.map( - (report) => report.contract - ); - - if (upgradeable.length === 0) { - console.log("No upgradeable contracts detected."); - return; - } - - const failures = []; - const skipped = []; - - for (const contract of upgradeable) { - const reference = `${referenceKey}:${contract}`; - try { - const report = await validateUpgradeSafety( - buildInfoDir, - contract, - reference, - {}, - [referenceDir], - exclude - ); - if (!report.ok) { - failures.push(report); - } - } catch (err) { - if (err instanceof ReferenceContractNotFound) { - skipped.push(contract); - continue; - } - throw err; - } - } - - if (skipped.length > 0) { - console.log( - `Skipped ${skipped.length} upgradeable contract(s) missing in reference: ${skipped.join( - ", " - )}` - ); - } - - if (failures.length > 0) { - console.error("Upgrade compatibility check failed:\n"); - for (const report of failures) { - console.error(report.explain(false)); - console.error("\n"); - } - process.exitCode = 1; - return; - } - - const validatedCount = upgradeable.length - skipped.length; - - console.log( - `Upgrade compatibility check passed for ${validatedCount} contract(s).` - ); -} - -main().catch((err) => { - console.error(err); - process.exitCode = 1; -}); diff --git a/tooling/hardhat/test/counter.test.js b/tooling/hardhat/test/counter.test.js deleted file mode 100644 index cdb080e2e8b..00000000000 --- a/tooling/hardhat/test/counter.test.js +++ /dev/null @@ -1,17 +0,0 @@ -const { expect } = require("chai"); -const { ethers } = require("hardhat"); - -describe("Counter", function () { - it("increments", async function () { - const Counter = await ethers.getContractFactory("Counter"); - const counter = await Counter.deploy(); - await counter.waitForDeployment(); - - expect(await counter.count()).to.equal(0n); - - const tx = await counter.increment(); - await tx.wait(); - - expect(await counter.count()).to.equal(1n); - }); -}); diff --git a/tooling/hardhat/test/upgradeability.local.test.js b/tooling/hardhat/test/upgradeability.local.test.js deleted file mode 100644 index bc21603af50..00000000000 --- a/tooling/hardhat/test/upgradeability.local.test.js +++ /dev/null @@ -1,15 +0,0 @@ -const { expect } = require("chai"); -const { ethers, upgrades } = require("hardhat"); - -describe("Upgrade validation (local example)", function () { - it("accepts compatible upgrades and rejects incompatible ones", async function () { - const Box = await ethers.getContractFactory("BoxUpgradeable"); - const BoxV2Good = await ethers.getContractFactory("BoxUpgradeableV2Good"); - const BoxV2Bad = await ethers.getContractFactory("BoxUpgradeableV2Bad"); - - await upgrades.validateUpgrade(Box, BoxV2Good, { kind: "uups" }); - await expect( - upgrades.validateUpgrade(Box, BoxV2Bad, { kind: "uups" }) - ).to.be.rejected; - }); -}); diff --git a/tooling/hive_report/Cargo.toml b/tooling/hive_report/Cargo.toml deleted file mode 100644 index 4b988c0be5f..00000000000 --- a/tooling/hive_report/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "hive_report" -version.workspace = true -edition.workspace = true -authors.workspace = true -documentation.workspace = true -license.workspace = true - -[dependencies] -serde.workspace = true -serde_json.workspace = true diff --git a/tooling/hive_report/src/main.rs b/tooling/hive_report/src/main.rs deleted file mode 100644 index f7049acabeb..00000000000 --- a/tooling/hive_report/src/main.rs +++ /dev/null @@ -1,377 +0,0 @@ -use serde::Deserialize; -use serde_json::json; -use std::cmp::Ordering; -use std::collections::HashMap; -use std::fs::{self, File}; -use std::io::BufReader; - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestCase { - summary_result: SummaryResult, - name: String, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct SummaryResult { - pass: bool, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct JsonFile { - name: String, - test_cases: std::collections::HashMap, -} - -const HIVE_SLACK_BLOCKS_FILE_PATH: &str = "./hive_slack_blocks.json"; - -struct HiveResult { - category: String, - display_name: String, - passed_tests: usize, - total_tests: usize, - success_percentage: f64, -} - -struct CategoryResults { - name: String, - tests: Vec, -} - -impl CategoryResults { - fn total_passed(&self) -> usize { - self.tests.iter().map(|res| res.passed_tests).sum() - } - - fn total_tests(&self) -> usize { - self.tests.iter().map(|res| res.total_tests).sum() - } - - fn success_percentage(&self) -> f64 { - calculate_success_percentage(self.total_passed(), self.total_tests()) - } -} - -impl HiveResult { - fn new(suite: String, fork: String, passed_tests: usize, total_tests: usize) -> Self { - let (category, display_name) = match suite.as_str() { - "engine-api" => ("Engine", "Paris"), - "engine-auth" => ("Engine", "Auth"), - "engine-cancun" => ("Engine", "Cancun"), - "engine-exchange-capabilities" => ("Engine", "Exchange Capabilities"), - "engine-withdrawals" => ("Engine", "Shanghai"), - "discv4" => ("P2P", "Discovery V4"), - "eth" => ("P2P", "Eth capability"), - "snap" => ("P2P", "Snap capability"), - "rpc-compat" => ("RPC", "RPC API Compatibility"), - "sync" => ("Sync", "Node Syncing"), - "eels/consume-rlp" => ("EVM - Consume RLP", fork.as_str()), - "eels/consume-engine" => ("EVM - Consume Engine", fork.as_str()), - "eels/execute-blobs" => ("EVM - Execute Blobs", "Execute Blobs"), - other => { - eprintln!("Warn: Unknown suite: {other}. Skipping"); - ("", "") - } - }; - - let success_percentage = calculate_success_percentage(passed_tests, total_tests); - - HiveResult { - category: category.to_string(), - display_name: display_name.to_string(), - passed_tests, - total_tests, - success_percentage, - } - } - - fn should_skip(&self) -> bool { - self.category.is_empty() - } -} - -impl std::fmt::Display for HiveResult { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}: {}/{} ({:.02}%)", - self.display_name, self.passed_tests, self.total_tests, self.success_percentage - ) - } -} - -fn create_fork_result(json_data: &JsonFile, fork: &str, test_pattern: &str) -> HiveResult { - let total_tests = json_data - .test_cases - .iter() - .filter(|(_, test_case)| test_case.name.contains(test_pattern)) - .count(); - let passed_tests = json_data - .test_cases - .iter() - .filter(|(_, test_case)| { - test_case.name.contains(test_pattern) && test_case.summary_result.pass - }) - .count(); - HiveResult::new( - json_data.name.clone(), - fork.to_string(), - passed_tests, - total_tests, - ) -} - -fn calculate_success_percentage(passed_tests: usize, total_tests: usize) -> f64 { - if total_tests == 0 { - 0.0 - } else { - (passed_tests as f64 / total_tests as f64) * 100.0 - } -} - -fn build_slack_blocks( - categories: &[CategoryResults], - total_passed: usize, - total_tests: usize, -) -> serde_json::Value { - let total_percentage = calculate_success_percentage(total_passed, total_tests); - let mut blocks = vec![json!({ - "type": "header", - "text": { - "type": "plain_text", - "text": format!( - "Daily Hive Coverage report — {total_passed}/{total_tests} ({total_percentage:.02}%)" - ) - } - })]; - - for category in categories { - let category_passed = category.total_passed(); - let category_total = category.total_tests(); - let category_percentage = category.success_percentage(); - let status = if category_passed == category_total { - "✅" - } else { - "⚠️" - }; - - let mut lines = vec![format!( - "*{}* {}/{} ({:.02}%) {}", - category.name, category_passed, category_total, category_percentage, status - )]; - - let mut failing_tests: Vec<_> = category - .tests - .iter() - .filter(|result| result.passed_tests < result.total_tests) - .collect(); - - failing_tests.sort_by(|a, b| { - a.success_percentage - .partial_cmp(&b.success_percentage) - .unwrap_or(Ordering::Equal) - }); - - for result in failing_tests { - lines.push(format!( - "- {}: {}/{} ({:.02}%)", - result.display_name, - result.passed_tests, - result.total_tests, - result.success_percentage - )); - } - - blocks.push(json!({ - "type": "section", - "text": { - "type": "mrkdwn", - "text": lines.join("\n"), - }, - })); - } - - json!({ "blocks": blocks }) -} - -fn aggregate_result( - aggregated_results: &mut HashMap<(String, String), (usize, usize)>, - result: HiveResult, -) { - if result.should_skip() { - return; - } - - let HiveResult { - category, - display_name, - passed_tests, - total_tests, - .. - } = result; - - let entry = aggregated_results - .entry((category, display_name)) - .or_insert((0, 0)); - entry.0 += passed_tests; - entry.1 += total_tests; -} - -fn main() -> Result<(), Box> { - let mut aggregated_results: HashMap<(String, String), (usize, usize)> = HashMap::new(); - - for entry in fs::read_dir("hive/workspace/logs")? { - let entry = entry?; - let path = entry.path(); - - if path.is_file() - && path.extension().and_then(|s| s.to_str()) == Some("json") - && path.file_name().and_then(|s| s.to_str()) != Some("hive.json") - { - let file_name = path - .file_name() - .and_then(|s| s.to_str()) - .expect("Path should be a valid string"); - let file = File::open(&path)?; - let reader = BufReader::new(file); - - let json_data: JsonFile = match serde_json::from_reader(reader) { - Ok(data) => data, - Err(_) => { - eprintln!("Error processing file: {file_name}"); - continue; - } - }; - - // Both of these simulators have only 1 suite where we can find tests for 3 different forks. - // To get the total tests and the passed tests a filtes is done each time so we do not clone the test cases each time. - if json_data.name.as_str() == "eels/consume-rlp" - || json_data.name.as_str() == "eels/consume-engine" - { - let result_paris = create_fork_result(&json_data, "Paris", "fork_Paris"); - // Shanghai - let result_shanghai = create_fork_result(&json_data, "Shanghai", "fork_Shanghai"); - // Cancun - let result_cancun = create_fork_result(&json_data, "Cancun", "fork_Cancun"); - // Prague - let result_prague = create_fork_result(&json_data, "Prague", "fork_Prague"); - // Osaka - let result_osaka = create_fork_result(&json_data, "Osaka", "fork_Osaka"); - - let result_amsterdam = - create_fork_result(&json_data, "Amsterdam", "fork_Amsterdam"); - - aggregate_result(&mut aggregated_results, result_paris); - aggregate_result(&mut aggregated_results, result_shanghai); - aggregate_result(&mut aggregated_results, result_cancun); - aggregate_result(&mut aggregated_results, result_prague); - aggregate_result(&mut aggregated_results, result_osaka); - aggregate_result(&mut aggregated_results, result_amsterdam); - } else { - let total_tests = json_data.test_cases.len(); - let passed_tests = json_data - .test_cases - .values() - .filter(|test_case| test_case.summary_result.pass) - .count(); - - let result = - HiveResult::new(json_data.name, String::new(), passed_tests, total_tests); - aggregate_result(&mut aggregated_results, result); - } - } - } - - let mut results: Vec = aggregated_results - .into_iter() - .map( - |((category, display_name), (passed_tests, total_tests))| HiveResult { - category, - display_name, - passed_tests, - total_tests, - success_percentage: calculate_success_percentage(passed_tests, total_tests), - }, - ) - .collect(); - - // First by category ascending, use fork ordering newest → oldest when applicable, then by passed tests descending. - results.sort_by(|a, b| { - let category_cmp = a.category.cmp(&b.category); - if category_cmp != Ordering::Equal { - return category_cmp; - } - - let fork_rank = |display_name: &str| match display_name { - "Amsterdam" => Some(0), - "Osaka" => Some(1), - "Prague" => Some(2), - "Cancun" => Some(3), - "Shanghai" => Some(4), - "Paris" => Some(5), - _ => None, - }; - - if let (Some(rank_a), Some(rank_b)) = - (fork_rank(&a.display_name), fork_rank(&b.display_name)) - { - let order_cmp = rank_a.cmp(&rank_b); - if order_cmp != Ordering::Equal { - return order_cmp; - } - } - - b.passed_tests.cmp(&a.passed_tests).then_with(|| { - b.success_percentage - .partial_cmp(&a.success_percentage) - .unwrap() - }) - }); - - let mut grouped_results: Vec = Vec::new(); - for result in results { - if let Some(last) = grouped_results - .last_mut() - .filter(|last| last.name == result.category) - { - last.tests.push(result); - continue; - } - - let name = result.category.clone(); - grouped_results.push(CategoryResults { - name, - tests: vec![result], - }); - } - - for category in &grouped_results { - println!("*{}*", category.name); - for result in &category.tests { - println!("\t{result}"); - } - println!(); - } - - println!(); - let total_passed = grouped_results - .iter() - .flat_map(|group| group.tests.iter().map(|r| r.passed_tests)) - .sum::(); - let total_tests = grouped_results - .iter() - .flat_map(|group| group.tests.iter().map(|r| r.total_tests)) - .sum::(); - let total_percentage = calculate_success_percentage(total_passed, total_tests); - println!("*Total: {total_passed}/{total_tests} ({total_percentage:.02}%)*"); - - let slack_blocks = build_slack_blocks(&grouped_results, total_passed, total_tests); - fs::write( - HIVE_SLACK_BLOCKS_FILE_PATH, - serde_json::to_string_pretty(&slack_blocks)?, - )?; - - Ok(()) -} diff --git a/tooling/import_benchmark/Makefile b/tooling/import_benchmark/Makefile deleted file mode 100644 index 38e02a8a879..00000000000 --- a/tooling/import_benchmark/Makefile +++ /dev/null @@ -1,20 +0,0 @@ -BENCH_ID ?= 1 -NETWORK ?= hoodi -NUM_REPETITIONS ?= 3 - -help: - @printf "run-bench: ## Runs a bench for the current pr. \nParameters:\n -BENCH_ID: number for the log file where it will\ - be saved with the format bench-BENCH_ID.log\n -NETWORK: which network to access (hoodi, mainnet)\nRequirements:\n This tool assumes we are running on Linux,\ - and that we have a valid db in ~/.local/share/ethrex_NETWORK_bench/ethrex\n with a valid state and a list of blocks for import\ - in ~/.local/share/ethrex_NETWORK_bench/chain.rlp\n\n" - @printf "run-benches: ## Runs multiple repetitions of the bench for the current pr. \nParameters:\n -NUM_REPETITIONS: number of runs\n -NETWORK: which network to access (hoodi, mainnet)\nRequirements:\n This tool assumes we are running on Linux,\ - and that we have a valid db in ~/.local/share/ethrex_NETWORK_bench/ethrex\n with a valid state and a list of blocks for import\ - in ~/.local/share/ethrex_NETWORK_bench/chain.rlp\n\n" - @printf "python3 parse_bench.py <...> : ## Parses the given bench log files to find average ggas\nRequirements\n\ - This script assumes we have the bench logs on the ethrex folder\n\n" - -run-bench: ## Runs a bench for the current pr. parameters -BENCH_ID: number for the log file where it will be saved -NETWORK: which network to access - ./benchmark.sh $(NETWORK) 1 $(BENCH_ID) - -run-benches: ## Runs multiple repetitions of the bench for the current pr. parameters -NUM_REPETITIONS: number of runs -NETWORK: which network to access - ./benchmark.sh $(NETWORK) $(NUM_REPETITIONS) diff --git a/tooling/import_benchmark/README.md b/tooling/import_benchmark/README.md deleted file mode 100644 index 7521622dc7c..00000000000 --- a/tooling/import_benchmark/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# Import Benchmark - -## Why - -This tool is used to benchmark the performance of **ethrex**. -We aim to execute the same set of blocks on the same hardware to ensure consistent -performance comparisons. Doing this on a running node is difficult because of variations -in hardware, peer count, block content, and system load. - -To achieve consistent results, we run the same blocks multiple times on the same machine -using the `import-bench` subcommand. - -## Setup - -To run this benchmark, you will need: - -- An **ethrex** database containing the blockchain state (required for realistic - database performance testing), located at: - `~/.local/share/ethrex_NETWORK_bench/ethrex` -- The database **must have completed snapshot generation** (`flatkeyvalue` generation). - *(On mainnet, this process takes about 8 hours.)* -- A `chain.rlp` file containing the blocks you want to test, located at: - `~/.local/share/ethrex_NETWORK_bench/chain.rlp` -- It is recommended that the file contains **at least 1,000 blocks**, -which can be generated using the `export` subcommand in ethrex. - -### Recommended procedure - -1. Run an ethrex node until it fully syncs and generates the snapshots. -2. Shut down the node and copy the database and the last block number. -3. Restart the node and let it advance by *X* additional blocks. -4. Stop the node again and run: - ```bash - ethrex export --first --last ~/.local/share/ethrex_NETWORK_bench/chain.rlp - ``` - -## Run - -The Makefile includes the following command: - -``` -run-bench: ## Runs a benchmark for the current PR. -``` - -Parameters: - - BENCH_ID: Identifier for the log file, saved as bench-BENCH_ID.log - - NETWORK: Network to access (e.g., hoodi, mainnet) - -Example: - -`make run-bench BENCH_ID=1 NETWORK=mainnet` - -## View Output - -You can view and compare benchmark results with: -`python3 parse_bench.py <...>` diff --git a/tooling/import_benchmark/benchmark.sh b/tooling/import_benchmark/benchmark.sh deleted file mode 100755 index 6450dc2eb23..00000000000 --- a/tooling/import_benchmark/benchmark.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash - -# tooling/import_benchmark/benchmark.sh -# ---------------------------------- -# Usage: tooling/import_benchmark/benchmark.sh -# -# Arguments: -# Name of the network to use (e.g.: 'mainnet', 'hoodi', 'sepolia'). -# Number of times to run the benchmark (positive integer). -# -# Description: -# This script runs the import benchmark repeatedly and saves each run's output -# to an incrementally numbered log file named `bench-.log`. -# -# Example: -# ./tooling/import_benchmark/benchmark.sh mainnet 5 # run 5 repetitions with mainnet -# ./tooling/import_benchmark/benchmark.sh mainnet 5 10 # same, but start with bench_id 10 -# -set -euo pipefail - -NETWORK=$1 -NUM_REPETITIONS=$2 -# Optional third argument: starting bench id. If provided, this value is used -# as the current `bench_id` (the script will increment it before the first run). -START_BENCH_ID=${3:-} - -# Get the directory of this script, so it can be ran from anywhere -BENCHMARKS_DIR=$( - cd "$(dirname "${BASH_SOURCE[0]}")" - pwd -P -) - -# Go to ethrex/tooling/import_benchmark -cd "$BENCHMARKS_DIR" - -# Create directory for benchmark results, if it doesn't exist -mkdir -p bench_results - -if [ -n "$START_BENCH_ID" ]; then - if ! [[ "$START_BENCH_ID" =~ ^[0-9]+$ ]]; then - echo "Error: START_BENCH_ID must be a non-negative integer" >&2 - exit 2 - fi - bench_id=$START_BENCH_ID -else - # Determine the next available bench id - cd bench_results - touch bench-0.log - bench_id=$(ls bench-*.log | cut -d '-' -f 2 | cut -d '.' -f 1 | sort -n | tail -1) - # When no START_BENCH_ID is supplied, start from the next available id - bench_id=$((bench_id + 1)) - cd .. -fi - -# Move to repo root -cd ../.. - -for i in $(seq 1 $NUM_REPETITIONS); do - # Remove previous temp data dir and copy base state to it - rm -rf ~/.local/share/temp - cp -r ~/.local/share/ethrex_${NETWORK}_bench/ethrex ~/.local/share/temp - - # Run the benchmark and save output to log file in the benchmarks directory - cargo run --release -- --network $NETWORK --datadir ~/.local/share/temp import-bench ~/.local/share/ethrex_${NETWORK}_bench/chain.rlp | tee "${BENCHMARKS_DIR}/bench_results/bench-${bench_id}.log" - - bench_id=$((bench_id + 1)) -done diff --git a/tooling/import_benchmark/parse_bench.py b/tooling/import_benchmark/parse_bench.py deleted file mode 100644 index 89a941da7de..00000000000 --- a/tooling/import_benchmark/parse_bench.py +++ /dev/null @@ -1,41 +0,0 @@ -import sys - -bench = {} -bench_around = {} - -for file_path in sys.argv[1:]: - bench_around[file_path] = {} - - with open(file_path, "r") as file: - for line in file: - if "Finished regenerating state" in line: - break - - for line in file: - if "[METRIC]" in line: - block_num = line.split(")")[0][-7:] - ggas = line.split(")")[1][2:7] - - if block_num not in bench: - bench[block_num] = {} - bench[block_num][file_path] = float(ggas) - bench_around[file_path][block_num] = float(ggas) - -total = 0 -count = 0 -for block in bench.values(): - for ggas in block.values(): - total += ggas - count += 1 - - -print("Blocks tested", len(bench)) -print("Mean ggas across multiple runs:", total / count) -for run_count, run in bench_around.items(): - print("Mean ggas in run:", run_count, sum(run.values()) / len(run.values())) - -average_difference = [] -for block_num, block in bench.items(): - average_difference.append(max(block.values()) - min(block.values())) - pass -print("Mean ggas spread across blocks:", sum(average_difference) / count) diff --git a/tooling/l2/dev/README.md b/tooling/l2/dev/README.md deleted file mode 100644 index 5273c77730f..00000000000 --- a/tooling/l2/dev/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# L2 dev environment - -## Usage - -1. Download the docker compose - -curl -L https://raw.githubusercontent.com/lambdaclass/ethrex/main/tooling/l2/dev/docker-compose.yaml -o docker-compose.yaml - -2. Start the containers - -```shell -docker compose up -``` - -this will launch: - -- on localhost:8083 blockscout explorer for L1 -- on localhost:8082 blockscout explorer for L2 -- on localhost:1729 l2 rpc -- on localhost:8545 l1 rpc -- on localhost:5173 ethrex L2 hub for withdrawals deposits and account abstraction - -3. Stop the containers and delete the volumes - -> [!NOTE] -> It is recommended to delete all the volumes because blockscout will keep the old state of the blockchain on its db -> but ethrex l2 dev mode starts a new chain on every restart. For this reason we use the `-v` flag - -```shell -docker compose down -v -``` diff --git a/tooling/l2/dev/docker-compose.yaml b/tooling/l2/dev/docker-compose.yaml deleted file mode 100644 index d72449c16b0..00000000000 --- a/tooling/l2/dev/docker-compose.yaml +++ /dev/null @@ -1,676 +0,0 @@ -services: - # DB services - redis-db: - image: 'redis:alpine' - container_name: redis-db - command: redis-server - - db-init: - image: postgres:17 - volumes: - - blockscout-db-data:/var/lib/postgresql/data - entrypoint: - - sh - - -c - - | - chown -R 2000:2000 /var/lib/postgresql/data - - db: - image: postgres:17 - user: 2000:2000 - shm_size: 256m - restart: always - container_name: 'db' - command: postgres -c 'max_connections=200' -c 'client_connection_check_interval=60000' - environment: - POSTGRES_DB: 'blockscout' - POSTGRES_USER: 'blockscout' - POSTGRES_PASSWORD: 'ceWb1MeLBEeOIfk65gU8EjF8' - ports: - - target: 5432 - published: 7432 - volumes: - - blockscout-db-data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U blockscout -d blockscout"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s - depends_on: - db-init: - condition: service_completed_successfully - - stats-db-init: - image: postgres:17 - volumes: - - stats-db-data:/var/lib/postgresql/data - entrypoint: - - sh - - -c - - | - chown -R 2000:2000 /var/lib/postgresql/data - - stats-db: - image: postgres:17 - user: 2000:2000 - shm_size: 256m - restart: always - container_name: 'stats-db' - command: postgres -c 'max_connections=200' - environment: - POSTGRES_DB: 'stats' - POSTGRES_USER: 'stats' - POSTGRES_PASSWORD: 'n0uejXPl61ci6ldCuE2gQU5Y' - ports: - - target: 5432 - published: 7433 - volumes: - - stats-db-data:/var/lib/postgresql/data - depends_on: - stats-db-init: - condition: service_completed_successfully - healthcheck: - test: ["CMD-SHELL", "pg_isready -U stats -d stats"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 10s - - # L1 Blockscout Services - backend-l1: - image: ghcr.io/lambdaclass/blockscout-private:9.2.2.commit.763c41da - pull_policy: always - depends_on: - db: - condition: service_healthy - redis-db: - condition: service_started - links: - - db:database - stop_grace_period: 5m - container_name: 'backend-l1' - command: sh -c "bin/blockscout eval \"Elixir.Explorer.ReleaseTasks.create_and_migrate()\" && bin/blockscout start" - extra_hosts: - - 'host.docker.internal:host-gateway' - environment: - ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:8545/ - ETHEREUM_JSONRPC_TRACE_URL: http://host.docker.internal:8545/ - CHAIN_ID: '9' - ETHEREUM_JSONRPC_VARIANT: geth - DISABLE_FILE_LOGGING: false - DATABASE_URL: postgresql://blockscout:ceWb1MeLBEeOIfk65gU8EjF8@db:5432/blockscout_l1 - ETHEREUM_JSONRPC_TRANSPORT: http - ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES: false - SECRET_KEY_BASE: 56NtB48ear7+wMSf0IQuWDAAazhpb31qyc7GiyspBP2vh7t5zlCsF5QDv76chXeN - PORT: 4000 - COIN_NAME: Ether - COIN: ETH - DISABLE_MARKET: true - POOL_SIZE: 80 - POOL_SIZE_API: 10 - ECTO_USE_SSL: false - HEART_BEAT_TIMEOUT: 30 - RELEASE_LINK: "prague,default" - ADMIN_PANEL_ENABLED: false - API_V1_READ_METHODS_DISABLED: false - API_V1_WRITE_METHODS_DISABLED: false - TXS_STATS_DAYS_TO_COMPILE_AT_INIT: 10 - COIN_BALANCE_HISTORY_DAYS: 90 - RE_CAPTCHA_DISABLED: false - MICROSERVICE_SC_VERIFIER_TYPE: eth_bytecode_db - MICROSERVICE_VISUALIZE_SOL2UML_ENABLED: true - MICROSERVICE_VISUALIZE_SOL2UML_URL: http://visualizer-l1:8050/ - MICROSERVICE_SIG_PROVIDER_ENABLED: true - MICROSERVICE_SIG_PROVIDER_URL: http://sig-provider-l1:8050/ - MICROSERVICE_ACCOUNT_ABSTRACTION_ENABLED: false - DECODE_NOT_A_CONTRACT_CALLS: true - ACCOUNT_ENABLED: false - ACCOUNT_REDIS_URL: redis://redis-db:6379/1 - NFT_MEDIA_HANDLER_ENABLED: false - NFT_MEDIA_HANDLER_REMOTE_DISPATCHER_NODE_MODE_ENABLED: false - NFT_MEDIA_HANDLER_BUCKET_FOLDER: /folder_1 - RELEASE_NODE: producer@172.18.0.4 - RELEASE_DISTRIBUTION: name - RELEASE_COOKIE: secret_cookie - - visualizer-l1: - image: ghcr.io/blockscout/visualizer:v0.2.1 - platform: linux/amd64 - restart: always - container_name: 'visualizer-l1' - environment: - VISUALIZER__SERVER__GRPC__ENABLED: false - - sig-provider-l1: - image: ghcr.io/blockscout/sig-provider:v1.1.1 - platform: linux/amd64 - restart: always - container_name: 'sig-provider-l1' - - frontend-l1: - image: ghcr.io/lambdaclass/frontend-private:test - platform: linux/amd64 - container_name: 'frontend-l1' - depends_on: - - backend-l1 - environment: - NEXT_PUBLIC_API_HOST: localhost:8083 - NEXT_PUBLIC_API_PROTOCOL: http - NEXT_PUBLIC_STATS_API_HOST: http://localhost:8084 - NEXT_PUBLIC_NETWORK_NAME: Ethrex L1 - NEXT_PUBLIC_NETWORK_SHORT_NAME: Ethrex L1 - NEXT_PUBLIC_NETWORK_ID: 9 - NEXT_PUBLIC_NETWORK_CURRENCY_NAME: Ether - NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL: ETH - NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS: 18 - NEXT_PUBLIC_API_BASE_PATH: / - NEXT_PUBLIC_APP_HOST: localhost:8083 - NEXT_PUBLIC_APP_PROTOCOL: http - NEXT_PUBLIC_HOMEPAGE_CHARTS: "['daily_txs']" - NEXT_PUBLIC_VISUALIZE_API_HOST: http://localhost:8085 - NEXT_PUBLIC_IS_TESTNET: true - NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL: ws - NEXT_PUBLIC_API_SPEC_URL: https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml - NEXT_PUBLIC_AD_BANNER_PROVIDER: none - NEXT_PUBLIC_AD_TEXT_PROVIDER: none - NEXT_PUBLIC_MARKETPLACE_ENABLED: false - NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE: false - - stats-l1: - image: ghcr.io/blockscout/stats:v2.12.0 - platform: linux/amd64 - restart: always - container_name: 'stats-l1' - extra_hosts: - - 'host.docker.internal:host-gateway' - depends_on: - - stats-db - - backend-l1 - environment: - - STATS__DB_URL=${STATS__DB_URL:-postgres://stats:n0uejXPl61ci6ldCuE2gQU5Y@stats-db:5432/stats_l1} - - STATS__BLOCKSCOUT_DB_URL=${STATS__BLOCKSCOUT_DB_URL:-postgresql://blockscout:ceWb1MeLBEeOIfk65gU8EjF8@db:5432/blockscout_l1} - - STATS__CREATE_DATABASE=${STATS__CREATE_DATABASE:-true} - - STATS__RUN_MIGRATIONS=${STATS__RUN_MIGRATIONS:-true} - - STATS__BLOCKSCOUT_API_URL=${STATS__BLOCKSCOUT_API_URL:-http://host.docker.internal:8083} - - STATS__SERVER__HTTP__CORS__ENABLED=${STATS__SERVER__HTTP__CORS__ENABLED:-false} - - STATS__SERVER__HTTP__CORS__ALLOWED_ORIGIN=${STATS__SERVER__HTTP__CORS__ALLOWED_ORIGIN} - - STATS__CONDITIONAL_START__USER_OPS_PAST_INDEXING_FINISHED__ENABLED=false - - STATS__SERVER__HTTP__ENABLED=true - - STATS__SERVER__HTTP__ADDR=0.0.0.0:8050 - - STATS__SERVER__HTTP__MAX_BODY_SIZE=2097152 - - STATS__SERVER__HTTP__CORS__ALLOWED_ORIGIN=http://localhost:8084 - - STATS__SERVER__GRPC__ENABLED=false - - STATS__SERVER__GRPC__ADDR=0.0.0.0:8051 - - STATS__DEFAULT_SCHEDULE=0 0 1 * * * * - - STATS__FORCE_UPDATE_ON_START=false - - STATS__METRICS__ENABLED=false - - STATS__METRICS__ADDR=0.0.0.0:6060 - - STATS__METRICS__ROUTE=/metrics - - STATS__JAEGER__ENABLED=false - - STATS__JAEGER__AGENT_ENDPOINT=localhost:6831 - - STATS__TRACING__ENABLED=true - - STATS__TRACING__FORMAT=default - - # L2 Blockscout Services - backend: - image: ghcr.io/lambdaclass/blockscout-private:9.2.2.commit.763c41da - depends_on: - db: - condition: service_healthy - redis-db: - condition: service_started - links: - - db:database - stop_grace_period: 5m - container_name: 'backend' - command: sh -c "bin/blockscout eval \"Elixir.Explorer.ReleaseTasks.create_and_migrate()\" && bin/blockscout start" - extra_hosts: - - 'host.docker.internal:host-gateway' - environment: - ETHEREUM_JSONRPC_HTTP_URL: http://host.docker.internal:1729/ - ETHEREUM_JSONRPC_TRACE_URL: http://host.docker.internal:1729/ - CHAIN_ID: '65536999' - ETHEREUM_JSONRPC_VARIANT: geth - DISABLE_FILE_LOGGING: false - DATABASE_URL: postgresql://blockscout:ceWb1MeLBEeOIfk65gU8EjF8@db:5432/blockscout_l2 - ETHEREUM_JSONRPC_TRANSPORT: http - ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES: false - SECRET_KEY_BASE: 56NtB48ear7+wMSf0IQuWDAAazhpb31qyc7GiyspBP2vh7t5zlCsF5QDv76chXeN - PORT: 4000 - COIN_NAME: Ether - COIN: ETH - DISABLE_MARKET: true - POOL_SIZE: 80 - POOL_SIZE_API: 10 - ECTO_USE_SSL: false - HEART_BEAT_TIMEOUT: 30 - RELEASE_LINK: "prague,default" - ADMIN_PANEL_ENABLED: false - API_V1_READ_METHODS_DISABLED: false - API_V1_WRITE_METHODS_DISABLED: false - TXS_STATS_DAYS_TO_COMPILE_AT_INIT: 10 - COIN_BALANCE_HISTORY_DAYS: 90 - RE_CAPTCHA_DISABLED: false - MICROSERVICE_SC_VERIFIER_TYPE: eth_bytecode_db - MICROSERVICE_VISUALIZE_SOL2UML_ENABLED: true - MICROSERVICE_VISUALIZE_SOL2UML_URL: http://visualizer:8050/ - MICROSERVICE_SIG_PROVIDER_ENABLED: true - MICROSERVICE_SIG_PROVIDER_URL: http://sig-provider:8050/ - MICROSERVICE_ACCOUNT_ABSTRACTION_ENABLED: false - DECODE_NOT_A_CONTRACT_CALLS: true - ACCOUNT_ENABLED: false - ACCOUNT_REDIS_URL: redis://redis-db:6379/2 - NFT_MEDIA_HANDLER_ENABLED: false - NFT_MEDIA_HANDLER_REMOTE_DISPATCHER_NODE_MODE_ENABLED: false - NFT_MEDIA_HANDLER_BUCKET_FOLDER: /folder_1 - RELEASE_NODE: producer@172.18.0.4 - RELEASE_DISTRIBUTION: name - RELEASE_COOKIE: secret_cookie - - visualizer: - image: ghcr.io/blockscout/visualizer:v0.2.1 - platform: linux/amd64 - restart: always - container_name: 'visualizer' - environment: - VISUALIZER__SERVER__GRPC__ENABLED: false - - sig-provider: - image: ghcr.io/blockscout/sig-provider:v1.1.1 - platform: linux/amd64 - restart: always - container_name: 'sig-provider' - - frontend: - image: ghcr.io/lambdaclass/frontend-private:test - platform: linux/amd64 - container_name: 'frontend' - depends_on: - - backend - environment: - NEXT_PUBLIC_API_HOST: localhost:8082 - NEXT_PUBLIC_API_PROTOCOL: http - NEXT_PUBLIC_STATS_API_HOST: http://localhost:8080 - NEXT_PUBLIC_NETWORK_NAME: Ethrex L2 - NEXT_PUBLIC_NETWORK_SHORT_NAME: Ethrex L2 - NEXT_PUBLIC_NETWORK_ID: 65536999 - NEXT_PUBLIC_NETWORK_CURRENCY_NAME: Ether - NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL: ETH - NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS: 18 - NEXT_PUBLIC_API_BASE_PATH: / - NEXT_PUBLIC_APP_HOST: localhost:8082 - NEXT_PUBLIC_APP_PROTOCOL: http - NEXT_PUBLIC_HOMEPAGE_CHARTS: "['daily_txs']" - NEXT_PUBLIC_VISUALIZE_API_HOST: http://localhost:8081 - NEXT_PUBLIC_IS_TESTNET: true - NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL: ws - NEXT_PUBLIC_API_SPEC_URL: https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml - NEXT_PUBLIC_AD_BANNER_PROVIDER: none - NEXT_PUBLIC_AD_TEXT_PROVIDER: none - NEXT_PUBLIC_MARKETPLACE_ENABLED: false - NEXT_PUBLIC_PROMOTE_BLOCKSCOUT_IN_TITLE: false - - stats: - image: ghcr.io/blockscout/stats:v2.12.0 - platform: linux/amd64 - restart: always - container_name: 'stats' - extra_hosts: - - 'host.docker.internal:host-gateway' - depends_on: - - stats-db - - backend - environment: - - STATS__DB_URL=${STATS__DB_URL:-postgres://stats:n0uejXPl61ci6ldCuE2gQU5Y@stats-db:5432/stats_l2} - - STATS__BLOCKSCOUT_DB_URL=${STATS__BLOCKSCOUT_DB_URL:-postgresql://blockscout:ceWb1MeLBEeOIfk65gU8EjF8@db:5432/blockscout_l2} - - STATS__CREATE_DATABASE=${STATS__CREATE_DATABASE:-true} - - STATS__RUN_MIGRATIONS=${STATS__RUN_MIGRATIONS:-true} - - STATS__BLOCKSCOUT_API_URL=${STATS__BLOCKSCOUT_API_URL:-http://host.docker.internal:8082} - - STATS__SERVER__HTTP__CORS__ENABLED=${STATS__SERVER__HTTP__CORS__ENABLED:-false} - - STATS__SERVER__HTTP__CORS__ALLOWED_ORIGIN=${STATS__SERVER__HTTP__CORS__ALLOWED_ORIGIN} - - STATS__CONDITIONAL_START__USER_OPS_PAST_INDEXING_FINISHED__ENABLED=false - - STATS__SERVER__HTTP__ENABLED=true - - STATS__SERVER__HTTP__ADDR=0.0.0.0:8050 - - STATS__SERVER__HTTP__MAX_BODY_SIZE=2097152 - # - STATS__SERVER__HTTP__CORS__ENABLED=true - - STATS__SERVER__HTTP__CORS__ALLOWED_ORIGIN=http://localhost:8080 - - STATS__SERVER__GRPC__ENABLED=false - - STATS__SERVER__GRPC__ADDR=0.0.0.0:8051 - - STATS__DEFAULT_SCHEDULE=0 0 1 * * * * - - STATS__FORCE_UPDATE_ON_START=false - - STATS__METRICS__ENABLED=false - - STATS__METRICS__ADDR=0.0.0.0:6060 - - STATS__METRICS__ROUTE=/metrics - - STATS__JAEGER__ENABLED=false - - STATS__JAEGER__AGENT_ENDPOINT=localhost:6831 - - STATS__TRACING__ENABLED=true - - STATS__TRACING__FORMAT=default - ports: - - 8050:8050 - - proxy: - depends_on: - - backend - - frontend - - stats - - backend-l1 - - frontend-l1 - - stats-l1 - image: nginx - container_name: proxy - extra_hosts: - - 'host.docker.internal:host-gateway' - configs: - - source: nginx_conf - target: /etc/nginx/conf.d/default.conf - restart: always - ports: - - target: 8082 - published: 8082 - - target: 8080 - published: 8080 - - target: 8081 - published: 8081 - - target: 8083 - published: 8083 - - target: 8084 - published: 8084 - - target: 8085 - published: 8085 - - # Ethrex Services - ethrex-dev: - container_name: ethrex-dev - pull_policy: always - image: "ghcr.io/lambdaclass/ethrex:l2" - depends_on: - - proxy - ports: - - 8545:8545 - - 5555:5555 - - 1729:1729 - configs: - - source: ethrex-sponsor-addresses - target: /usr/local/bin/sponsorable-addresses.txt - command: > - l2 --dev --no-monitor --proof-coordinator.addr 0.0.0.0 --admin-server.addr 0.0.0.0 --block-producer.block-time 1000 --sponsorable-addresses sponsorable-addresses.txt - - ethrex-prover: - container_name: ethrex-prover - pull_policy: always - image: "ghcr.io/lambdaclass/ethrex:l2" - depends_on: - - ethrex-dev - command: > - l2 prover --proof-coordinators tcp://ethrex-dev:3900 - - ethrex-delegation-contracts: - image: ghcr.io/lambdaclass/rex:latest - platform: linux/amd64 - depends_on: - - ethrex-dev - entrypoint: > - sh -c " - set -e - echo 'Waiting for L2...' - until rex block-number http://ethrex-dev:1729 >/dev/null 2>&1; do - echo 'Not ready, retrying...' - sleep 2 - done - echo 'L2 is ready!' - while true; do - BAL=\$(rex balance 0x0000bd19F707CA481886244bDd20Bd6B8a81bd3e http://ethrex-dev:1729) - if [ \"$$BAL\" != \"0\" ]; then - echo 'Account has funds' - break - fi - echo 'Balance still zero, retrying...' - sleep 2 - done - rex deploy --bytecode 0x608060405234601c57600e6020565b6118c761002b82396118c790f35b6026565b60405190565b5f80fdfe60806040526004361015610015575b366103d457005b61001f5f3561005e565b80630f0b3785146100595780633bc67d4b14610054578063affed0e01461004f5763c417a8c50361000e5761039e565b610303565b61025d565b6100d2565b60e01c90565b60405190565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b908160409103126100885790565b610076565b9081606091031261009b5790565b610076565b919060a0838203126100c857806100bc6100c5925f860161007a565b9360400161008d565b90565b61006e565b5f0190565b34610101576100eb6100e53660046100a0565b9061076f565b6100f3610064565b806100fd816100cd565b0390f35b61006a565b60018060a01b031690565b61011a90610106565b90565b61012681610111565b0361012d57565b5f80fd5b9050359061013e8261011d565b565b90565b61014c81610140565b0361015357565b5f80fd5b9050359061016482610143565b565b5f80fd5b5f80fd5b5f80fd5b909182601f830112156101ac5781359167ffffffffffffffff83116101a75760200192600183028401116101a257565b61016e565b61016a565b610166565b908160409103126101bf5790565b610076565b908160a09103126101d25790565b610076565b60c081830312610258576101ed825f8301610131565b926101fb8360208401610157565b92604083013567ffffffffffffffff8111610253578161021c918501610172565b92909361022c83606083016101b1565b9260a082013567ffffffffffffffff811161024e5761024b92016101c4565b90565b610072565b610072565b61006e565b346102925761027c6102703660046101d7565b94939093929192610e00565b610284610064565b8061028e816100cd565b0390f35b61006a565b5f9103126102a157565b61006e565b1c90565b90565b6102bd9060086102c293026102a6565b6102aa565b90565b906102d091546102ad565b90565b6102de5f5f906102c5565b90565b6102ea90610140565b9052565b9190610301905f602085019401906102e1565b565b3461033357610313366004610297565b61032f61031e6102d3565b610326610064565b918291826102ee565b0390f35b61006a565b5f1c90565b61034961034e91610338565b6102aa565b90565b61035b905461033d565b90565b60019061037860016103715f8501610351565b9301610351565b90565b91602061039c92949361039560408201965f8301906102e1565b01906102e1565b565b346103cf576103ae366004610297565b6103b661035e565b906103cb6103c2610064565b9283928361037b565b0390f35b61006a565b5f80fd5b356103e281610143565b90565b90565b6103f46103f991610140565b6103e5565b9052565b6020939261041c85836104148295610424976103e8565b0180926103e8565b0180926103e8565b0190565b601f801991011690565b634e487b7160e01b5f52604160045260245ffd5b9061045090610428565b810190811067ffffffffffffffff82111761046a57604052565b610432565b60200190565b5190565b634e487b7160e01b5f52601160045260245ffd5b61049690610140565b5f1981146104a45760010190565b610479565b5f1b90565b906104ba5f19916104a9565b9181191691161790565b90565b6104db6104d66104e092610140565b6104c4565b610140565b90565b90565b906104fb6104f6610502926104c7565b6104e3565b82546104ae565b9055565b60ff1690565b61051581610506565b0361051c57565b5f80fd5b3561052a8161050c565b90565b90565b61054461053f6105499261052d565b6104c4565b610506565b90565b90565b61056361055e6105689261054c565b6104c4565b610506565b90565b90565b61058261057d6105879261056b565b6104c4565b610506565b90565b90565b6105a161059c6105a692610140565b6104a9565b61058a565b90565b6105b29061058a565b9052565b6105bf90610506565b9052565b6105f86105ff946105ee6060949897956105e4608086019a5f8701906105a9565b60208501906105b6565b60408301906105a9565b01906105a9565b565b610609610064565b3d5f823e3d90fd5b61062561062061062a92610106565b6104c4565b610106565b90565b61063690610611565b90565b6106429061062d565b90565b60209181520190565b5f7f556e617574686f72697a65640000000000000000000000000000000000000000910152565b610682600c602092610645565b61068b8161064e565b0190565b6106a49060208101905f818303910152610675565b90565b906106d2602060016106d8946106ca5f82016106c45f88016103d8565b906104e6565b0192016103d8565b906104e6565b565b906106e4916106a7565b565b506106f5906020810190610157565b90565b61070190610140565b9052565b90610712816020936106f8565b0190565b61073c6107316107419361072c5f8501856106e6565b610705565b9160208101906106e6565b610705565b50565b61075081604093610716565b0190565b61076990610760610064565b91829182610744565b03902090565b905f60209161077d82610351565b6107bb61078b8487016103d8565b916107ad61079a8789016103d8565b6107a2610064565b9485938985016103fd565b868201810382520382610446565b6107cd6107c782610475565b9161046f565b206107e86107e26107dd85610351565b61048d565b846104e6565b6107f460408301610520565b61080661080085610530565b91610506565b1483146108ff5761085461081a601b61056e565b925b61084261083d876108366108318986016103d8565b61058d565b93016103d8565b61058d565b9061084b610064565b948594856105c3565b838052039060015afa156108fa5761086c5f516104a9565b61088661088061087b30610639565b610111565b91610111565b036108d8576108968160016106da565b6108c07f7ab2d3841cf199f7bdf9a46a067653d528b42029acf32bdbe62dd8191ce226ad91610754565b906108c9610064565b806108d3816100cd565b0390a2565b6108e0610064565b62461bcd60e51b8152806108f66004820161068f565b0390fd5b610601565b61085461090c601c61054f565b9261081c565b60601b90565b61092190610912565b90565b61092d90610918565b90565b61093c61094191610111565b610924565b9052565b905090565b90825f939282370152565b9091826109658161096c93610945565b809361094a565b0190565b6014602093610994858461098c61099c966109a39c9a986103e8565b018092610930565b0180926103e8565b0191610955565b90565b5f80fd5b906109bd6109b6610064565b9283610446565b565b5f80fd5b5f80fd5b67ffffffffffffffff81116109e5576109e1602091610428565b0190565b610432565b909291926109ff6109fa826109c7565b6109aa565b93818552602085019082840111610a1b57610a199261094a565b565b6109c3565b9080601f83011215610a3e57816020610a3b933591016109ea565b90565b610166565b67ffffffffffffffff8111610a6157610a5d602091610428565b0190565b610432565b90929192610a7b610a7682610a43565b6109aa565b93818552602085019082840111610a9757610a959261094a565b565b6109c3565b9080601f83011215610aba57816020610ab793359101610a66565b90565b610166565b61ffff1690565b610acf81610abf565b03610ad657565b5f80fd5b90503590610ae782610ac6565b565b151590565b610af781610ae9565b03610afe57565b5f80fd5b90503590610b0f82610aee565b565b91909160a081840312610bb257610b2860a06109aa565b925f82013567ffffffffffffffff8111610bad5781610b48918401610a20565b5f85015260208201359167ffffffffffffffff8311610ba857610b7082610ba1948301610a9c565b6020860152610b828260408301610ada565b6040860152610b948260608301610ada565b6060860152608001610b02565b6080830152565b6109bf565b6109bf565b6109a6565b610bc2903690610b11565b90565b9190604083820312610bff57610bf890610bdf60406109aa565b93610bec825f8301610157565b5f860152602001610157565b6020830152565b6109a6565b610c0f903690610bc5565b90565b90610c1c90610140565b9052565b610c2a60406109aa565b90565b90610c64610c5b6001610c3e610c20565b94610c55610c4d5f8301610351565b5f8801610c12565b01610351565b60208401610c12565b565b610c6f90610c2d565b90565b5f7f496e76616c6964207369676e6174757265000000000000000000000000000000910152565b610ca66011602092610645565b610caf81610c72565b0190565b610cc89060208101905f818303910152610c99565b90565b9091610cd692610955565b90565b90610ceb610ce6836109c7565b6109aa565b918252565b606090565b3d5f14610d1057610d053d610cd9565b903d5f602084013e5b565b610d18610cf0565b90610d0e565b5f7f43616c6c206661696c6564000000000000000000000000000000000000000000910152565b610d52600b602092610645565b610d5b81610d1e565b0190565b610d749060208101905f818303910152610d45565b90565b15610d7e57565b610d86610064565b62461bcd60e51b815280610d9c60048201610d5f565b0390fd5b610da99061062d565b90565b60209181520190565b9190610dcf81610dc881610dd495610dac565b809561094a565b610428565b0190565b91610dfd939192610df060408201945f8301906102e1565b6020818503910152610db5565b90565b9194610e9590610e9b9295610e145f610351565b610e438691610e348a8c8b91610e28610064565b96879560208701610970565b60208201810382520382610446565b610e55610e4f82610475565b9161046f565b20610e70610e6a610e655f610351565b61048d565b5f6104e6565b9190610e8f610e89610e83600194610bb7565b92610c04565b92610c66565b9261117d565b15610ae9565b610f1857610ed05f80838688908791610ebe610eb5610064565b93849283610ccb565b03925af1610eca610cf5565b50610d77565b91929092610f13610f017fcaf938de11c367272220bfd1d2baa99ca46665e7bc4d85f00adb51b90fe1fa9f94610da0565b94610f0a610064565b93849384610dd8565b0390a2565b610f20610064565b62461bcd60e51b815280610f3660048201610cb3565b0390fd5b5f90565b90565b610f55610f50610f5a92610f3e565b6104c4565b610140565b90565b634e487b7160e01b5f52603260045260245ffd5b90610f7b82610475565b811015610f8d57600160209102010190565b610f5d565b90565b610fa9610fa4610fae92610f92565b6104c4565b610140565b90565b60ff60f81b1690565b610fc49051610fb1565b90565b610fd19051610ae9565b90565b90610fe6610fe183610a43565b6109aa565b918252565b5f74113a3cb832911d113bb2b130baba34371733b2ba1160591b910152565b6110146015610fd4565b9061102160208301610feb565b565b61102b61100a565b90565b6110389051610abf565b90565b61104f61104a61105492610abf565b6104c4565b610140565b90565b90565b61106661106b9161058a565b611057565b9052565b61107b8160209361105a565b0190565b6c1131b430b63632b733b2911d1160991b9052565b5190565b905090565b90825f9392825e0152565b6110cd6110c4926020926110bb81611094565b94858093611098565b9384910161109d565b0190565b601160f91b9052565b600191600d6110f3926110ec8161107f565b01906110a8565b6110fc816110d1565b0190565b9061112861110c610064565b809361111c6020830191826110da565b90810382520383610446565b565b90565b6111526111499260209261114081610475565b94858093610945565b9384910161109d565b0190565b61115f9161112d565b90565b611172611179916020949361112d565b809261105a565b0190565b611185610f3a565b506111925f830151610475565b6111a561119f6025610f41565b91610140565b108015611315575b61130d576111e46111de6111bf611023565b60208501516111d86111d36060880161102e565b61103b565b91611530565b15610ae9565b6113055761124b61122c61122761120f61121e61125195611203610064565b9283916020830161106f565b60208201810382520382610446565b60018091611620565b611100565b60208401516112456112406040870161102e565b61103b565b91611530565b15610ae9565b6112fe5760205f6112786112678385015161112a565b61126f610064565b91829182611156565b039060025afa156112f9575f6112d06020926112bf8361129881516104a9565b9201516112b16112a6610064565b938492888401611162565b868201810382520382610446565b6112c7610064565b91829182611156565b039060025afa156112f4576112f1916112e95f516104a9565b9190916117d5565b90565b610601565b610601565b5050505f90565b505050505f90565b505050505f90565b5061135361134d61133b6113365f8601516113306020610f95565b90610f71565b610fba565b61134760808601610fc7565b9061140e565b15610ae9565b6111ad565b90565b60f81b90565b61137561137061137a92611358565b61135b565b610fb1565b90565b6113876001611361565b90565b90565b6113a161139c6113a69261138a565b61135b565b610fb1565b90565b6113b3600461138d565b90565b90565b6113cd6113c86113d2926113b6565b61135b565b610fb1565b90565b6113df60086113b9565b90565b90565b6113f96113f46113fe926113e2565b61135b565b610fb1565b90565b61140b60106113e5565b90565b90611417610f3a565b508161142161137d565b1661143b61143561143061137d565b610fb1565b91610fb1565b036114da57806114af575b6114aa57806114536113d5565b1661146d6114676114626113d5565b610fb1565b91610fb1565b03611479575b50600190565b611481611401565b1661149b611495611490611401565b610fb1565b91610fb1565b146114a6575f611473565b5f90565b505f90565b50806114b96113a9565b166114d36114cd6114c86113a9565b610fb1565b91610fb1565b1415611446565b50505f90565b6114f46114ef6114f99261052d565b6104c4565b610140565b90565b60016115089101610140565b90565b61151a61152091939293610140565b92610140565b820180921161152b57565b610479565b61154861154e91939293611542610f3a565b5061112a565b9261112a565b9061155883610475565b9261156283610475565b61156b5f6114e0565b5b8061157f61157988610140565b91610140565b10156116105761159084829061150b565b6115a261159c84610140565b91610140565b1015611606576115bb6115b6848390610f71565b610fba565b6115e96115e36115de6115d9896115d38a889061150b565b90610f71565b610fba565b610fb1565b91610fb1565b036115fc576115f7906114fc565b61156c565b5050505050505f90565b5050505050505f90565b505050505050600190565b606090565b92919261162b61161b565b9381518061163a575b50505050565b90919294506003600282010460021b91610670604051967f4142434445464748494a4b4c4d4e4f505152535455565758595a616263646566601f5215027f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392d5f18603f5260208601938385019280826020010195600460038851945f8a525b0191603f8351818160121c16515f538181600c1c1651600153818160061c165160025316516003535f5181520190858210156117005790603f60036004939091509291926116b7565b50955f9460039360209252016040520660020490613d3d60f01b82860352151502809303520382525f808080611634565b90565b61174861174361174d92611731565b6104c4565b610106565b90565b61175990611734565b90565b6117669051610140565b90565b909594926117b4946117a36117ad9261179960809661178f60a088019c5f8901906105a9565b60208701906102e1565b60408501906102e1565b60608301906102e1565b01906102e1565b565b90565b6117cd6117c86117d2926117b6565b6104c4565b610140565b90565b5f929161184984936117e5610f3a565b5061183a6117f4610100611750565b949161180d602061180689880161175c565b960161175c565b90611825602061181e8a840161175c565b920161175c565b9161182e610064565b96879560208701611769565b60208201810382520382610446565b602081019051915afa61185a610cf5565b90806118a4575b908161186c575b5090565b61188a91506118859061187f601f6117b9565b90610f71565b610fba565b61189d6118976001611361565b91610fb1565b145f611868565b506118ae81610475565b6118c16118bb6020610f95565b91610140565b1461186156 0 0xe4f7dc8b199fdaac6693c9c412ea68aed9e1584d193e1c3478d30a6f01f26057 --rpc-url http://ethrex-dev:1729 - rex deploy --bytecode 0x6080604052346100275761001161015e565b61001961002c565b610d6c6103e98239610d6c90f35b610032565b60405190565b5f80fd5b601f801991011690565b634e487b7160e01b5f52604160045260245ffd5b9061005e90610036565b810190811060018060401b0382111761007657604052565b610040565b9061008e61008761002c565b9283610054565b565b60018060401b0381116100ac576100a8602091610036565b0190565b610040565b906100c36100be83610090565b61007b565b918252565b5f7f54657374546f6b656e0000000000000000000000000000000000000000000000910152565b6100f960096100b1565b90610106602083016100c8565b565b6101106100ef565b90565b5f7f54544f4b00000000000000000000000000000000000000000000000000000000910152565b61014460046100b1565b9061015160208301610113565b565b61015b61013a565b90565b610177610169610108565b610171610153565b906103d0565b565b5190565b634e487b7160e01b5f52602260045260245ffd5b90600160028304921680156101b1575b60208310146101ac57565b61017d565b91607f16916101a1565b5f5260205f2090565b601f602091010490565b1b90565b919060086101ed9102916101e75f19846101ce565b926101ce565b9181191691161790565b90565b90565b61021161020c610216926101f7565b6101fa565b6101f7565b90565b90565b919061023261022d61023a936101fd565b610219565b9083546101d2565b9055565b5f90565b6102549161024e61023e565b9161021c565b565b5b818110610262575050565b8061026f5f600193610242565b01610257565b9190601f8111610285575b505050565b6102916102b6936101bb565b90602061029d846101c4565b830193106102be575b6102af906101c4565b0190610256565b5f8080610280565b91506102af819290506102a6565b1c90565b906102e0905f19906008026102cc565b191690565b816102ef916102d0565b906002021790565b9061030181610179565b9060018060401b0382116103bf576103238261031d8554610191565b85610275565b602090601f831160011461035757918091610346935f9261034b575b50506102e5565b90555b565b90915001515f8061033f565b601f19831691610366856101bb565b925f5b8181106103a75750916002939185600196941061038d575b50505002019055610349565b61039d910151601f8416906102d0565b90555f8080610381565b91936020600181928787015181550195019201610369565b610040565b906103ce916102f7565b565b906103df6103e69260036103c4565b60046103c4565b56fe60806040526004361015610013575b6104e7565b61001d5f356100bc565b806306fdde03146100b7578063095ea7b3146100b257806318160ddd146100ad57806323b872dd146100a8578063313ce567146100a357806340c10f191461009e57806370a082311461009957806395d89b4114610094578063a9059cbb1461008f5763dd62ed3e0361000e576104b1565b61044e565b610419565b6103e4565b610392565b610358565b6102fa565b61028b565b610233565b61014a565b60e01c90565b60405190565b5f80fd5b5f80fd5b5f9103126100da57565b6100cc565b5190565b60209181520190565b90825f9392825e0152565b601f801991011690565b61012061012960209361012e93610117816100df565b938480936100e3565b958691016100ec565b6100f7565b0190565b6101479160208201915f818403910152610101565b90565b3461017a5761015a3660046100d0565b610176610165610644565b61016d6100c2565b91829182610132565b0390f35b6100c8565b60018060a01b031690565b6101939061017f565b90565b61019f8161018a565b036101a657565b5f80fd5b905035906101b782610196565b565b90565b6101c5816101b9565b036101cc57565b5f80fd5b905035906101dd826101bc565b565b919060408382031261020757806101fb610204925f86016101aa565b936020016101d0565b90565b6100cc565b151590565b61021a9061020c565b9052565b9190610231905f60208501940190610211565b565b346102645761026061024f6102493660046101df565b9061065e565b6102576100c2565b9182918261021e565b0390f35b6100c8565b610272906101b9565b9052565b9190610289905f60208501940190610269565b565b346102bb5761029b3660046100d0565b6102b76102a66106ad565b6102ae6100c2565b91829182610276565b0390f35b6100c8565b90916060828403126102f5576102f26102db845f85016101aa565b936102e981602086016101aa565b936040016101d0565b90565b6100cc565b3461032b576103276103166103103660046102c0565b916106c3565b61031e6100c2565b9182918261021e565b0390f35b6100c8565b60ff1690565b61033f90610330565b9052565b9190610356905f60208501940190610336565b565b34610388576103683660046100d0565b610384610373610718565b61037b6100c2565b91829182610343565b0390f35b6100c8565b5f0190565b346103c1576103ab6103a53660046101df565b9061072e565b6103b36100c2565b806103bd8161038d565b0390f35b6100c8565b906020828203126103df576103dc915f016101aa565b90565b6100cc565b34610414576104106103ff6103fa3660046103c6565b610784565b6104076100c2565b91829182610276565b0390f35b6100c8565b34610449576104293660046100d0565b6104456104346107a2565b61043c6100c2565b91829182610132565b0390f35b6100c8565b3461047f5761047b61046a6104643660046101df565b906107b8565b6104726100c2565b9182918261021e565b0390f35b6100c8565b91906040838203126104ac57806104a06104a9925f86016101aa565b936020016101aa565b90565b6100cc565b346104e2576104de6104cd6104c7366004610484565b906107f0565b6104d56100c2565b91829182610276565b0390f35b6100c8565b5f80fd5b606090565b634e487b7160e01b5f52602260045260245ffd5b9060016002830492168015610524575b602083101461051f57565b6104f0565b91607f1691610514565b60209181520190565b5f5260205f2090565b905f929180549061055a61055383610504565b809461052e565b916001811690815f146105b15750600114610575575b505050565b6105829192939450610537565b915f925b81841061059957505001905f8080610570565b60018160209295939554848601520191019290610586565b92949550505060ff19168252151560200201905f8080610570565b906105d691610540565b90565b634e487b7160e01b5f52604160045260245ffd5b906105f7906100f7565b810190811067ffffffffffffffff82111761061157604052565b6105d9565b9061063661062f926106266100c2565b938480926105cc565b03836105ed565b565b61064190610616565b90565b61064c6104eb565b506106576003610638565b90565b5f90565b61067b9161066a61065a565b5061067361081c565b919091610829565b600190565b5f90565b5f1c90565b90565b61069861069d91610684565b610689565b90565b6106aa905461068c565b90565b6106b5610680565b506106c060026106a0565b90565b916106ed926106d061065a565b506106e56106dc61081c565b82908491610886565b919091610952565b600190565b5f90565b90565b90565b61071061070b610715926106f6565b6106f9565b610330565b90565b6107206106f2565b5061072b60126106fc565b90565b90610738916109ef565b565b61074e6107496107539261017f565b6106f9565b61017f565b90565b61075f9061073a565b90565b61076b90610756565b90565b9061077890610762565b5f5260205260405f2090565b61079a61079f91610793610680565b505f61076e565b6106a0565b90565b6107aa6104eb565b506107b56004610638565b90565b6107d5916107c461065a565b506107cd61081c565b919091610952565b600190565b906107e490610762565b5f5260205260405f2090565b6108159161080b61081092610803610680565b5060016107da565b61076e565b6106a0565b90565b5f90565b610824610818565b503390565b916108379291600192610aa7565b565b6108429061018a565b9052565b60409061086f610876949695939661086560608401985f850190610839565b6020830190610269565b0190610269565b565b9061088391036101b9565b90565b9291926108948183906107f0565b90816108a96108a35f196101b9565b916101b9565b106108b6575b5050509050565b816108c96108c3876101b9565b916101b9565b106108ef576108e693946108de919392610878565b905f92610aa7565b805f80806108af565b5061090e849291925f938493637dc7a0d960e11b855260048501610846565b0390fd5b90565b61092961092461092e92610912565b6106f9565b61017f565b90565b61093a90610915565b90565b9190610950905f60208501940190610839565b565b918261096e6109686109635f610931565b61018a565b9161018a565b146109c8578161098e6109886109835f610931565b61018a565b9161018a565b146109a15761099f92919091610bfd565b565b6109c46109ad5f610931565b5f91829163ec442f0560e01b83526004830161093d565b0390fd5b6109eb6109d45f610931565b5f918291634b637e8f60e11b83526004830161093d565b0390fd5b80610a0a610a046109ff5f610931565b61018a565b9161018a565b14610a2657610a2491610a1c5f610931565b919091610bfd565b565b610a49610a325f610931565b5f91829163ec442f0560e01b83526004830161093d565b0390fd5b5f1b90565b90610a5e5f1991610a4d565b9181191691161790565b610a7c610a77610a81926101b9565b6106f9565b6101b9565b90565b90565b90610a9c610a97610aa392610a68565b610a84565b8254610a52565b9055565b909281610ac4610abe610ab95f610931565b61018a565b9161018a565b14610b8f5783610ae4610ade610ad95f610931565b61018a565b9161018a565b14610b6857610b0883610b03610afc600186906107da565b879061076e565b610a87565b610b12575b505050565b919091610b5d610b4b610b457f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92593610762565b93610762565b93610b546100c2565b91829182610276565b0390a35f8080610b0d565b610b8b610b745f610931565b5f918291634a1406b160e11b83526004830161093d565b0390fd5b610bb2610b9b5f610931565b5f91829163e602df0560e01b83526004830161093d565b0390fd5b634e487b7160e01b5f52601160045260245ffd5b610bd9610bdf919392936101b9565b926101b9565b8201809211610bea57565b610bb6565b90610bfa91016101b9565b90565b91909180610c1b610c15610c105f610931565b61018a565b9161018a565b145f14610cfc57610c3f610c3883610c3360026106a0565b610bca565b6002610a87565b5b82610c5b610c55610c505f610931565b61018a565b9161018a565b145f14610cd057610c7f610c7883610c7360026106a0565b610878565b6002610a87565b5b919091610ccb610cb9610cb37fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef93610762565b93610762565b93610cc26100c2565b91829182610276565b0390a3565b610cf782610cf1610ce25f879061076e565b91610cec836106a0565b610bef565b90610a87565b610c80565b610d0f610d0a5f839061076e565b6106a0565b80610d22610d1c856101b9565b916101b9565b10610d4a57610d35610d45918490610878565b610d405f849061076e565b610a87565b610c40565b90610d689091925f93849363391434e360e21b855260048501610846565b0390fd 0 0xe4f7dc8b199fdaac6693c9c412ea68aed9e1584d193e1c3478d30a6f01f26057 --rpc-url http://ethrex-dev:1729 - " - - blockscout_l1_function_selectors: - image: postgres:17 - depends_on: - ethrex-delegation-contracts: - condition: service_completed_successfully - command: > - sh -c " - echo inserting commitBatch signature - psql postgresql://blockscout:ceWb1MeLBEeOIfk65gU8EjF8@db:5432/blockscout_l1 < - -Options: - -n, --node URL of the node being tested. [default: http://localhost:8545] - -k, --pkeys Path to the file containing private keys. - -t, --test-type Type of test to run. Can be eth_transfers or erc20. [default: erc20] [possible values: eth-transfers, erc20, fibonacci, io-heavy] - -N, --tx-amount Number of transactions to send for each account. [default: 1000] - -w, --wait Timeout to wait for all transactions to be included. If 0 is specified, wait indefinitely. [default: 0] - -h, --help Print help -``` - -The only mandatory argument is the path to the rich account private keys file. - -## Simple run - -Before starting, consider increasing the maximum amount of open files for the current shell with the following command: - -```bash -ulimit -n 65536 -``` - -On some machines, this fixes the `ERROR axum::serve::listener: accept error: Too many open files (os error 24)` and sometimes nonce related errors. - -To run a load test, first run the node using a command like the following in the root folder: - -```bash -cargo run --bin ethrex --release -- --network fixtures/genesis/load-test.json --dev -``` - -Genesis-l2-ci has many rich accounts and does not include the prague fork, which is important for dev mode until it's fixed. - -After the node is runing, still in the repo root folder, execute the script with `make`. For example: - -```bash -# Eth transfer load test -make load-test - -# ERC 20 transfer load test -make load-test-erc20 - -# Tests a contract that executes fibonacci (high cpu) -make load-test-fibonacci - -# Tests a contract that makes heavy access to storage slots -make load-test-io -``` - -You should see the ethrex client producing blocks and logs with the gas throughput. - -To execute it with non-default parameters, you can do so with `cargo`: - -```bash -cargo run --manifest-path ./tooling/load_test/Cargo.toml -- -k ./fixtures/keys/private_keys.txt -t erc20 -N 1000 -n http://localhost:8545 -``` - -You may want to delete the dev database in between runs with - -```bash -make rm-test-db -``` - -## Getting performance metrics - -Load tests are usually used to get performance metrics. We usually want to generate flamegraphs or samply reports. - -To produce a flamegraph, run the node in the following way. - -```bash -cargo flamegraph --root --bin ethrex --release -- --network fixtures/genesis/load-test.json --dev -``` - -The "root" command is only needed for mac. It can be removed if running on linux. - -For a samply report, run the following: - -```bash -cargo b --profile release-with-debug && \ - ./target/release-with-debug/ethrex removedb --force && \ - samply record -r 10000 ./target/release-with-debug/ethrex --network fixtures/genesis/load-test.json --dev -``` - -## Interacting with reth - -The same load test can be run, the only difference is how you run the node: - -```bash -cargo run --release -- node --chain /fixtures/genesis/load-test.json --dev --dev.block-time 5000ms --http.port 8545 --txpool.max-pending-txns 100000000 --txpool.max-new-txns 1000000000 --txpool.pending-max-count 100000000 --txpool.pending-max-size 10000000000 --txpool.basefee-max-count 100000000000 --txpool.basefee-max-size 1000000000000 --txpool.queued-max-count 1000000000 -``` - -All of the txpool parameters are to make sure that it doesn't discard transactions sent by the load test. Trhoughput measurements in the logs are typically near 1Gigagas/second. To remove the database before getting measurements again: - -```bash -cargo run --release -- db --chain /fixtures/genesis/load-test.json drop -f -``` - -To get a flamegraph of its execution, run with the same parameters, just replace `cargo run --release` with `cargo flamegraph --bin reth --profiling`: - -```bash -cargo flamegraph --bin reth --root --profiling -- node --chain ~/workspace/ethrex/fixtures/genesis/load-test.json --dev --dev.block-time 5000ms --http.port 8545 --txpool.max-pending-txns 100000000 --txpool.max-new-txns 1000000000 --txpool.pending-max-count 100000000 --txpool.pending-max-size 10000000000 --txpool.basefee-max-count 100000000000 --txpool.basefee-max-size 1000000000000 --txpool.queued-max-count 1000000000 -``` - -For samply we want to directly execute the binary, so that it records the binary and not cargo itself: - -```bash -samply record ./target/profiling/reth node --chain ~/workspace/ethrex/fixtures/genesis/load-test.json --dev --dev.block-time 5000ms --http.port 8545 --txpool.max-pending-txns 100000000 --txpool.max-new-txns 1000000000 --txpool.pending-max-count 100000000 --txpool.pending-max-size 10000000000 --txpool.basefee-max-count 100000000000 --txpool.basefee-max-size 1000000000000 --txpool.queued-max-count 1000000000 -``` diff --git a/tooling/load_test/src/main.rs b/tooling/load_test/src/main.rs deleted file mode 100644 index d7d40366870..00000000000 --- a/tooling/load_test/src/main.rs +++ /dev/null @@ -1,466 +0,0 @@ -use clap::{Parser, ValueEnum}; -use ethereum_types::{Address, H160, H256, U256}; -use ethrex_blockchain::constants::TX_GAS_COST; -use ethrex_common::types::TxType; -use ethrex_l2_common::calldata::Value; -use ethrex_l2_rpc::signer::{LocalSigner, Signer}; -use ethrex_l2_sdk::{ - build_generic_tx, - calldata::{self}, - create_deploy, send_generic_transaction, wait_for_transaction_receipt, -}; -use ethrex_rpc::clients::{EthClient, EthClientError, Overrides}; -use ethrex_rpc::types::block_identifier::{BlockIdentifier, BlockTag}; -use ethrex_rpc::types::receipt::RpcReceipt; -use futures::StreamExt; -use futures::stream::FuturesUnordered; -use hex::ToHex; -use secp256k1::SecretKey; -use std::fs; -use std::path::Path; -use std::time::Duration; -use tokio::{task::JoinSet, time::sleep}; -use url::Url; - -// ERC20 compiled artifact generated from this tutorial: -// https://medium.com/@kaishinaw/erc20-using-hardhat-a-comprehensive-guide-3211efba98d4 -// If you want to modify the behaviour of the contract, edit the ERC20.sol file, -// and compile it with solc. -const ERC20: &str = - include_str!("../../../fixtures/contracts/ERC20/ERC20.bin/TestToken.bin").trim_ascii(); - -// This is the bytecode for the contract with the following functions -// version() -> always returns 2 -// function fibonacci(uint n) public pure returns (uint) -> returns the nth fib number -const FIBO_CODE: &str = "6080604052348015600e575f5ffd5b506103198061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806354fd4d501461003857806361047ff414610056575b5f5ffd5b610040610086565b60405161004d9190610152565b60405180910390f35b610070600480360381019061006b9190610199565b61008b565b60405161007d9190610152565b60405180910390f35b600281565b5f5f8210156100cf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016100c69061021e565b60405180910390fd5b5f82036100de575f9050610135565b600182036100ef5760019050610135565b5f5f90505f600190505f600290505b84811161012e575f82905083836101159190610269565b92508093505080806101269061029c565b9150506100fe565b5080925050505b919050565b5f819050919050565b61014c8161013a565b82525050565b5f6020820190506101655f830184610143565b92915050565b5f5ffd5b6101788161013a565b8114610182575f5ffd5b50565b5f813590506101938161016f565b92915050565b5f602082840312156101ae576101ad61016b565b5b5f6101bb84828501610185565b91505092915050565b5f82825260208201905092915050565b7f496e707574206d757374206265206e6f6e2d6e656761746976650000000000005f82015250565b5f610208601a836101c4565b9150610213826101d4565b602082019050919050565b5f6020820190508181035f830152610235816101fc565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6102738261013a565b915061027e8361013a565b92508282019050808211156102965761029561023c565b5b92915050565b5f6102a68261013a565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036102d8576102d761023c565b5b60018201905091905056fea264697066735822122021e2c2b56b7e23b9555cc95390dfb2979a8526595038818d133d5bb772c01a6564736f6c634300081c0033"; - -// Contract with a function that touches 100 storage slots on every transaction. -// See `fixtures/contracts/load_test/IOHeavyContract.sol` for the code. -const IO_HEAVY_CODE: &str = "6080604052348015600e575f5ffd5b505f5f90505b6064811015603e57805f8260648110602d57602c6043565b5b018190555080806001019150506014565b506070565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b6102728061007d5f395ff3fe608060405234801561000f575f5ffd5b506004361061003f575f3560e01c8063431aabc21461004357806358faa02f1461007357806362f8e72a1461007d575b5f5ffd5b61005d6004803603810190610058919061015c565b61009b565b60405161006a9190610196565b60405180910390f35b61007b6100b3565b005b61008561010a565b6040516100929190610196565b60405180910390f35b5f81606481106100a9575f80fd5b015f915090505481565b5f5f90505b60648110156101075760015f82606481106100d6576100d56101af565b5b01546100e29190610209565b5f82606481106100f5576100f46101af565b5b018190555080806001019150506100b8565b50565b5f5f5f6064811061011e5761011d6101af565b5b0154905090565b5f5ffd5b5f819050919050565b61013b81610129565b8114610145575f5ffd5b50565b5f8135905061015681610132565b92915050565b5f6020828403121561017157610170610125565b5b5f61017e84828501610148565b91505092915050565b61019081610129565b82525050565b5f6020820190506101a95f830184610187565b92915050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61021382610129565b915061021e83610129565b9250828201905080821115610236576102356101dc565b5b9291505056fea264697066735822122055f6d7149afdb56c745a203d432710eaa25a8ccdb030503fb970bf1c964ac03264736f6c634300081b0033"; - -#[derive(Parser)] -#[command(name = "load_test")] -#[command(about = "A CLI tool with a single test flag", long_about = None)] -struct Cli { - #[arg( - long, - short = 'n', - default_value = "http://localhost:8545", - env = "LOAD_TEST_RPC_URL", - help = "URL of the node being tested." - )] - node: Url, - - #[arg(long, short = 'k', help = "Path to the file containing private keys.")] - pkeys: String, - - #[arg(long, short='t', value_enum, default_value_t=TestType::Erc20, help="Type of test to run. Can be eth_transfers or erc20.")] - test_type: TestType, - - #[arg( - short = 'N', - long, - default_value_t = 1000, - env = "LOAD_TEST_TX_AMOUNT", - help = "Number of transactions to send for each account." - )] - tx_amount: u64, - - // Amount of minutes to wait before exiting. If the value is 0, the program will wait indefinitely. If not present, the program will not wait for transactions to be included in blocks. - #[arg( - long, - short = 'w', - default_value_t = 0, - help = "Timeout in minutes. If the node doesn't provide updates in this time, it's considered stuck and the load test fails. If 0 is specified, the load test will wait indefinitely." - )] - wait: u64, - - #[arg( - long, - default_value_t = false, - env = "LOAD_TEST_ENDLESS", - help = "Run the load test in an infinite loop, restarting after each round finishes." - )] - endless: bool, -} - -#[derive(ValueEnum, Clone, Debug)] // Derive ValueEnum for TestType -pub enum TestType { - EthTransfers, - Erc20, - Fibonacci, - IOHeavy, -} - -const RETRIES: u64 = 1000; -const ETH_TRANSFER_VALUE: u64 = 1000; - -// Private key for the rich account after making the initial deposits on the L2. -const RICH_ACCOUNT: &str = "0xbcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31"; - -async fn deploy_contract( - client: EthClient, - deployer: &Signer, - contract: Vec, -) -> eyre::Result
{ - let (_, contract_address) = - create_deploy(&client, deployer, contract.into(), Overrides::default()).await?; - - eyre::Ok(contract_address) -} - -async fn erc20_deploy(client: EthClient, deployer: &Signer) -> eyre::Result
{ - let erc20_bytecode = hex::decode(ERC20).expect("Failed to decode ERC20 bytecode"); - deploy_contract(client, deployer, erc20_bytecode).await -} - -async fn deploy_fibo(client: EthClient, deployer: &Signer) -> eyre::Result
{ - let fibo_bytecode = hex::decode(FIBO_CODE).expect("Failed to decode Fibo bytecode"); - deploy_contract(client, deployer, fibo_bytecode).await -} - -async fn deploy_io_heavy(client: EthClient, deployer: &Signer) -> eyre::Result
{ - let io_heavy_bytecode = hex::decode(IO_HEAVY_CODE).expect("Failed to decode IO Heavy bytecode"); - deploy_contract(client, deployer, io_heavy_bytecode).await -} - -// Given an account vector and the erc20 contract address, claim balance for all accounts. -async fn claim_erc20_balances( - contract_address: Address, - client: EthClient, - accounts: Vec, -) -> eyre::Result<()> { - let mut tasks = JoinSet::new(); - - for account in accounts { - let contract = contract_address; - let client = client.clone(); - - tasks.spawn(async move { - let claim_balance_calldata = calldata::encode_calldata("freeMint()", &[]).unwrap(); - - let claim_tx = build_generic_tx( - &client, - TxType::EIP1559, - contract, - account.address(), - claim_balance_calldata.into(), - Default::default(), - ) - .await - .unwrap(); - let tx_hash = send_generic_transaction(&client, claim_tx, &account) - .await - .unwrap(); - wait_for_transaction_receipt(tx_hash, &client, RETRIES).await - }); - } - for response in tasks.join_all().await { - match response { - Ok(RpcReceipt { receipt, .. }) if !receipt.status => { - return Err(eyre::eyre!( - "Failed to assign balance to an account, tx failed with receipt: {receipt:?}" - )); - } - Err(err) => { - return Err(eyre::eyre!( - "Failed to assign balance to an account, tx failed: {err}" - )); - } - Ok(_) => { - continue; - } - } - } - Ok(()) -} - -#[derive(Clone)] -enum TxBuilder { - Erc20(Address), - EthTransfer, - Fibonacci(Address), - IOHeavy(Address), -} - -impl TxBuilder { - // Returns value, the calldata and the destination (contract or eoa). - fn build_tx(&self) -> (Option, Vec, H160) { - let dst = H160::random(); - match self { - TxBuilder::Erc20(contract_address) => { - let send_calldata = calldata::encode_calldata( - "transfer(address,uint256)", - &[Value::Address(dst), Value::Uint(U256::one())], - ) - .unwrap(); - (None, send_calldata, *contract_address) - } - TxBuilder::EthTransfer => (Some(U256::from(ETH_TRANSFER_VALUE)), [].into(), dst), - TxBuilder::Fibonacci(contract_address) => { - let fibo_calldata = calldata::encode_calldata( - "fibonacci(uint256)", - &[Value::Uint(100000000000000_u64.into())], - ) - .unwrap(); - (None, fibo_calldata, *contract_address) - } - TxBuilder::IOHeavy(contract_address) => { - let io_heavy_calldata = - calldata::encode_calldata("incrementNumbers()", &[]).unwrap(); - (None, io_heavy_calldata, *contract_address) - } - } - } -} - -/// Sends `tx_amount` transactions per account. -/// Returns a vec of (address, start_nonce, target_nonce) tuples for use with `wait_until_all_included`. -async fn load_test( - tx_amount: u64, - accounts: &[Signer], - client: &EthClient, - chain_id: u64, - tx_builder: &TxBuilder, -) -> eyre::Result> { - let mut tasks = FuturesUnordered::new(); - for account in accounts { - let account = account.clone(); - let client = client.clone(); - let tx_builder = tx_builder.clone(); - tasks.push(async move { - let nonce = client - .get_nonce(account.address(), BlockIdentifier::Tag(BlockTag::Pending)) - .await - .unwrap(); - let src = account.address(); - let encoded_src: String = src.encode_hex(); - let target_nonce = nonce + tx_amount; - - for i in 0..tx_amount { - let (value, calldata, dst) = tx_builder.build_tx(); - let tx = build_generic_tx( - &client, - TxType::EIP1559, - dst, - src, - calldata.into(), - Overrides { - chain_id: Some(chain_id), - value, - nonce: Some(nonce + i), - max_fee_per_gas: Some(i64::MAX as u64), - max_priority_fee_per_gas: Some(10_u64), - gas_limit: Some(TX_GAS_COST * 100), - ..Default::default() - }, - ) - .await?; - let client = client.clone(); - sleep(Duration::from_micros(800)).await; - let _sent = send_generic_transaction(&client, tx, &account).await?; - } - println!("{tx_amount} transactions have been sent for {encoded_src} (nonces {nonce}..{target_nonce})",); - Ok::<(Address, u64, u64), EthClientError>((src, nonce, target_nonce)) - }); - } - - let mut targets = Vec::new(); - while let Some(result) = tasks.next().await { - targets.push(result?); - } - Ok(targets) -} - -/// Waits until the confirmed nonce of each account reaches its target. -async fn wait_until_all_included( - client: &EthClient, - timeout: Option, - targets: Vec<(Address, u64, u64)>, -) -> Result<(), String> { - for (src, start_nonce, target_nonce) in targets { - let encoded_src: String = src.encode_hex(); - let mut last_updated = tokio::time::Instant::now(); - let mut last_nonce = 0; - let total_txs = target_nonce - start_nonce; - - loop { - let nonce = client - .get_nonce(src, BlockIdentifier::Tag(BlockTag::Latest)) - .await - .unwrap(); - if nonce >= target_nonce { - println!( - "All transactions sent from {encoded_src} have been included in blocks. Nonce: {nonce}", - ); - break; - } else { - let confirmed = nonce.saturating_sub(start_nonce); - let pct = if total_txs > 0 { - (confirmed as f64 / total_txs as f64) * 100.0 - } else { - 100.0 - }; - println!( - "Waiting for transactions to be included from {encoded_src}. Nonce: {nonce}. Target: {target_nonce}. Percentage: {pct:.2}%.", - ); - } - - if let Some(timeout) = timeout { - if last_nonce == nonce { - let inactivity_time = last_updated.elapsed(); - if inactivity_time > timeout { - return Err(format!( - "Node inactive for {} seconds. Timeout reached.", - inactivity_time.as_secs() - )); - } - } else { - last_nonce = nonce; - last_updated = tokio::time::Instant::now(); - } - } - - sleep(Duration::from_secs(5)).await; - } - } - - Ok(()) -} - -fn parse_pk_file(path: &Path) -> eyre::Result> { - let pkeys_content = fs::read_to_string(path).expect("Unable to read private keys file"); - let accounts: Vec = pkeys_content - .lines() - .map(parse_private_key_into_local_signer) - .collect(); - - Ok(accounts) -} - -fn parse_private_key_into_local_signer(pkey: &str) -> Signer { - let key = pkey - .parse::() - .unwrap_or_else(|_| panic!("Private key is not a valid hex representation {pkey}")); - let secret_key = SecretKey::from_slice(key.as_bytes()) - .unwrap_or_else(|_| panic!("Invalid private key {}", pkey)); - LocalSigner::new(secret_key).into() -} - -#[tokio::main] -async fn main() { - let cli = Cli::parse(); - let pkeys_path = Path::new(&cli.pkeys); - let accounts = parse_pk_file(pkeys_path) - .unwrap_or_else(|_| panic!("Failed to parse private keys file {}", pkeys_path.display())); - let client = EthClient::new(cli.node).expect("Failed to create EthClient"); - - // We ask the client for the chain id. - let chain_id = client - .get_chain_id() - .await - .expect("Failed to get chain id") - .try_into() - .unwrap(); - - let deployer = parse_private_key_into_local_signer(RICH_ACCOUNT); - - let tx_builder = match cli.test_type { - TestType::Erc20 => { - println!("ERC20 Load test starting"); - println!("Deploying ERC20 contract..."); - let contract_address = erc20_deploy(client.clone(), &deployer) - .await - .expect("Failed to deploy ERC20 contract"); - claim_erc20_balances(contract_address, client.clone(), accounts.clone()) - .await - .expect("Failed to claim ERC20 balances"); - TxBuilder::Erc20(contract_address) - } - TestType::EthTransfers => { - println!("Eth transfer load test starting"); - TxBuilder::EthTransfer - } - TestType::Fibonacci => { - println!("Fibonacci load test starting"); - println!("Deploying Fibonacci contract..."); - let contract_address = deploy_fibo(client.clone(), &deployer) - .await - .expect("Failed to deploy Fibonacci contract"); - TxBuilder::Fibonacci(contract_address) - } - TestType::IOHeavy => { - println!("IO Heavy load test starting"); - println!("Deploying IO Heavy contract..."); - let contract_address = deploy_io_heavy(client.clone(), &deployer) - .await - .expect("Failed to deploy IO Heavy contract"); - TxBuilder::IOHeavy(contract_address) - } - }; - - let wait_time = if cli.wait > 0 { - Some(Duration::from_secs(cli.wait * 60)) - } else { - None - }; - - if cli.endless { - let mut round = 1; - loop { - if let Err(e) = run_round( - round, - cli.tx_amount, - &accounts, - &client, - chain_id, - &tx_builder, - wait_time, - ) - .await - { - eprintln!("Round {round} failed: {e}"); - } - round += 1; - } - } else { - run_round( - 1, - cli.tx_amount, - &accounts, - &client, - chain_id, - &tx_builder, - wait_time, - ) - .await - .expect("Load test failed"); - } -} - -async fn run_round( - round: u64, - tx_amount: u64, - accounts: &[Signer], - client: &EthClient, - chain_id: u64, - tx_builder: &TxBuilder, - wait_time: Option, -) -> eyre::Result<()> { - println!("Starting load test round {round} with {tx_amount} transactions per account..."); - let time_now = tokio::time::Instant::now(); - - let targets = load_test(tx_amount, accounts, client, chain_id, tx_builder).await?; - - println!("Waiting for all transactions to be included in blocks..."); - wait_until_all_included(client, wait_time, targets) - .await - .map_err(|e| eyre::eyre!(e))?; - - let elapsed_time = time_now.elapsed(); - println!( - "Load test round {round} finished. Elapsed time: {} seconds", - elapsed_time.as_secs() - ); - Ok(()) -} diff --git a/tooling/loc/Cargo.toml b/tooling/loc/Cargo.toml deleted file mode 100644 index abc9fba9b96..00000000000 --- a/tooling/loc/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "loc" -version.workspace = true -edition.workspace = true -authors.workspace = true -documentation.workspace = true -license.workspace = true - -[dependencies] -tokei = "12.1.2" -serde.workspace = true -serde_json.workspace = true -clap.workspace = true -clap_complete.workspace = true -colored = "2.1.0" -spinoff = "0.8.0" -prettytable = "0.10.0" diff --git a/tooling/loc/Makefile b/tooling/loc/Makefile deleted file mode 100644 index f840b207b96..00000000000 --- a/tooling/loc/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: loc loc-stats loc-detailed loc-compare-detailed - -loc: - cargo run - -loc-stats: - if [ "$(QUIET)" = "true" ]; then \ - cargo run --quiet -- --summary;\ - else \ - cargo run -- --summary;\ - fi - -loc-detailed: - cargo run -- --detailed - -loc-compare-detailed: - cargo run -- --compare-detailed diff --git a/tooling/loc/src/main.rs b/tooling/loc/src/main.rs deleted file mode 100644 index 9f25385a480..00000000000 --- a/tooling/loc/src/main.rs +++ /dev/null @@ -1,158 +0,0 @@ -use clap::Parser; -use report::{LinesOfCodeReport, LinesOfCodeReporterOptions, shell_summary}; -use spinoff::{Color, Spinner, spinners::Dots}; -use std::{collections::HashMap, fs::DirEntry, path::PathBuf}; -use tokei::{Config, Language, LanguageType, Languages}; - -mod report; - -fn count_crates_loc(crates_path: &PathBuf, config: &Config) -> Vec<(String, usize)> { - let nested_dirs = ["networking"]; - - let top_level_crate_dirs = std::fs::read_dir(crates_path) - .unwrap() - .filter_map(|e| e.ok()) - .filter(|e| !nested_dirs.contains(&e.file_name().to_str().unwrap())) - .collect::>(); - - let nested_crate_dirs: Vec = nested_dirs - .iter() - .flat_map(|nested_dir| { - std::fs::read_dir(crates_path.join(nested_dir)) - .unwrap() - .filter_map(|e| e.ok()) - .collect::>() - }) - .collect(); - - let mut crate_dirs = top_level_crate_dirs; - crate_dirs.extend(nested_crate_dirs); - - let mut ethrex_crates_loc: Vec<(String, usize)> = crate_dirs - .into_iter() - .filter_map(|crate_dir_entry| { - let crate_path = crate_dir_entry.path(); - - if let Some(crate_loc) = count_loc(crate_path.clone(), config) { - Some(( - crate_path.file_name().unwrap().to_str().unwrap().to_owned(), - crate_loc.code, - )) - } else { - None - } - }) - .collect(); - - ethrex_crates_loc.sort_by_key(|(_crate_name, loc)| *loc); - ethrex_crates_loc.reverse(); - ethrex_crates_loc -} - -fn count_loc(path: PathBuf, config: &Config) -> Option { - let mut languages = Languages::new(); - languages.get_statistics(&[path], &["tests", "test", "tooling", "benches"], config); - languages.get(&LanguageType::Rust).cloned() -} - -fn main() { - let opts = LinesOfCodeReporterOptions::parse(); - - let mut spinner = Spinner::new(Dots, "Counting lines of code...", Color::Cyan); - - // Find the root of the ethrex repo - let ethrex_path = std::env::var("CARGO_MANIFEST_DIR") - .map(PathBuf::from) - .map(|path| path.parent().unwrap().parent().unwrap().to_path_buf()) - .unwrap(); - let ethrex_crates_path = ethrex_path.join("crates"); - let levm_path = ethrex_crates_path.join("vm"); - let ethrex_l2_path = ethrex_crates_path.join("l2"); - - let config = Config::default(); - - let ethrex_loc = count_loc(ethrex_path.clone(), &config).unwrap(); - let levm_loc = count_loc(levm_path, &config).unwrap(); - let ethrex_l2_loc = count_loc(ethrex_l2_path, &config).unwrap(); - let mut ethrex_crates_loc = count_crates_loc(ðrex_crates_path, &config); - - // Also include cmd/ in the breakdown (counted in ethrex_l1 but outside crates/) - if let Some(cmd_loc) = count_loc(ethrex_path.join("cmd"), &config) { - ethrex_crates_loc.push(("cmd".to_owned(), cmd_loc.code)); - } - ethrex_crates_loc.sort_by(|a, b| b.1.cmp(&a.1)); - - spinner.success("Lines of code calculated!"); - - let mut spinner = Spinner::new(Dots, "Generating report...", Color::Cyan); - - let new_report = LinesOfCodeReport { - ethrex: ethrex_loc.code, - ethrex_l1: ethrex_loc.code - ethrex_l2_loc.code - levm_loc.code, - ethrex_l2: ethrex_l2_loc.code, - levm: levm_loc.code, - ethrex_crates: ethrex_crates_loc, - }; - - if opts.detailed { - let mut current_detailed_loc_report = HashMap::new(); - for report in ethrex_loc.reports { - let file_path = report.name; - // let file_name = file_path.file_name().unwrap().to_str().unwrap(); - // let dir_path = file_path.parent().unwrap(); - - current_detailed_loc_report - .entry(file_path.as_os_str().to_str().unwrap().to_owned()) - .and_modify(|e: &mut usize| *e += report.stats.code) - .or_insert_with(|| report.stats.code); - } - - std::fs::write( - "current_detailed_loc_report.json", - serde_json::to_string(¤t_detailed_loc_report).unwrap(), - ) - .expect("current_detailed_loc_report.json could not be written"); - } else if opts.compare_detailed { - let current_detailed_loc_report: HashMap = - std::fs::read_to_string("current_detailed_loc_report.json") - .map(|s| serde_json::from_str(&s).unwrap()) - .expect("current_detailed_loc_report.json could not be read"); - - let previous_detailed_loc_report: HashMap = - std::fs::read_to_string("previous_detailed_loc_report.json") - .map(|s| serde_json::from_str(&s).unwrap()) - .unwrap_or(current_detailed_loc_report.clone()); - - std::fs::write( - "detailed_loc_report.txt", - report::pr_message(previous_detailed_loc_report, current_detailed_loc_report), - ) - .unwrap(); - } else if opts.summary { - spinner.success("Report generated!"); - println!("{}", shell_summary(new_report)); - } else { - std::fs::write( - "loc_report.json", - serde_json::to_string(&new_report).unwrap(), - ) - .expect("loc_report.json could not be written"); - - let old_report: LinesOfCodeReport = std::fs::read_to_string("loc_report.json.old") - .map(|s| serde_json::from_str(&s).unwrap()) - .unwrap_or(new_report.clone()); - - std::fs::write( - "loc_report_slack.txt", - report::slack_message(old_report.clone(), new_report.clone()), - ) - .unwrap(); - std::fs::write( - "loc_report_github.txt", - report::github_step_summary(old_report, new_report), - ) - .unwrap(); - - spinner.success("Report generated!"); - } -} diff --git a/tooling/loc/src/report.rs b/tooling/loc/src/report.rs deleted file mode 100644 index 38c5a9d1551..00000000000 --- a/tooling/loc/src/report.rs +++ /dev/null @@ -1,354 +0,0 @@ -use clap::Parser; -use colored::Colorize; -use prettytable::{Table, row}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Parser)] -pub struct LinesOfCodeReporterOptions { - #[arg(short, long, value_name = "SUMMARY", default_value = "false")] - pub summary: bool, - #[arg(short, long, value_name = "DETAILED", default_value = "false")] - pub detailed: bool, - #[arg(short, long, value_name = "PR_SUMMARY", default_value = "false")] - pub compare_detailed: bool, -} - -#[derive(Default, Serialize, Deserialize, Clone)] -pub struct LinesOfCodeReport { - pub ethrex: usize, - pub ethrex_l1: usize, - pub ethrex_l2: usize, - pub levm: usize, - pub ethrex_crates: Vec<(String, usize)>, -} - -pub fn pr_message( - old_report: HashMap, - new_report: HashMap, -) -> String { - let sorted_file_paths = { - let mut keys: Vec<_> = new_report.keys().collect(); - keys.sort(); - keys - }; - - let mut table = Table::new(); - - table.add_row(row!["File", "Lines", "Diff"]); - - let mut total_lines_changed: i64 = 0; - let mut total_lines_added: i64 = 0; - let mut total_lines_removed: i64 = 0; - - for file_path in sorted_file_paths { - let current_loc = *new_report.get(file_path).unwrap() as i64; - let previous_loc = *old_report.get(file_path).unwrap_or(&0) as i64; - let loc_diff = current_loc - previous_loc; - - if loc_diff == 0 { - continue; - } - - if loc_diff > 0 { - total_lines_added += loc_diff; - } else { - total_lines_removed += loc_diff.abs(); - } - - total_lines_changed += loc_diff.abs(); - - // remove "ethrex/" and everything before it - const ETHREX_PREFIX: &str = "ethrex/"; - let file_path_printable = if let Some(idx) = file_path.find(ETHREX_PREFIX) { - &file_path[idx + ETHREX_PREFIX.len()..] - } else { - file_path - }; - - table.add_row(row![ - file_path_printable, - current_loc, - match current_loc.cmp(&previous_loc) { - std::cmp::Ordering::Greater => format!("+{loc_diff}"), - std::cmp::Ordering::Less => format!("{loc_diff}"), - std::cmp::Ordering::Equal => "-".to_owned(), - } - ]); - } - - if total_lines_changed == 0 { - return "".to_string(); - } - - let mut pr_message = String::new(); - - pr_message.push_str("

Lines of code report

\n"); - pr_message.push('\n'); - - pr_message.push_str(&pr_message_summary( - total_lines_added, - total_lines_removed, - total_lines_changed, - )); - - pr_message.push('\n'); - pr_message.push_str("
\n"); - pr_message.push_str("Detailed view\n"); - pr_message.push('\n'); - pr_message.push_str("```\n"); - pr_message.push_str(&format!("{table}\n")); - pr_message.push_str("```\n"); - pr_message.push_str("
\n"); - - pr_message -} - -fn pr_message_summary( - total_lines_added: i64, - total_lines_removed: i64, - total_lines_changed: i64, -) -> String { - let mut pr_message = String::new(); - - pr_message.push_str(&format!( - "Total lines added: `{}`\n", - match total_lines_added.cmp(&0) { - std::cmp::Ordering::Greater => format!("{total_lines_added}"), - std::cmp::Ordering::Less => - unreachable!("total_lines_added should never be less than 0"), - std::cmp::Ordering::Equal => format!("{total_lines_added}"), - } - )); - pr_message.push_str(&format!( - "Total lines removed: `{}`\n", - match total_lines_removed.cmp(&0) { - std::cmp::Ordering::Greater | std::cmp::Ordering::Equal => - format!("{total_lines_removed}"), - std::cmp::Ordering::Less => - unreachable!("total_lines_removed should never be less than 0"), - } - )); - pr_message.push_str(&format!( - "Total lines changed: `{}`\n", - match total_lines_changed.cmp(&0) { - std::cmp::Ordering::Greater | std::cmp::Ordering::Equal => - format!("{total_lines_changed}"), - std::cmp::Ordering::Less => - unreachable!("total_lines_changed should never be less than 0"), - } - )); - - pr_message -} - -pub fn slack_message(old_report: LinesOfCodeReport, new_report: LinesOfCodeReport) -> String { - let ethrex_l1_diff = new_report.ethrex_l1.abs_diff(old_report.ethrex_l1); - let ethrex_l2_diff = new_report.ethrex_l2.abs_diff(old_report.ethrex_l2); - let levm_diff = new_report.levm.abs_diff(old_report.levm); - let ethrex_diff_total = ethrex_l1_diff + ethrex_l2_diff + levm_diff; - - let ethrex_crates_mrkdwn = - new_report - .ethrex_crates - .iter() - .fold(String::new(), |acc, (crate_name, loc)| { - let old_loc = old_report - .ethrex_crates - .iter() - .find(|(old_crate_name, _)| old_crate_name == crate_name) - .map(|(_, old_loc)| old_loc) - .unwrap_or(&0); - - let loc_diff = loc.abs_diff(*old_loc); - format!( - "{}*{}*: {} {}\\n", - acc, - crate_name, - loc, - match loc.cmp(old_loc) { - std::cmp::Ordering::Greater => format!("(+{loc_diff})"), - std::cmp::Ordering::Less => format!("(-{loc_diff})"), - std::cmp::Ordering::Equal => "".to_string(), - } - ) - }); - - format!( - r#"{{ - "blocks": [ - {{ - "type": "header", - "text": {{ - "type": "plain_text", - "text": "Daily Lines of Code Report" - }} - }}, - {{ - "type": "divider" - }}, - {{ - "type": "header", - "text": {{ - "type": "plain_text", - "text": "Summary" - }} - }}, - {{ - "type": "section", - "text": {{ - "type": "mrkdwn", - "text": "*ethrex L1:* {} {}\n*ethrex L2:* {} {}\n*levm:* {} {}\n*ethrex (total):* {} {}" - }} - }}, - {{ - "type": "header", - "text": {{ - "type": "plain_text", - "text": "Crates" - }} - }}, - {{ - "type": "section", - "text": {{ - "type": "mrkdwn", - "text": "{}" - }} - }}, - {{ - "type": "context", - "elements": [ - {{ - "type": "mrkdwn", - "text": "_Excluded folders: test/, tests/, tooling/_" - }} - ] - }} - ] -}}"#, - new_report.ethrex_l1, - match new_report.ethrex_l1.cmp(&old_report.ethrex_l1) { - std::cmp::Ordering::Greater => format!("(+{ethrex_l1_diff})"), - std::cmp::Ordering::Less => format!("(-{ethrex_l1_diff})"), - std::cmp::Ordering::Equal => "".to_string(), - }, - new_report.ethrex_l2, - match new_report.ethrex_l2.cmp(&old_report.ethrex_l2) { - std::cmp::Ordering::Greater => format!("(+{ethrex_l2_diff})"), - std::cmp::Ordering::Less => format!("(-{ethrex_l2_diff})"), - std::cmp::Ordering::Equal => "".to_string(), - }, - new_report.levm, - match new_report.levm.cmp(&old_report.levm) { - std::cmp::Ordering::Greater => format!("(+{levm_diff})"), - std::cmp::Ordering::Less => format!("(-{levm_diff})"), - std::cmp::Ordering::Equal => "".to_string(), - }, - new_report.ethrex, - match new_report.ethrex.cmp(&old_report.ethrex) { - std::cmp::Ordering::Greater => format!("(+{ethrex_diff_total})"), - std::cmp::Ordering::Less => format!("(-{ethrex_diff_total})"), - std::cmp::Ordering::Equal => "".to_string(), - }, - ethrex_crates_mrkdwn - ) -} - -pub fn github_step_summary(old_report: LinesOfCodeReport, new_report: LinesOfCodeReport) -> String { - let ethrex_l1_diff = new_report.ethrex_l1.abs_diff(old_report.ethrex_l1); - let ethrex_l2_diff = new_report.ethrex_l2.abs_diff(old_report.ethrex_l2); - let levm_diff = new_report.levm.abs_diff(old_report.levm); - let ethrex_diff_total = ethrex_l1_diff + ethrex_l2_diff + levm_diff; - - let ethrex_crates_github = - new_report - .ethrex_crates - .iter() - .fold(String::new(), |acc, (crate_name, loc)| { - let old_loc = old_report - .ethrex_crates - .iter() - .find(|(old_crate_name, _)| old_crate_name == crate_name) - .map(|(_, old_loc)| old_loc) - .unwrap_or(&0); - - let loc_diff = loc.abs_diff(*old_loc); - format!( - "{}{}: {} {}\n", - acc, - crate_name, - loc, - match loc.cmp(old_loc) { - std::cmp::Ordering::Greater => format!("(+{loc_diff})"), - std::cmp::Ordering::Less => format!("(-{loc_diff})"), - std::cmp::Ordering::Equal => "".to_string(), - } - ) - }); - - format!( - r#"``` -ethrex loc summary -==================== -ethrex L1: {} {} -ethrex L2: {} {} -levm: {} {} -ethrex (total): {} {} - -ethrex crates loc -================= -{} -Excluded folders: test/, tests/, tooling/ -```"#, - new_report.ethrex_l1, - if new_report.ethrex_l1 > old_report.ethrex_l1 { - format!("(+{ethrex_l1_diff})") - } else if new_report.ethrex_l1 < old_report.ethrex_l1 { - format!("(-{ethrex_l1_diff})") - } else { - "".to_string() - }, - new_report.ethrex_l2, - if new_report.ethrex_l2 > old_report.ethrex_l2 { - format!("(+{ethrex_l2_diff})") - } else if new_report.ethrex_l2 < old_report.ethrex_l2 { - format!("(-{ethrex_l2_diff})") - } else { - "".to_string() - }, - new_report.levm, - if new_report.levm > old_report.levm { - format!("(+{levm_diff})") - } else if new_report.levm < old_report.levm { - format!("(-{levm_diff})") - } else { - "".to_string() - }, - new_report.ethrex, - if new_report.ethrex > old_report.ethrex { - format!("(+{ethrex_diff_total})") - } else if new_report.ethrex < old_report.ethrex { - format!("(-{ethrex_diff_total})") - } else { - "".to_string() - }, - ethrex_crates_github - ) -} - -pub fn shell_summary(new_report: LinesOfCodeReport) -> String { - format!( - "{}\n{}\n{} {}\n{} {}\n{} {}\n{} {}\n\n{}", - "Lines of Code".bold(), - "=============".bold(), - "ethrex L1:".bold(), - new_report.ethrex_l1, - "ethrex L2:".bold(), - new_report.ethrex_l2, - "levm:".bold(), - new_report.levm, - "ethrex (total):".bold(), - new_report.ethrex, - "Excluded folders: test/, tests/, tooling/".italic(), - ) -} diff --git a/tooling/log_analysis/.gitignore b/tooling/log_analysis/.gitignore deleted file mode 100644 index 763513e910f..00000000000 --- a/tooling/log_analysis/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.ipynb_checkpoints diff --git a/tooling/log_analysis/Makefile b/tooling/log_analysis/Makefile deleted file mode 100644 index 39549a56098..00000000000 --- a/tooling/log_analysis/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -.PHONY: all deps notebook - -all: notebook - -.venv: - uv venv - uv pip install -r requirements.txt - -deps: .venv - -notebook: deps - uv run jupyter notebook log_analysis.ipynb diff --git a/tooling/log_analysis/README.md b/tooling/log_analysis/README.md deleted file mode 100644 index 79e80a76839..00000000000 --- a/tooling/log_analysis/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Log Analysis Notebook - -This tool is a Jupyter notebook to perform bottleneck analysis based on logs from a block import. - -## Instructions - -0. Make sure you have the [uv](https://docs.astral.sh/uv/) tool installed. We use it to manage dependencies. -1. Run an import with the execution client, saving the output to a file: -```shell -cargo run --release --bin ethrex -- --network ./cmd/ethrex/networks/hoodi/genesis.json import ./hoodi-100k.rlp > import-100k.log -``` -2. Move the file to the `tooling/log_analysis` directory: -```shell -mv import-100k.log tooling/log_analysis/import-100k.log -``` -3. Start the notebook: -```shell -make notebook -``` -4. Go to the `kernel` menu and select `Restart Kernel and Run All Cells`; -5. Go to the bottom of the page, where you'll see the graphs showing participation of each step of the block import process per block. diff --git a/tooling/log_analysis/log_analysis.ipynb b/tooling/log_analysis/log_analysis.ipynb deleted file mode 100644 index d9fae3dfc8a..00000000000 --- a/tooling/log_analysis/log_analysis.ipynb +++ /dev/null @@ -1,185 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "id": "d0fe5b65-003f-4ca6-96ec-64c779230a3d", - "metadata": {}, - "outputs": [], - "source": [ - "import re\n", - "import pandas as pd\n", - "import matplotlib.pyplot as plt" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "aeef554c-9f71-40cf-bcc5-f1c3b51eaabf", - "metadata": {}, - "outputs": [], - "source": [ - "# Parses a metrics log line. Returns a struct with metrics. Returns None if this is not a metrics log.\n", - "def metrics_log(log):\n", - " pattern = re.compile(\n", - " r\"\\[METRIC\\] BLOCK EXECUTION THROUGHPUT \\(([\\d]+)\\): ([\\d.]+) Ggas/s TIME SPENT: ([\\d]+) ms. Gas Used: ([\\d.]+) \\(([\\d]+)%\\), #Txs: ([\\d]+). exec: ([\\d]+)% merkle: ([\\d]+)% store: ([\\d]+)%\"\n", - " )\n", - "\n", - " match = pattern.search(log)\n", - " if match:\n", - " return {\n", - " \"block_number\": int(match.group(1)),\n", - " \"throughput\": float(match.group(2)),\n", - " \"time_spent\": int(match.group(3)),\n", - " \"gas_used\": float(match.group(4)),\n", - " \"gas_percentage\": int(match.group(5)),\n", - " \"num_txs\": int(match.group(6)),\n", - " \"exec_pct\": int(match.group(7)),\n", - " \"merkle_pct\": int(match.group(8)),\n", - " \"store_pct\": int(match.group(9)),\n", - " }" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "28eda9ec-133c-486c-871c-eb50d47adef4", - "metadata": {}, - "outputs": [], - "source": [ - "# Parses a full log file. Returns a pandas dataframe.\n", - "def log_to_df(filename):\n", - " with open(filename) as file:\n", - " metrics = []\n", - " lines = file.readlines()\n", - " for line in lines:\n", - " metric = metrics_log(line)\n", - " if metric:\n", - " metrics.append(metric)\n", - " return pd.DataFrame(metrics)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "f104b40a-71a8-4224-9258-ce628498a98b", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_proportions(df):\n", - " plt.figure(figsize=(20, 4))\n", - " \n", - " plt.stackplot(\n", - " df['block_number'],\n", - " df['exec_pct'],\n", - " df['merkle_pct'],\n", - " df['store_pct'],\n", - " labels=['Exec %', 'Merkle %', 'Store %'],\n", - " alpha=0.8\n", - " )\n", - " \n", - " plt.xlabel('Block Number')\n", - " plt.ylabel('Percentage')\n", - " plt.title('Proportion of Exec, Merkle, and Store per Block')\n", - " plt.legend(loc='upper right')\n", - " plt.ylim(0, 100)\n", - " plt.tight_layout()\n", - " plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "616b0f2f-8234-40cf-9bc2-5def962950ae", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_time_spent(df):\n", - " plt.figure(figsize=(20, 4))\n", - " \n", - " plt.stackplot(\n", - " df['block_number'],\n", - " df['exec_pct']*df['time_spent']/100,\n", - " df['merkle_pct']*df['time_spent']/100,\n", - " df['store_pct']*df['time_spent']/100,\n", - " labels=['Exec %', 'Merkle %', 'Store %'],\n", - " alpha=0.8\n", - " )\n", - " \n", - " plt.xlabel('Block Number')\n", - " plt.ylabel('Time spent (ms)')\n", - " plt.title('Time spent per block, divided by exec, merkle and storage')\n", - " plt.legend(loc='upper right')\n", - " plt.ylim(0, 170)\n", - " plt.tight_layout()\n", - " plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "cbe6f87b-22b5-4b05-a184-94d286cdbe64", - "metadata": {}, - "outputs": [], - "source": [ - "def plot_throughput(df):\n", - " plt.figure(figsize=(20, 4))\n", - " plt.plot(df['block_number'], df['throughput'], label='Throughput (Ggas/s)')\n", - " \n", - " plt.xlabel('Block Number')\n", - " plt.ylabel('Throughput (Ggas/s)')\n", - "\n", - " # We limit the y axis to prevent outliers from making normal values look too small\n", - " plt.ylim(0,0.6)\n", - " plt.title('Block Throughput per Block Number')\n", - " plt.legend()\n", - " plt.grid(True)\n", - " plt.tight_layout()\n", - " plt.show()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "36a4b631-edd7-439c-8bee-6bcbbb5e600b", - "metadata": {}, - "outputs": [], - "source": [ - "df = log_to_df(\"import-100k.log\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9bb86c04-2a79-445c-9017-ebcca1004ec0", - "metadata": {}, - "outputs": [], - "source": [ - "plot_throughput(df)\n", - "plot_time_spent(df)\n", - "plot_proportions(df)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.13.5" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/tooling/log_analysis/requirements.txt b/tooling/log_analysis/requirements.txt deleted file mode 100644 index 4d7351de5fb..00000000000 --- a/tooling/log_analysis/requirements.txt +++ /dev/null @@ -1,109 +0,0 @@ -anyio==4.9.0 -appnope==0.1.4 -argon2-cffi==25.1.0 -argon2-cffi-bindings==21.2.0 -arrow==1.3.0 -asttokens==3.0.0 -async-lru==2.0.5 -attrs==25.3.0 -babel==2.17.0 -beautifulsoup4==4.13.4 -bleach==6.2.0 -certifi==2025.7.9 -cffi==1.17.1 -charset-normalizer==3.4.2 -comm==0.2.2 -contourpy==1.3.2 -cycler==0.12.1 -debugpy==1.8.14 -decorator==5.2.1 -defusedxml==0.7.1 -executing==2.2.0 -fastjsonschema==2.21.1 -fonttools==4.58.5 -fqdn==1.5.1 -h11==0.16.0 -httpcore==1.0.9 -httpx==0.28.1 -idna==3.10 -ipykernel==6.29.5 -ipython==9.4.0 -ipython-pygments-lexers==1.1.1 -ipywidgets==8.1.7 -isoduration==20.11.0 -jedi==0.19.2 -jinja2==3.1.6 -json5==0.12.0 -jsonpointer==3.0.0 -jsonschema==4.24.0 -jsonschema-specifications==2025.4.1 -jupyter==1.1.1 -jupyter-client==8.6.3 -jupyter-console==6.6.3 -jupyter-core==5.8.1 -jupyter-events==0.12.0 -jupyter-lsp==2.2.5 -jupyter-server==2.16.0 -jupyter-server-terminals==0.5.3 -jupyterlab==4.4.4 -jupyterlab-pygments==0.3.0 -jupyterlab-server==2.27.3 -jupyterlab-widgets==3.0.15 -kiwisolver==1.4.8 -markupsafe==3.0.2 -matplotlib==3.10.3 -matplotlib-inline==0.1.7 -mistune==3.1.3 -nbclient==0.10.2 -nbconvert==7.16.6 -nbformat==5.10.4 -nest-asyncio==1.6.0 -notebook==7.4.4 -notebook-shim==0.2.4 -numpy==2.3.1 -overrides==7.7.0 -packaging==25.0 -pandas==2.3.1 -pandocfilters==1.5.1 -parso==0.8.4 -pexpect==4.9.0 -pillow==11.3.0 -platformdirs==4.3.8 -prometheus-client==0.22.1 -prompt-toolkit==3.0.51 -psutil==7.0.0 -ptyprocess==0.7.0 -pure-eval==0.2.3 -pycparser==2.22 -pygments==2.19.2 -pyparsing==3.2.3 -python-dateutil==2.9.0.post0 -python-json-logger==3.3.0 -pytz==2025.2 -pyyaml==6.0.2 -pyzmq==27.0.0 -referencing==0.36.2 -requests==2.32.4 -rfc3339-validator==0.1.4 -rfc3986-validator==0.1.1 -rpds-py==0.26.0 -send2trash==1.8.3 -setuptools==80.9.0 -six==1.17.0 -sniffio==1.3.1 -soupsieve==2.7 -stack-data==0.6.3 -terminado==0.18.1 -tinycss2==1.4.0 -tornado==6.5.1 -traitlets==5.14.3 -types-python-dateutil==2.9.0.20250708 -typing-extensions==4.14.1 -tzdata==2025.2 -uri-template==1.3.0 -urllib3==2.5.0 -wcwidth==0.2.13 -webcolors==24.11.1 -webencodings==0.5.1 -websocket-client==1.8.0 -widgetsnbextension==4.0.14 diff --git a/tooling/migrations/Cargo.toml b/tooling/migrations/Cargo.toml deleted file mode 100644 index f2492698bb5..00000000000 --- a/tooling/migrations/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "migrations" -version.workspace = true -edition.workspace = true -authors.workspace = true -documentation.workspace = true -license.workspace = true - -[dependencies] -clap.workspace = true -ethrex-blockchain.workspace = true -ethrex-common.workspace = true -ethrex-common-libmdbx = { git = "https://github.com/lambdaclass/ethrex", tag = "v1.0.0", package = "ethrex-common" } -ethrex-storage-libmdbx = { features = [ - "libmdbx", -], git = "https://github.com/lambdaclass/ethrex", tag = "v1.0.0", package = "ethrex-storage" } -ethrex-storage = { features = ["rocksdb"], workspace = true } -tokio = { features = ["full"], workspace = true } diff --git a/tooling/migrations/README.md b/tooling/migrations/README.md deleted file mode 100644 index b7bf314b188..00000000000 --- a/tooling/migrations/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Ethrex migration tools - -This tool provides a way to migrate ethrex databases created with Libmdbx to RocksDB. - -## Instructions - -> [!IMPORTANT] -> If you are migrating a db from an ethrex L2 rollup you should also copy the `rollup_store`, `rollup_store-shm` and `rollup_store-wal` files to ``. - -It is recomended to backup the original database before migration even if this process does not erase the old data. To migrate a database run: - -``` -cargo run --release -- l2r --genesis --store.old --store.new -``` - -This will output the migrated database to ``. -Finally restart your ethrex node pointing `--datadir` to the path of the migrated database - -## CLI Reference - -``` -Migrate a libmdbx database to rocksdb - -Usage: migrations libmdbx2rocksdb --genesis --store.old --store.new - -Options: - --genesis Path to the genesis file for the genesis block of store.old - --store.old Path to the target database to migrate - --store.new Path to use for the migrated database - -h, --help Print help -``` diff --git a/tooling/migrations/src/cli.rs b/tooling/migrations/src/cli.rs deleted file mode 100644 index 4993be20606..00000000000 --- a/tooling/migrations/src/cli.rs +++ /dev/null @@ -1,144 +0,0 @@ -use std::path::{Path, PathBuf}; - -use clap::{Parser as ClapParser, Subcommand as ClapSubcommand}; -use ethrex_blockchain::{Blockchain, BlockchainOptions, BlockchainType, L2Config}; -use ethrex_common::types::Block; - -use crate::utils::{migrate_block_body, migrate_block_header}; - -#[allow(clippy::upper_case_acronyms)] -#[derive(ClapParser)] -#[command( - name = "migrations", - author = "Lambdaclass", - about = "ethrex migration tools" -)] -pub struct CLI { - #[command(subcommand)] - pub command: Subcommand, -} - -#[derive(ClapSubcommand)] -pub enum Subcommand { - #[command( - name = "libmdbx2rocksdb", - visible_alias = "l2r", - about = "Migrate a libmdbx database to rocksdb" - )] - Libmdbx2Rocksdb { - #[arg(long = "genesis")] - /// Path to the genesis file for the old database - genesis_path: PathBuf, - #[arg(long = "store.old")] - /// Path to the target Libmbdx database to migrate - old_storage_path: PathBuf, - #[arg(long = "store.new")] - /// Path for the new RocksDB database - new_storage_path: PathBuf, - }, -} - -impl Subcommand { - pub async fn run(&self) { - match self { - Self::Libmdbx2Rocksdb { - genesis_path, - old_storage_path, - new_storage_path, - } => migrate_libmdbx_to_rocksdb(genesis_path, old_storage_path, new_storage_path).await, - } - } -} - -async fn migrate_libmdbx_to_rocksdb( - genesis_path: &Path, - old_storage_path: &Path, - new_storage_path: &Path, -) { - let old_store = ethrex_storage_libmdbx::Store::new( - old_storage_path.to_str().expect("Invalid old storage path"), - ethrex_storage_libmdbx::EngineType::Libmdbx, - ) - .expect("Cannot open libmdbx store"); - old_store - .load_initial_state() - .await - .expect("Cannot load libmdbx store state"); - - let new_store = ethrex_storage::Store::new_from_genesis( - new_storage_path, - ethrex_storage::EngineType::RocksDB, - genesis_path - .to_str() - .expect("Cannot convert genesis path to str"), - ) - .await - .expect("Cannot create rocksdb store"); - - let last_block_number = old_store - .get_latest_block_number() - .await - .expect("Cannot get latest block from libmdbx store"); - let last_known_block = new_store - .get_latest_block_number() - .await - .expect("Cannot get latest known block from rocksdb store"); - - if last_known_block >= last_block_number { - println!("Rocksdb store is already up to date"); - return; - } - - println!("Migrating from block {last_known_block} to {last_block_number}"); - - let blockchain_opts = BlockchainOptions { - // TODO: we may want to migrate using a specified fee config - r#type: BlockchainType::L2(L2Config::default()), - ..Default::default() - }; - let blockchain = Blockchain::new(new_store.clone(), blockchain_opts); - - let block_bodies = old_store - .get_block_bodies(last_known_block + 1, last_block_number) - .await - .expect("Cannot get bodies from libmdbx store"); - - let block_headers = (last_known_block + 1..=last_block_number).map(|i| { - old_store - .get_block_header(i) - .ok() - .flatten() - .expect("Cannot get block headers from libmdbx store") - }); - - let blocks = block_headers.zip(block_bodies); - let mut added_blocks = Vec::new(); - for (header, body) in blocks { - let header = migrate_block_header(header); - let body = migrate_block_body(body); - let block_number = header.number; - let block = Block::new(header, body); - - let block_hash = block.hash(); - blockchain - .add_block_pipeline(block, None) - .unwrap_or_else(|e| panic!("Cannot add block {block_number} to rocksdb store: {e}")); - added_blocks.push((block_number, block_hash)); - } - - let last_block = old_store - .get_block_header(last_block_number) - .ok() - .flatten() - .expect("Cannot get last block from libmdbx store"); - new_store - .forkchoice_update( - added_blocks, - last_block.number, - last_block.hash(), - None, - None, - ) - .await - .expect("Cannot apply forkchoice update"); -} diff --git a/tooling/migrations/src/main.rs b/tooling/migrations/src/main.rs deleted file mode 100644 index 63e44cdde8e..00000000000 --- a/tooling/migrations/src/main.rs +++ /dev/null @@ -1,12 +0,0 @@ -mod cli; -mod utils; - -use crate::cli::CLI; -use clap::Parser; - -#[tokio::main] -async fn main() { - let CLI { command } = CLI::parse(); - - command.run().await; -} diff --git a/tooling/migrations/src/utils.rs b/tooling/migrations/src/utils.rs deleted file mode 100644 index 389a49412ae..00000000000 --- a/tooling/migrations/src/utils.rs +++ /dev/null @@ -1,198 +0,0 @@ -use ethrex_common::types::{ - AuthorizationTuple, BlockBody, BlockHeader, EIP1559Transaction, EIP2930Transaction, - EIP4844Transaction, EIP7702Transaction, LegacyTransaction, PrivilegedL2Transaction, - Transaction, TxKind, Withdrawal, -}; -use ethrex_common_libmdbx::types::{ - BlockBody as LibmdbxBlockBody, BlockHeader as LibmdbxBlockHeader, - Transaction as LibmdbxTransaction, TxKind as LibmdbxTxKind, -}; - -pub fn migrate_block_header(header: LibmdbxBlockHeader) -> BlockHeader { - BlockHeader { - hash: header.hash, - parent_hash: header.parent_hash, - ommers_hash: header.ommers_hash, - coinbase: header.coinbase, - state_root: header.state_root, - transactions_root: header.transactions_root, - receipts_root: header.receipts_root, - logs_bloom: header.logs_bloom, - difficulty: header.difficulty, - number: header.number, - gas_limit: header.gas_limit, - gas_used: header.gas_used, - timestamp: header.timestamp, - extra_data: header.extra_data, - prev_randao: header.prev_randao, - nonce: header.nonce, - base_fee_per_gas: header.base_fee_per_gas, - withdrawals_root: header.withdrawals_root, - blob_gas_used: header.blob_gas_used, - excess_blob_gas: header.excess_blob_gas, - parent_beacon_block_root: header.parent_beacon_block_root, - requests_hash: header.requests_hash, - block_access_list_hash: None, - slot_number: None, - } -} - -pub fn migrate_block_body(body: LibmdbxBlockBody) -> BlockBody { - BlockBody { - transactions: body - .transactions - .into_iter() - .map(migrate_transaction) - .collect(), - ommers: body.ommers.into_iter().map(migrate_block_header).collect(), - withdrawals: body.withdrawals.map(|withdrawals| { - withdrawals - .iter() - .map(|withdrawal| Withdrawal { - index: withdrawal.index, - validator_index: withdrawal.validator_index, - address: withdrawal.address, - amount: withdrawal.amount, - }) - .collect() - }), - } -} - -pub fn migrate_transaction(tx: LibmdbxTransaction) -> Transaction { - match tx { - LibmdbxTransaction::EIP1559Transaction(tx) => { - Transaction::EIP1559Transaction(EIP1559Transaction { - chain_id: tx.chain_id, - nonce: tx.nonce, - max_priority_fee_per_gas: tx.max_priority_fee_per_gas, - max_fee_per_gas: tx.max_fee_per_gas, - gas_limit: tx.gas_limit, - to: match tx.to { - LibmdbxTxKind::Create => TxKind::Create, - LibmdbxTxKind::Call(to) => TxKind::Call(to), - }, - value: tx.value, - data: tx.data, - access_list: tx.access_list, - signature_y_parity: tx.signature_y_parity, - signature_r: tx.signature_r, - signature_s: tx.signature_s, - inner_hash: tx.inner_hash, - sender_cache: Default::default(), - cached_canonical: Default::default(), - }) - } - LibmdbxTransaction::LegacyTransaction(tx) => { - Transaction::LegacyTransaction(LegacyTransaction { - nonce: tx.nonce, - gas_price: tx.gas_price.into(), - gas: tx.gas, - to: match tx.to { - LibmdbxTxKind::Create => TxKind::Create, - LibmdbxTxKind::Call(to) => TxKind::Call(to), - }, - value: tx.value, - data: tx.data, - v: tx.v, - r: tx.r, - s: tx.s, - inner_hash: tx.inner_hash, - sender_cache: Default::default(), - }) - } - LibmdbxTransaction::EIP2930Transaction(tx) => { - Transaction::EIP2930Transaction(EIP2930Transaction { - chain_id: tx.chain_id, - nonce: tx.nonce, - gas_price: tx.gas_price.into(), - gas_limit: tx.gas_limit, - to: match tx.to { - LibmdbxTxKind::Create => TxKind::Create, - LibmdbxTxKind::Call(to) => TxKind::Call(to), - }, - value: tx.value, - data: tx.data, - access_list: tx.access_list, - signature_y_parity: tx.signature_y_parity, - signature_r: tx.signature_r, - signature_s: tx.signature_s, - inner_hash: tx.inner_hash, - sender_cache: Default::default(), - cached_canonical: Default::default(), - }) - } - LibmdbxTransaction::EIP4844Transaction(tx) => { - Transaction::EIP4844Transaction(EIP4844Transaction { - chain_id: tx.chain_id, - nonce: tx.nonce, - max_priority_fee_per_gas: tx.max_priority_fee_per_gas, - max_fee_per_gas: tx.max_fee_per_gas, - gas: tx.gas, - to: tx.to, - value: tx.value, - data: tx.data, - access_list: tx.access_list, - max_fee_per_blob_gas: tx.max_fee_per_blob_gas, - blob_versioned_hashes: tx.blob_versioned_hashes, - signature_y_parity: tx.signature_y_parity, - signature_r: tx.signature_r, - signature_s: tx.signature_s, - inner_hash: tx.inner_hash, - sender_cache: Default::default(), - cached_canonical: Default::default(), - }) - } - LibmdbxTransaction::EIP7702Transaction(tx) => { - Transaction::EIP7702Transaction(EIP7702Transaction { - chain_id: tx.chain_id, - nonce: tx.nonce, - max_priority_fee_per_gas: tx.max_priority_fee_per_gas, - max_fee_per_gas: tx.max_fee_per_gas, - gas_limit: tx.gas_limit, - to: tx.to, - value: tx.value, - data: tx.data, - access_list: tx.access_list, - authorization_list: tx - .authorization_list - .iter() - .map(|auth| AuthorizationTuple { - chain_id: auth.chain_id, - address: auth.address, - nonce: auth.nonce, - y_parity: auth.y_parity, - r_signature: auth.r_signature, - s_signature: auth.s_signature, - }) - .collect(), - signature_y_parity: tx.signature_y_parity, - signature_r: tx.signature_r, - signature_s: tx.signature_s, - inner_hash: tx.inner_hash, - sender_cache: Default::default(), - cached_canonical: Default::default(), - }) - } - LibmdbxTransaction::PrivilegedL2Transaction(tx) => { - Transaction::PrivilegedL2Transaction(PrivilegedL2Transaction { - chain_id: tx.chain_id, - nonce: tx.nonce, - max_priority_fee_per_gas: tx.max_priority_fee_per_gas, - max_fee_per_gas: tx.max_fee_per_gas, - gas_limit: tx.gas_limit, - to: match tx.to { - LibmdbxTxKind::Create => TxKind::Create, - LibmdbxTxKind::Call(to) => TxKind::Call(to), - }, - value: tx.value, - data: tx.data, - access_list: tx.access_list, - from: tx.from, - inner_hash: tx.inner_hash, - sender_cache: Default::default(), - cached_canonical: Default::default(), - }) - } - } -} diff --git a/tooling/monitor/Cargo.toml b/tooling/monitor/Cargo.toml deleted file mode 100644 index 7a2b7b6cb59..00000000000 --- a/tooling/monitor/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -name = "ethrex-monitor" -version.workspace = true -edition.workspace = true -authors.workspace = true -documentation.workspace = true -license.workspace = true - -[dependencies] -ethrex-common.workspace = true -ethrex-config = { path = "../../crates/common/config" } -ethrex-l2-common.workspace = true -ethrex-sdk = { path = "../../crates/l2/sdk" } -ethrex-rlp = { path = "../../crates/common/rlp" } -ethrex-rpc.workspace = true -ethrex-storage.workspace = true -ethrex-storage-rollup.workspace = true - -bytes.workspace = true -chrono = "0.4.41" -crossterm = { version = "0.29.0", features = ["event-stream"] } -futures.workspace = true -hex.workspace = true -ratatui = "0.29.0" -reqwest.workspace = true -secp256k1.workspace = true -serde.workspace = true -spawned-concurrency.workspace = true -thiserror.workspace = true -tokio.workspace = true -tokio-util.workspace = true -tracing.workspace = true -tui-logger.workspace = true - -[lib] -name = "ethrex_monitor" -path = "src/lib.rs" - -[lints.clippy] -unwrap_used = "deny" -expect_used = "deny" -indexing_slicing = "deny" -as_conversions = "deny" -unnecessary_cast = "warn" -panic = "deny" -redundant_clone = "warn" diff --git a/tooling/monitor/src/app.rs b/tooling/monitor/src/app.rs deleted file mode 100644 index 491ff62027a..00000000000 --- a/tooling/monitor/src/app.rs +++ /dev/null @@ -1,579 +0,0 @@ -use crossterm::{ - event::{DisableMouseCapture, EnableMouseCapture, Event, EventStream, KeyCode, MouseEventKind}, - execute, - terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode}, -}; -use ethrex_l2_common::sequencer_state::SequencerState; -use ethrex_rpc::EthClient; -use ethrex_storage::Store; -use ethrex_storage_rollup::StoreRollup; -use futures::StreamExt; -use ratatui::buffer::Buffer; -use ratatui::layout::{Constraint, Layout, Rect}; -use ratatui::style::{Color, Modifier, Style}; -use ratatui::text::{Line, Span}; -use ratatui::widgets::{Block, Paragraph, StatefulWidget, Tabs, Widget}; -use ratatui::{ - Terminal, - backend::{Backend, CrosstermBackend}, -}; -use reqwest::Url; -use spawned_concurrency::{ - actor, - error::ActorError, - protocol, - tasks::{Actor, ActorStart as _, Context, Handler, send_after, spawn_listener}, -}; -use std::io; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use tokio::sync::Mutex; -use tui_logger::{TuiLoggerLevelOutput, TuiLoggerSmartWidget, TuiWidgetEvent, TuiWidgetState}; - -use crate::MonitorConfig; -use crate::error::MonitorError; -use crate::utils::SelectableScroller; -use crate::widget::rich_accounts::RichAccountsTable; -use crate::widget::{ - BatchesTable, BlocksTable, GlobalChainStatusTable, L1ToL2MessagesTable, L2ToL1MessagesTable, - MempoolTable, NodeStatusTable, tabs::TabsState, -}; -use crate::widget::{ETHREX_LOGO, LATEST_BLOCK_STATUS_TABLE_LENGTH_IN_DIGITS}; -use tokio_util::sync::CancellationToken; -use tracing::{error, info}; - -const SCROLL_DEBOUNCE_DURATION: Duration = Duration::from_millis(700); // 700ms - -const SCROLLABLE_WIDGETS: usize = 5; -pub struct EthrexMonitorWidget { - pub title: String, - pub should_quit: bool, - pub tabs: TabsState, - pub tick_rate: u64, - pub batch_widget_height: Option, - - pub logger: TuiWidgetState, - pub node_status: NodeStatusTable, - pub global_chain_status: GlobalChainStatusTable, - pub mempool: MempoolTable, - pub batches_table: BatchesTable, - pub blocks_table: BlocksTable, - pub l1_to_l2_messages: L1ToL2MessagesTable, - pub l2_to_l1_messages: L2ToL1MessagesTable, - pub rich_accounts: RichAccountsTable, - - pub eth_client: EthClient, - pub rollup_client: EthClient, - pub store: Store, - pub rollup_store: StoreRollup, - pub last_scroll: Instant, - pub overview_selected_widget: usize, - - pub osaka_activation_time: Option, - pub mouse_captured: bool, -} - -#[protocol] -pub trait MonitorProtocol: Send + Sync { - fn tick(&self) -> Result<(), ActorError>; - fn event(&self, event: Event) -> Result<(), ActorError>; -} - -pub struct EthrexMonitor { - widget: EthrexMonitorWidget, - terminal: Arc>>>, - cancellation_token: CancellationToken, -} - -#[actor(protocol = MonitorProtocol)] -impl EthrexMonitor { - pub async fn spawn( - sequencer_state: SequencerState, - store: Store, - rollup_store: StoreRollup, - cfg: &MonitorConfig, - cancellation_token: CancellationToken, - ) -> Result<(), MonitorError> { - let widget = EthrexMonitorWidget::new(sequencer_state, store, rollup_store, cfg).await?; - let monitor = EthrexMonitor { - widget, - terminal: Arc::new(Mutex::new(setup_terminal()?)), - cancellation_token, - }; - monitor.start(); - Ok(()) - } - - #[started] - async fn started(&mut self, ctx: &Context) { - // Use send_after (not send_interval) so the next tick is only - // scheduled after the current one finishes. This prevents tick - // backlog from blocking keyboard events when on_tick() is slow. - send_after( - Duration::from_millis(self.widget.tick_rate), - ctx.clone(), - monitor_protocol::Tick, - ); - spawn_listener( - ctx.clone(), - EventStream::new().filter_map(|result| async move { - result.ok().map(|e| monitor_protocol::Event { event: e }) - }), - ); - } - - #[stopped] - async fn stopped(&mut self, _ctx: &Context) { - let mut terminal = self.terminal.lock().await; - let _ = restore_terminal(&mut terminal) - .inspect_err(|err| error!("Error restoring terminal: {err}")); - info!("Monitor has been cancelled"); - self.cancellation_token.cancel(); - } - - #[send_handler] - async fn handle_tick(&mut self, _msg: monitor_protocol::Tick, ctx: &Context) { - let _ = self - .widget - .on_tick() - .await - .inspect_err(|err| error!("Monitor error: {err}")); - - if self.widget.should_quit { - ctx.stop(); - return; - } - let _ = self - .widget - .draw(&mut *self.terminal.lock().await) - .inspect_err(|err| error!("Render error: {err}")); - // Schedule next tick only after this one finishes - send_after( - Duration::from_millis(self.widget.tick_rate), - ctx.clone(), - monitor_protocol::Tick, - ); - } - - #[send_handler] - async fn handle_event(&mut self, msg: monitor_protocol::Event, ctx: &Context) { - if let Some(key) = msg.event.as_key_press_event() { - self.widget.on_key_event(key.code); - } - if let Some(mouse) = msg.event.as_mouse_event() { - self.widget.on_mouse_event(mouse.kind); - } - - if self.widget.should_quit { - ctx.stop(); - return; - } - let _ = self - .widget - .draw(&mut *self.terminal.lock().await) - .inspect_err(|err| error!("Render error: {err}")); - } -} - -impl EthrexMonitorWidget { - pub async fn new( - sequencer_state: SequencerState, - store: Store, - rollup_store: StoreRollup, - cfg: &MonitorConfig, - ) -> Result { - let eth_client = EthClient::new( - cfg.rpc_urls - .first() - .ok_or(MonitorError::RPCListEmpty)? - .clone(), - ) - .map_err(MonitorError::EthClientError)?; - // TODO: De-hardcode the rollup client URL - #[allow(clippy::expect_used)] - let rollup_client = EthClient::new( - Url::parse("http://localhost:1729").expect("Unreachable error. URL is hardcoded"), - ) - .map_err(MonitorError::EthClientError)?; - - let mut monitor_widget = EthrexMonitorWidget { - title: if cfg.is_based { - "Based Ethrex Monitor".to_string() - } else { - "Ethrex Monitor".to_string() - }, - should_quit: false, - tabs: TabsState::default(), - tick_rate: cfg.tick_rate, - batch_widget_height: cfg.batch_widget_height, - global_chain_status: GlobalChainStatusTable::new(cfg), - logger: TuiWidgetState::new().set_default_display_level(tui_logger::LevelFilter::Info), - node_status: NodeStatusTable::new(sequencer_state.clone(), cfg.is_based), - mempool: MempoolTable::new(), - batches_table: BatchesTable::new(cfg.on_chain_proposer_address), - blocks_table: BlocksTable::new(), - l1_to_l2_messages: L1ToL2MessagesTable::new(cfg.bridge_address), - l2_to_l1_messages: L2ToL1MessagesTable::new(cfg.bridge_address), - rich_accounts: RichAccountsTable::new(&rollup_client).await?, - eth_client, - rollup_client, - store, - rollup_store, - last_scroll: Instant::now(), - overview_selected_widget: 0, - osaka_activation_time: cfg.osaka_activation_time, - mouse_captured: true, - }; - monitor_widget.selected_table().selected(true); - monitor_widget.on_tick().await?; - Ok(monitor_widget) - } - - fn draw(&mut self, terminal: &mut Terminal) -> Result<(), MonitorError> { - terminal.draw(|frame| { - frame.render_widget(self, frame.area()); - })?; - Ok(()) - } - - fn selected_table(&mut self) -> &mut dyn SelectableScroller { - let widgets: [&mut dyn SelectableScroller; SCROLLABLE_WIDGETS] = [ - &mut self.batches_table, - &mut self.blocks_table, - &mut self.mempool, - &mut self.l1_to_l2_messages, - &mut self.l2_to_l1_messages, - ]; - // index always within bounds - #[expect(clippy::indexing_slicing)] - widgets[self.overview_selected_widget % SCROLLABLE_WIDGETS] - } - - pub fn on_key_event(&mut self, code: KeyCode) { - match (&self.tabs, code) { - (TabsState::Logs, KeyCode::Left) => self.logger.transition(TuiWidgetEvent::LeftKey), - (TabsState::Logs, KeyCode::Down) => self.logger.transition(TuiWidgetEvent::DownKey), - (TabsState::Logs, KeyCode::Up) => self.logger.transition(TuiWidgetEvent::UpKey), - (TabsState::Logs, KeyCode::Right) => self.logger.transition(TuiWidgetEvent::RightKey), - (TabsState::Logs, KeyCode::Char('h')) => { - self.logger.transition(TuiWidgetEvent::HideKey) - } - (TabsState::Logs, KeyCode::Char('f')) => { - self.logger.transition(TuiWidgetEvent::FocusKey) - } - (TabsState::Logs, KeyCode::Char('+')) => { - self.logger.transition(TuiWidgetEvent::PlusKey) - } - (TabsState::Logs, KeyCode::Char('-')) => { - self.logger.transition(TuiWidgetEvent::MinusKey) - } - (TabsState::Overview, KeyCode::Up) => { - self.selected_table().selected(false); - self.overview_selected_widget = self - .overview_selected_widget - .wrapping_add(SCROLLABLE_WIDGETS - 1) - % SCROLLABLE_WIDGETS; - self.selected_table().selected(true); - } - (TabsState::Overview, KeyCode::Down) => { - self.selected_table().selected(false); - self.overview_selected_widget = - self.overview_selected_widget.wrapping_add(1) % SCROLLABLE_WIDGETS; - self.selected_table().selected(true); - } - (TabsState::Overview, KeyCode::Char('w')) => { - self.selected_table().scroll_up(); - } - (TabsState::Overview, KeyCode::Char('s')) => { - self.selected_table().scroll_down(); - } - ( - TabsState::Overview | TabsState::Logs | TabsState::RichAccounts, - KeyCode::Char('Q'), - ) => self.should_quit = true, - ( - TabsState::Overview | TabsState::Logs | TabsState::RichAccounts, - KeyCode::Char('m'), - ) => { - let new_state = !self.mouse_captured; - let result = if new_state { - execute!(io::stdout(), EnableMouseCapture) - } else { - execute!(io::stdout(), DisableMouseCapture) - }; - if result.is_ok() { - self.mouse_captured = new_state; - } - } - (TabsState::Overview | TabsState::Logs | TabsState::RichAccounts, KeyCode::Tab) => { - self.tabs.next() - } - (TabsState::RichAccounts, KeyCode::Char('w')) => { - self.rich_accounts.scroll_up(); - } - (TabsState::RichAccounts, KeyCode::Char('s')) => { - self.rich_accounts.scroll_down(); - } - _ => {} - } - } - - pub fn on_mouse_event(&mut self, kind: MouseEventKind) { - let now = Instant::now(); - if now.duration_since(self.last_scroll) < SCROLL_DEBOUNCE_DURATION { - return; // Ignore the scroll — too soon - } - - self.last_scroll = now; - - match (&self.tabs, kind) { - (TabsState::Logs, MouseEventKind::ScrollDown) => { - self.logger.transition(TuiWidgetEvent::NextPageKey) - } - (TabsState::Logs, MouseEventKind::ScrollUp) => { - self.logger.transition(TuiWidgetEvent::PrevPageKey) - } - _ => {} - } - } - - pub async fn on_tick(&mut self) -> Result<(), MonitorError> { - self.node_status - .on_tick(&self.store, &self.rollup_client) - .await?; - self.global_chain_status - .on_tick(&self.eth_client, &self.store, &self.rollup_store) - .await?; - self.mempool.on_tick(&self.rollup_client).await?; - self.batches_table - .on_tick( - &self.eth_client, - &self.rollup_store, - self.osaka_activation_time, - ) - .await?; - self.blocks_table.on_tick(&self.store).await?; - self.l1_to_l2_messages - .on_tick(&self.eth_client, &self.store) - .await?; - self.l2_to_l1_messages - .on_tick(&self.eth_client, &self.rollup_client) - .await?; - self.rich_accounts.on_tick(&self.rollup_client).await?; - - Ok(()) - } - - fn mouse_label(&self) -> &str { - if self.mouse_captured { - "m: mouse [ON]" - } else { - "m: mouse [OFF]" - } - } - - fn render(&mut self, area: Rect, buf: &mut Buffer) -> Result<(), MonitorError> - where - Self: Sized, - { - let chunks = Layout::vertical([Constraint::Length(3), Constraint::Min(0)]).split(area); - let tabs = Tabs::default() - .titles([ - TabsState::Overview.to_string(), - TabsState::Logs.to_string(), - TabsState::RichAccounts.to_string(), - ]) - .block( - Block::bordered() - .border_style(Style::default().fg(Color::Cyan)) - .title(Span::styled( - self.title.clone(), - Style::default().add_modifier(Modifier::BOLD), - )), - ) - .highlight_style(Style::default().add_modifier(Modifier::BOLD)) - .select(self.tabs.clone()); - - tabs.render(*chunks.first().ok_or(MonitorError::Chunks)?, buf); - - match self.tabs { - TabsState::Overview => { - let chunks = Layout::vertical([ - Constraint::Length(10), - if let Some(height) = self.batch_widget_height { - Constraint::Length(height) - } else { - Constraint::Fill(1) - }, - Constraint::Fill(1), - Constraint::Fill(1), - Constraint::Fill(1), - Constraint::Fill(1), - Constraint::Length(1), - ]) - .split(*chunks.get(1).ok_or(MonitorError::Chunks)?); - { - let constraints = vec![ - Constraint::Fill(1), - Constraint::Length(LATEST_BLOCK_STATUS_TABLE_LENGTH_IN_DIGITS), - ]; - - let chunks = Layout::horizontal(constraints) - .split(*chunks.first().ok_or(MonitorError::Chunks)?); - - let logo = Paragraph::new(ETHREX_LOGO) - .centered() - .style(Style::default()) - .block(Block::bordered().border_style(Style::default().fg(Color::Cyan))); - - logo.render(*chunks.first().ok_or(MonitorError::Chunks)?, buf); - - { - let constraints = vec![Constraint::Fill(1), Constraint::Fill(1)]; - - let chunks = Layout::horizontal(constraints) - .split(*chunks.get(1).ok_or(MonitorError::Chunks)?); - - let mut node_status_state = self.node_status.state.clone(); - self.node_status.render( - *chunks.first().ok_or(MonitorError::Chunks)?, - buf, - &mut node_status_state, - ); - - let mut global_chain_status_state = self.global_chain_status.state.clone(); - self.global_chain_status.render( - *chunks.get(1).ok_or(MonitorError::Chunks)?, - buf, - &mut global_chain_status_state, - ); - } - } - let mut batches_table_state = self.batches_table.state.clone(); - self.batches_table.render( - *chunks.get(1).ok_or(MonitorError::Chunks)?, - buf, - &mut batches_table_state, - ); - - let mut blocks_table_state = self.blocks_table.state.clone(); - self.blocks_table.render( - *chunks.get(2).ok_or(MonitorError::Chunks)?, - buf, - &mut blocks_table_state, - ); - - let mut mempool_state = self.mempool.state.clone(); - self.mempool.render( - *chunks.get(3).ok_or(MonitorError::Chunks)?, - buf, - &mut mempool_state, - ); - - let mut l1_to_l2_messages_state = self.l1_to_l2_messages.state.clone(); - self.l1_to_l2_messages.render( - *chunks.get(4).ok_or(MonitorError::Chunks)?, - buf, - &mut l1_to_l2_messages_state, - ); - - let mut l2_to_l1_messages_state = self.l2_to_l1_messages.state.clone(); - self.l2_to_l1_messages.render( - *chunks.get(5).ok_or(MonitorError::Chunks)?, - buf, - &mut l2_to_l1_messages_state, - ); - - let help = Line::raw(format!( - "↑/↓: select table | w/s: scroll table | {} | tab: switch tab | Q: quit", - self.mouse_label() - )) - .centered(); - - help.render(*chunks.get(6).ok_or(MonitorError::Chunks)?, buf); - } - TabsState::Logs => { - let chunks = Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]) - .split(*chunks.get(1).ok_or(MonitorError::Chunks)?); - let log_widget = TuiLoggerSmartWidget::default() - .style_error(Style::default().fg(Color::Red)) - .style_debug(Style::default().fg(Color::LightBlue)) - .style_warn(Style::default().fg(Color::Yellow)) - .style_trace(Style::default().fg(Color::Magenta)) - .style_info(Style::default().fg(Color::White)) - .border_style(Style::default().fg(Color::Cyan)) - .output_separator(' ') - .output_timestamp(Some("%F %H:%M:%S%.3f".to_string())) - .output_level(Some(TuiLoggerLevelOutput::Long)) - .output_target(true) - .output_file(false) - .output_line(false) - .state(&self.logger); - - log_widget.render(*chunks.first().ok_or(MonitorError::Chunks)?, buf); - - let help = Line::raw(format!("↑/↓: select target | f: focus target | ←/→: display level | +/-: filter level | h: hide target selector | {} | tab: switch tab | Q: quit", self.mouse_label())).centered(); - - help.render(*chunks.get(1).ok_or(MonitorError::Chunks)?, buf); - } - TabsState::RichAccounts => { - let chunks = Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]) - .split(*chunks.get(1).ok_or(MonitorError::Chunks)?); - let mut accounts = self.rich_accounts.state.clone(); - self.rich_accounts.render( - *chunks.first().ok_or(MonitorError::Chunks)?, - buf, - &mut accounts, - ); - let help = Line::raw(format!( - "w/s: scroll table | {} | tab: switch tab | Q: quit", - self.mouse_label() - )) - .centered(); - help.render(*chunks.get(1).ok_or(MonitorError::Chunks)?, buf); - } - }; - Ok(()) - } -} - -fn setup_terminal() -> Result>, MonitorError> { - enable_raw_mode().map_err(MonitorError::Io)?; - let mut stdout = io::stdout(); - execute!(stdout, EnterAlternateScreen, EnableMouseCapture).map_err(MonitorError::Io)?; - let backend = CrosstermBackend::new(stdout); - let terminal = Terminal::new(backend).map_err(MonitorError::Io)?; - Ok(terminal) -} - -fn restore_terminal( - terminal: &mut Terminal>, -) -> Result<(), MonitorError> { - disable_raw_mode().map_err(MonitorError::Io)?; - execute!( - terminal.backend_mut(), - LeaveAlternateScreen, - DisableMouseCapture - ) - .map_err(MonitorError::Io)?; - terminal.show_cursor().map_err(MonitorError::Io)?; - Ok(()) -} - -impl Widget for &mut EthrexMonitorWidget { - fn render(self, area: Rect, buf: &mut Buffer) - where - Self: Sized, - { - let result = self.render(area, buf); - match result { - Ok(_) => {} - Err(e) => { - buf.reset(); - let error = Line::raw(format!("Error: {e}")).centered(); - - error.render(area, buf); - } - } - } -} diff --git a/tooling/monitor/src/config.rs b/tooling/monitor/src/config.rs deleted file mode 100644 index e0bd68e7315..00000000000 --- a/tooling/monitor/src/config.rs +++ /dev/null @@ -1,14 +0,0 @@ -#[derive(Clone, Debug)] -pub struct MonitorConfig { - pub enabled: bool, - /// time in ms between two ticks. - pub tick_rate: u64, - /// height in lines of the batch widget - pub batch_widget_height: Option, - pub on_chain_proposer_address: ethrex_common::Address, - pub bridge_address: ethrex_common::Address, - pub sequencer_registry_address: Option, - pub rpc_urls: Vec, - pub is_based: bool, - pub osaka_activation_time: Option, -} diff --git a/tooling/monitor/src/error.rs b/tooling/monitor/src/error.rs deleted file mode 100644 index ade7e00ca41..00000000000 --- a/tooling/monitor/src/error.rs +++ /dev/null @@ -1,65 +0,0 @@ -use ethrex_common::Address; -use ethrex_rpc::clients::eth::errors::{CalldataEncodeError, EthClientError}; -use ethrex_storage::error::StoreError; -use ethrex_storage_rollup::RollupStoreError; -use spawned_concurrency::error::ActorError; - -#[derive(Debug, thiserror::Error)] -pub enum MonitorError { - #[error("Failed because of io error: {0}")] - Io(#[from] std::io::Error), - #[error("Failed to fetch {0:?} logs from {1}, {2}")] - LogsSignatures(Vec, Address, #[source] EthClientError), - #[error("Failed to get batch by number {0}: {1}")] - GetBatchByNumber(u64, #[source] RollupStoreError), - #[error("Failed to get blocks by batch number {0}: {1}")] - GetBlocksByBatch(u64, #[source] RollupStoreError), - #[error("Batch {0} not found in the rollup store")] - BatchNotFound(u64), - #[error("Failed to get block by number {0}, {1}")] - GetBlockByNumber(u64, #[source] StoreError), - #[error("Block {0} not found in the store")] - BlockNotFound(u64), - #[error("Internal Error: {0}")] - InternalError(#[from] ActorError), - #[error("Failed to get logs topics {0}")] - LogsTopics(usize), - #[error("Failed to get logs data from {0}")] - LogsData(usize), - #[error("Failed to get area chunks")] - Chunks, - #[error("Failed to get latest block")] - GetLatestBlock, - #[error("Failed to get latest batch")] - GetLatestBatch, - #[error("Failed to get latest verified batch")] - GetLatestVerifiedBatch, - #[error("Failed to get committed batch")] - GetLatestCommittedBatch, - #[error("Failed to get last L1 block fetched")] - GetLastFetchedL1, - #[error("Failed to get pending privileged transactions")] - GetPendingPrivilegedTx, - #[error("Failed to get transaction pool")] - TxPoolError, - #[error("Failed to encode calldata: {0}")] - CalldataEncodeError(#[from] CalldataEncodeError), - #[error("Failed to parse privileged transaction")] - PrivilegedTxParseError, - #[error("Failure in rpc call: {0}")] - EthClientError(#[from] EthClientError), - #[error("Failed to get receipt for transaction")] - ReceiptError, - #[error("Expected transaction to have logs")] - NoLogs, - #[error("Expected items in the table")] - NoItemsInTable, - #[error("RPC List can't be empty")] - RPCListEmpty, - #[error("Error converting batch window")] - BatchWindow, - #[error("Error while parsing private key")] - DecodingError(String), - #[error("Error parsing secret key")] - FromHexError(#[from] hex::FromHexError), -} diff --git a/tooling/monitor/src/lib.rs b/tooling/monitor/src/lib.rs deleted file mode 100644 index 28331671eed..00000000000 --- a/tooling/monitor/src/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -// TODO: Handle this expects -#[expect(clippy::result_large_err)] -pub mod app; -pub mod config; -pub mod error; -pub mod utils; -pub mod widget; - -pub use app::EthrexMonitor; -pub use config::MonitorConfig; -pub use error::MonitorError; diff --git a/tooling/monitor/src/utils.rs b/tooling/monitor/src/utils.rs deleted file mode 100644 index 4bab0f003f3..00000000000 --- a/tooling/monitor/src/utils.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::cmp::min; - -use ethrex_common::utils::keccak; -use ethrex_common::{Address, U256}; -use ethrex_rpc::{EthClient, types::receipt::RpcLog}; - -use crate::error::MonitorError; - -pub async fn get_logs( - last_block_fetched: &mut U256, - emitter: Address, - logs_signatures: Vec<&str>, - client: &EthClient, -) -> Result, MonitorError> { - let last_block_number = U256::from( - client - .get_block_number() - .await - .map_err(|_| MonitorError::GetLatestBlock)?, - ); - - let mut batch_committed_logs = Vec::new(); - while *last_block_fetched < last_block_number { - let new_last_l1_fetched_block = min(*last_block_fetched + 50, last_block_number); - - // Fetch logs from the L1 chain for the BatchCommitted event. - let logs = client - .get_logs( - *last_block_fetched + 1, - new_last_l1_fetched_block, - emitter, - logs_signatures - .iter() - .map(|log_signature| keccak(log_signature.as_bytes())) - .collect(), - ) - .await - .map_err(|e| { - MonitorError::LogsSignatures( - logs_signatures.iter().map(|s| s.to_string()).collect(), - emitter, - e, - ) - })?; - - // Update the last L1 block fetched. - *last_block_fetched = new_last_l1_fetched_block; - - batch_committed_logs.extend_from_slice(&logs); - } - - Ok(batch_committed_logs) -} - -pub trait SelectableScroller { - fn selected(&mut self, is_selected: bool); - fn scroll_up(&mut self); - fn scroll_down(&mut self); -} diff --git a/tooling/monitor/src/widget/batches.rs b/tooling/monitor/src/widget/batches.rs deleted file mode 100644 index 6551be42334..00000000000 --- a/tooling/monitor/src/widget/batches.rs +++ /dev/null @@ -1,270 +0,0 @@ -use ethrex_common::{ - Address, H256, - types::{Fork, batch::Batch}, -}; -use ethrex_l2_sdk::{get_l1_active_fork, get_last_committed_batch}; -use ethrex_rpc::EthClient; -use ethrex_storage_rollup::StoreRollup; -use ratatui::{ - buffer::Buffer, - layout::{Constraint, Rect}, - style::{Color, Modifier, Style}, - text::Span, - widgets::{Block, Row, StatefulWidget, Table, TableState}, -}; - -use crate::{ - error::MonitorError, - utils::SelectableScroller, - widget::{HASH_LENGTH_IN_DIGITS, NUMBER_LENGTH_IN_DIGITS}, -}; - -const BATCH_WINDOW_SIZE: usize = 50; - -#[derive(Clone)] -pub struct BatchLine { - number: u64, - block_count: u64, - message_count: usize, - commit_tx: Option, - verify_tx: Option, -} - -#[derive(Clone, Default)] -pub struct BatchesTable { - pub state: TableState, - pub items: Vec, - last_l1_block_fetched: u64, - on_chain_proposer_address: Address, - selected: bool, -} - -impl BatchesTable { - pub fn new(on_chain_proposer_address: Address) -> Self { - Self { - on_chain_proposer_address, - ..Default::default() - } - } - - pub async fn on_tick( - &mut self, - eth_client: &EthClient, - rollup_store: &StoreRollup, - osaka_activation_time: Option, - ) -> Result<(), MonitorError> { - let mut new_latest_batches = Self::fetch_new_items( - &mut self.last_l1_block_fetched, - self.on_chain_proposer_address, - eth_client, - rollup_store, - osaka_activation_time, - ) - .await?; - new_latest_batches.truncate(BATCH_WINDOW_SIZE); - - let l1_fork = get_l1_active_fork(eth_client, osaka_activation_time) - .await - .map_err(MonitorError::EthClientError)?; - - let n_new_latest_batches = new_latest_batches.len(); - self.items - .truncate(BATCH_WINDOW_SIZE - n_new_latest_batches); - self.refresh_items(rollup_store, l1_fork).await?; - self.items.extend_from_slice(&new_latest_batches); - self.items.rotate_right(n_new_latest_batches); - - Ok(()) - } - - async fn refresh_items( - &mut self, - rollup_store: &StoreRollup, - fork: Fork, - ) -> Result<(), MonitorError> { - if self.items.is_empty() { - return Ok(()); - } - - let mut refreshed_batches = Vec::new(); - - for batch in self.items.iter() { - if batch.commit_tx.is_some() && batch.verify_tx.is_some() { - refreshed_batches.push(batch.clone()); - } else { - let batch_number = batch.number; - let new_batch = rollup_store - .get_batch(batch_number, fork) - .await - .map_err(|e| MonitorError::GetBatchByNumber(batch_number, e))? - .ok_or(MonitorError::BatchNotFound(batch_number))?; - - refreshed_batches.push(Self::process_batch(&new_batch)); - } - } - - Self::reorder_batches(&mut refreshed_batches); - - self.items = refreshed_batches; - - Ok(()) - } - - async fn fetch_new_items( - last_l2_batch_fetched: &mut u64, - on_chain_proposer_address: Address, - eth_client: &EthClient, - rollup_store: &StoreRollup, - osaka_activation_time: Option, - ) -> Result, MonitorError> { - let last_l2_batch_number = get_last_committed_batch(eth_client, on_chain_proposer_address) - .await - .map_err(|_| MonitorError::GetLatestBatch)?; - - *last_l2_batch_fetched = (*last_l2_batch_fetched).max( - last_l2_batch_number.saturating_sub( - BATCH_WINDOW_SIZE - .try_into() - .map_err(|_| MonitorError::BatchWindow)?, - ), - ); - let l1_fork = get_l1_active_fork(eth_client, osaka_activation_time) - .await - .map_err(MonitorError::EthClientError)?; - - let new_batches = Self::get_batches( - last_l2_batch_fetched, - last_l2_batch_number, - rollup_store, - l1_fork, - ) - .await?; - - Ok(Self::process_batches(new_batches)) - } - - async fn get_batches( - from: &mut u64, - to: u64, - rollup_store: &StoreRollup, - fork: Fork, - ) -> Result, MonitorError> { - let mut new_batches = Vec::new(); - - for batch_number in *from + 1..=to { - let batch = rollup_store - .get_batch(batch_number, fork) - .await - .map_err(|e| MonitorError::GetBatchByNumber(batch_number, e))? - .ok_or(MonitorError::BatchNotFound(batch_number))?; - - *from = batch_number; - - new_batches.push(batch); - } - - Ok(new_batches) - } - - fn process_batch(batch: &Batch) -> BatchLine { - BatchLine { - number: batch.number, - block_count: batch.last_block - batch.first_block + 1, - message_count: batch.l1_out_message_hashes.len(), - commit_tx: batch.commit_tx, - verify_tx: batch.verify_tx, - } - } - - fn reorder_batches(new_blocks_processed: &mut [BatchLine]) { - new_blocks_processed.sort_by(|a, b| b.number.cmp(&a.number)); - } - - fn process_batches(new_batches: Vec) -> Vec { - let mut new_blocks_processed = new_batches - .iter() - .map(Self::process_batch) - .collect::>(); - - Self::reorder_batches(&mut new_blocks_processed); - - new_blocks_processed - } -} - -impl StatefulWidget for &mut BatchesTable { - type State = TableState; - - fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { - let constraints = vec![ - Constraint::Length(NUMBER_LENGTH_IN_DIGITS), - Constraint::Length(NUMBER_LENGTH_IN_DIGITS), - Constraint::Length(17), - Constraint::Length(HASH_LENGTH_IN_DIGITS), - Constraint::Length(HASH_LENGTH_IN_DIGITS), - ]; - let rows = self.items.iter().map(|batch| { - Row::new(vec![ - Span::styled(batch.number.to_string(), Style::default()), - Span::styled(batch.block_count.to_string(), Style::default()), - Span::styled(batch.message_count.to_string(), Style::default()), - Span::styled( - batch - .commit_tx - .map_or_else(|| "Uncommitted".to_string(), |hash| format!("{hash:#x}")), - Style::default(), - ), - Span::styled( - batch - .verify_tx - .map_or_else(|| "Unverified".to_string(), |hash| format!("{hash:#x}")), - Style::default(), - ), - ]) - }); - let committed_batches_table = Table::new(rows, constraints) - .header( - Row::new(vec![ - "Number", - "# Blocks", - "# L2 to L1 Messages", - "Commit Tx Hash", - "Verify Tx Hash", - ]) - .style(Style::default()), - ) - .block( - Block::bordered() - .border_style(Style::default().fg(if self.selected { - Color::Magenta - } else { - Color::Cyan - })) - .title(Span::styled( - "L2 Batches", - Style::default().add_modifier(Modifier::BOLD), - )), - ); - - committed_batches_table.render(area, buf, state); - } -} - -impl SelectableScroller for BatchesTable { - fn selected(&mut self, is_selected: bool) { - self.selected = is_selected; - } - fn scroll_up(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some(selected.unwrap_or(0).saturating_sub(1)) - } - fn scroll_down(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some( - selected - .unwrap_or(0) - .saturating_add(1) - .min(self.items.len().saturating_sub(1)), - ) - } -} diff --git a/tooling/monitor/src/widget/blocks.rs b/tooling/monitor/src/widget/blocks.rs deleted file mode 100644 index 537448fc0fc..00000000000 --- a/tooling/monitor/src/widget/blocks.rs +++ /dev/null @@ -1,219 +0,0 @@ -use std::cmp::min; - -use ethrex_common::{Address, H256, types::Block}; -use ethrex_rlp::encode::RLPEncode; -use ethrex_storage::Store; -use ratatui::{ - buffer::Buffer, - layout::{Constraint, Rect}, - style::{Color, Modifier, Style}, - text::Span, - widgets::{Row, StatefulWidget, Table, TableState}, -}; - -use crate::{ - error::MonitorError, - utils::SelectableScroller, - widget::{ - ADDRESS_LENGTH_IN_DIGITS, BLOCK_SIZE_LENGTH_IN_DIGITS, GAS_USED_LENGTH_IN_DIGITS, - HASH_LENGTH_IN_DIGITS, NUMBER_LENGTH_IN_DIGITS, TX_NUMBER_LENGTH_IN_DIGITS, - }, -}; - -struct BlockEntry { - number: u64, - transaction_count: usize, - hash: H256, - coinbase: Address, - gas_used: u64, - blob_gas_used: Option, - size: usize, -} - -impl From<&BlockEntry> for BlockListItem { - fn from(entry: &BlockEntry) -> Self { - BlockListItem { - number: entry.number.to_string(), - transaction_count: entry.transaction_count.to_string(), - hash: format!("{:#x}", entry.hash), - coinbase: format!("{:#x}", entry.coinbase), - gas_used: entry.gas_used.to_string(), - blob_gas_used: entry - .blob_gas_used - .map_or("0".to_string(), |bg| bg.to_string()), - size: entry.size.to_string(), - } - } -} - -#[derive(Clone)] -/// block number | #transactions | hash | coinbase | gas | blob gas | size -struct BlockListItem { - number: String, - transaction_count: String, - hash: String, - coinbase: String, - gas_used: String, - blob_gas_used: String, - size: String, -} - -#[derive(Clone, Default)] -pub struct BlocksTable { - pub state: TableState, - items: Vec, - last_l2_block_known: u64, - selected: bool, -} - -impl BlocksTable { - pub fn new() -> Self { - Default::default() - } - - pub async fn on_tick(&mut self, store: &Store) -> Result<(), MonitorError> { - let mut new_blocks = Self::refresh_items(&mut self.last_l2_block_known, store).await?; - new_blocks.drain(..new_blocks.len().saturating_sub(50)); - - let n_new_blocks = new_blocks.len(); - let items_to_keep = 50usize.saturating_sub(n_new_blocks); - self.items - .drain(..self.items.len().saturating_sub(items_to_keep)); - self.items.extend_from_slice(&new_blocks); - self.items.rotate_right(n_new_blocks); - - Ok(()) - } - - async fn refresh_items( - last_l2_block_known: &mut u64, - store: &Store, - ) -> Result, MonitorError> { - let new_blocks = Self::get_blocks(last_l2_block_known, store).await?; - - let new_blocks_processed = Self::process_blocks(new_blocks); - - let new_items = new_blocks_processed - .iter() - .map(BlockListItem::from) - .collect(); - Ok(new_items) - } - - async fn get_blocks( - last_l2_block_known: &mut u64, - store: &Store, - ) -> Result, MonitorError> { - let last_l2_block_number = store - .get_latest_block_number() - .await - .map_err(|_| MonitorError::GetLatestBlock)?; - - let mut new_blocks = Vec::new(); - while *last_l2_block_known < last_l2_block_number { - let new_last_l1_fetched_block = min(*last_l2_block_known + 1, last_l2_block_number); - - let new_block = store - .get_block_by_number(new_last_l1_fetched_block) - .await - .map_err(|e| MonitorError::GetBlockByNumber(new_last_l1_fetched_block, e))? - .ok_or(MonitorError::BlockNotFound(new_last_l1_fetched_block))?; - - // Update the last L1 block fetched. - *last_l2_block_known = new_last_l1_fetched_block; - - new_blocks.push(new_block); - } - - Ok(new_blocks) - } - - fn process_blocks(new_blocks: Vec) -> Vec { - let mut new_blocks_processed = new_blocks - .iter() - .map(|block| BlockEntry { - number: block.header.number, - transaction_count: block.body.transactions.len(), - hash: block.header.hash(), - coinbase: block.header.coinbase, - gas_used: block.header.gas_used, - blob_gas_used: block.header.blob_gas_used, - size: block.length(), - }) - .collect::>(); - - new_blocks_processed.sort_by_key(|x| x.number); - - new_blocks_processed - } -} - -impl StatefulWidget for &mut BlocksTable { - type State = TableState; - - fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) - where - Self: Sized, - { - let constraints = vec![ - Constraint::Length(NUMBER_LENGTH_IN_DIGITS), - Constraint::Length(TX_NUMBER_LENGTH_IN_DIGITS), - Constraint::Length(HASH_LENGTH_IN_DIGITS), - Constraint::Length(ADDRESS_LENGTH_IN_DIGITS), - Constraint::Length(GAS_USED_LENGTH_IN_DIGITS), - Constraint::Length(GAS_USED_LENGTH_IN_DIGITS), - Constraint::Length(BLOCK_SIZE_LENGTH_IN_DIGITS), - ]; - let rows = self.items.iter().map(|item| { - Row::new(vec![ - Span::styled(&item.number, Style::default()), - Span::styled(&item.transaction_count, Style::default()), - Span::styled(&item.hash, Style::default()), - Span::styled(&item.coinbase, Style::default()), - Span::styled(&item.gas_used, Style::default()), - Span::styled(&item.blob_gas_used, Style::default()), - Span::styled(&item.size, Style::default()), - ]) - }); - let latest_blocks_table = Table::new(rows, constraints) - .header( - Row::new(vec![ - "Number", "#Txs", "Hash", "Coinbase", "Gas", "Blob Gas", "Size", - ]) - .style(Style::default()), - ) - .block( - ratatui::widgets::Block::bordered() - .border_style(Style::default().fg(if self.selected { - Color::Magenta - } else { - Color::Cyan - })) - .title(Span::styled( - "L2 Blocks", - Style::default().add_modifier(Modifier::BOLD), - )), - ); - - latest_blocks_table.render(area, buf, state); - } -} - -impl SelectableScroller for BlocksTable { - fn selected(&mut self, is_selected: bool) { - self.selected = is_selected; - } - fn scroll_up(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some(selected.unwrap_or(0).saturating_sub(1)) - } - fn scroll_down(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some( - selected - .unwrap_or(0) - .saturating_add(1) - .min(self.items.len().saturating_sub(1)), - ) - } -} diff --git a/tooling/monitor/src/widget/chain_status.rs b/tooling/monitor/src/widget/chain_status.rs deleted file mode 100644 index 68e4fb070df..00000000000 --- a/tooling/monitor/src/widget/chain_status.rs +++ /dev/null @@ -1,201 +0,0 @@ -use ethrex_common::{Address, H256}; -use ethrex_l2_sdk::{calldata::encode_calldata, get_last_committed_batch, get_last_verified_batch}; -use ethrex_rpc::{EthClient, clients::Overrides}; -use ethrex_storage::Store; -use ethrex_storage_rollup::StoreRollup; -use ratatui::{ - buffer::Buffer, - layout::{Constraint, Rect}, - style::{Color, Modifier, Style}, - text::Span, - widgets::{Block, Row, StatefulWidget, Table, TableState}, -}; - -use crate::{MonitorConfig, error::MonitorError}; - -#[derive(Clone, Default)] -pub struct GlobalChainStatusTable { - pub state: TableState, - pub items: Vec<(String, String)>, - pub on_chain_proposer_address: Address, - pub sequencer_registry_address: Option
, -} - -impl GlobalChainStatusTable { - pub fn new(cfg: &MonitorConfig) -> Self { - let sequencer_registry_address = cfg.sequencer_registry_address; - Self { - on_chain_proposer_address: cfg.on_chain_proposer_address, - sequencer_registry_address, - ..Default::default() - } - } - - pub async fn on_tick( - &mut self, - eth_client: &EthClient, - store: &Store, - rollup_store: &StoreRollup, - ) -> Result<(), MonitorError> { - self.items = Self::refresh_items( - eth_client, - self.on_chain_proposer_address, - self.sequencer_registry_address, - store, - rollup_store, - ) - .await?; - Ok(()) - } - - async fn refresh_items( - eth_client: &EthClient, - on_chain_proposer_address: Address, - sequencer_registry_address: Option
, - store: &Store, - rollup_store: &StoreRollup, - ) -> Result, MonitorError> { - let last_update = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); - - let lead_sequencer = if let Some(sequencer_registry_address) = sequencer_registry_address { - let calldata = encode_calldata("leaderSequencer()", &[]) - .map_err(MonitorError::CalldataEncodeError)?; - - let raw_lead_sequencer: H256 = eth_client - .call( - sequencer_registry_address, - calldata.into(), - Overrides::default(), - ) - .await - .map_err(MonitorError::EthClientError)? - .parse() - .unwrap_or_default(); - - Address::from_slice(&raw_lead_sequencer.as_fixed_bytes()[12..]) - } else { - Address::default() - }; - - let last_committed_batch = get_last_committed_batch(eth_client, on_chain_proposer_address) - .await - .map_err(|_| MonitorError::GetLatestCommittedBatch)?; - - let last_verified_batch = get_last_verified_batch(eth_client, on_chain_proposer_address) - .await - .map_err(|_| MonitorError::GetLatestVerifiedBatch)?; - - let last_committed_block = if last_committed_batch == 0 { - 0 - } else { - match rollup_store - .get_block_numbers_by_batch(last_committed_batch) - .await - .map_err(|e| MonitorError::GetBlocksByBatch(last_committed_batch, e))? - { - Some(block_numbers) => block_numbers.last().copied().unwrap_or(0), - None => 0, - } - }; - - let last_verified_block = if last_verified_batch == 0 { - 0 - } else { - match rollup_store - .get_block_numbers_by_batch(last_verified_batch) - .await - .map_err(|e| MonitorError::GetBlocksByBatch(last_verified_batch, e))? - { - Some(block_numbers) => block_numbers.last().copied().unwrap_or(0), - None => 0, - } - }; - - let current_block = store - .get_latest_block_number() - .await - .map_err(|_| MonitorError::GetLatestBlock)? - + 1; - - let current_batch = if sequencer_registry_address.is_some() { - "NaN".to_string() // TODO: Implement current batch retrieval (should be last known + 1) - } else { - (last_committed_batch + 1).to_string() - }; - - let items = if sequencer_registry_address.is_some() { - vec![ - ("Last Update:".to_string(), last_update), - ( - "Lead Sequencer:".to_string(), - format!("{lead_sequencer:#x}"), - ), - ("Current Batch:".to_string(), current_batch), - ("Current Block:".to_string(), current_block.to_string()), - ( - "Last Committed Batch:".to_string(), - last_committed_batch.to_string(), - ), - ( - "Last Committed Block:".to_string(), - last_committed_block.to_string(), - ), - ( - "Last Verified Batch:".to_string(), - last_verified_batch.to_string(), - ), - ( - "Last Verified Block:".to_string(), - last_verified_block.to_string(), - ), - ] - } else { - vec![ - ("Last Update:".to_string(), last_update), - ("Current Batch:".to_string(), current_batch), - ("Current Block:".to_string(), current_block.to_string()), - ( - "Last Committed Batch:".to_string(), - last_committed_batch.to_string(), - ), - ( - "Last Committed Block:".to_string(), - last_committed_block.to_string(), - ), - ( - "Last Verified Batch:".to_string(), - last_verified_batch.to_string(), - ), - ( - "Last Verified Block:".to_string(), - last_verified_block.to_string(), - ), - ] - }; - Ok(items) - } -} - -impl StatefulWidget for &mut GlobalChainStatusTable { - type State = TableState; - - fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { - let constraints = vec![Constraint::Percentage(50), Constraint::Percentage(50)]; - let rows = self.items.iter().map(|(key, value)| { - Row::new(vec![ - Span::styled(key, Style::default()), - Span::styled(value, Style::default()), - ]) - }); - let global_chain_status_table = Table::new(rows, constraints).block( - Block::bordered() - .border_style(Style::default().fg(Color::Cyan)) - .title(Span::styled( - "Global Chain Status", - Style::default().add_modifier(Modifier::BOLD), - )), - ); - - global_chain_status_table.render(area, buf, state); - } -} diff --git a/tooling/monitor/src/widget/l1_to_l2_messages.rs b/tooling/monitor/src/widget/l1_to_l2_messages.rs deleted file mode 100644 index 382c29b3426..00000000000 --- a/tooling/monitor/src/widget/l1_to_l2_messages.rs +++ /dev/null @@ -1,273 +0,0 @@ -use std::fmt::Display; - -use ethrex_common::utils::keccak; -use ethrex_common::{Address, H256, U256}; -use ethrex_l2_sdk::privileged_data::PrivilegedTransactionData; -use ethrex_l2_sdk::{COMMON_BRIDGE_L2_ADDRESS, get_pending_l1_messages}; -use ethrex_rpc::{EthClient, types::receipt::RpcLog}; -use ethrex_storage::Store; -use ratatui::{ - buffer::Buffer, - layout::{Constraint, Rect}, - style::{Color, Modifier, Style}, - text::Span, - widgets::{Block, Row, StatefulWidget, Table, TableState}, -}; - -use crate::{error::MonitorError, utils::SelectableScroller, widget::HASH_LENGTH_IN_DIGITS}; - -// kind | status | L1 tx hash | L2 tx hash | amount -pub type L1ToL2MessagesRow = (L1ToL2MessageKind, L1ToL2MessageStatus, H256, H256, U256); - -#[derive(Clone, Default)] -pub struct L1ToL2MessagesTable { - pub state: TableState, - pub items: Vec, - last_l1_block_fetched: U256, - common_bridge_address: Address, - selected: bool, -} - -#[derive(Debug, Clone)] -pub enum L1ToL2MessageStatus { - Unknown = 0, - Pending = 1, - ProcessedOnL2 = 3, - Committed = 4, - Verified = 5, -} - -impl L1ToL2MessageStatus { - pub async fn for_tx( - l2_tx_hash: H256, - common_bridge_address: Address, - eth_client: &EthClient, - store: &Store, - ) -> Result { - if let Ok(Some(_tx)) = store.get_transaction_by_hash(l2_tx_hash).await { - Ok(Self::ProcessedOnL2) - } else if get_pending_l1_messages(eth_client, common_bridge_address) - .await - .map_err(|_| MonitorError::GetPendingPrivilegedTx)? - .contains(&l2_tx_hash) - { - Ok(Self::Pending) - } else { - Ok(Self::Unknown) - } - } -} - -impl Display for L1ToL2MessageStatus { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - L1ToL2MessageStatus::Unknown => write!(f, "Unknown"), - L1ToL2MessageStatus::Pending => write!(f, "Pending"), - L1ToL2MessageStatus::ProcessedOnL2 => write!(f, "Processed on L2"), - L1ToL2MessageStatus::Committed => write!(f, "Committed"), - L1ToL2MessageStatus::Verified => write!(f, "Verified"), - } - } -} - -#[derive(Debug, Clone)] -pub enum L1ToL2MessageKind { - Deposit, - Message, -} - -impl From<&PrivilegedTransactionData> for L1ToL2MessageKind { - fn from(data: &PrivilegedTransactionData) -> Self { - if data.from == COMMON_BRIDGE_L2_ADDRESS && data.to_address == COMMON_BRIDGE_L2_ADDRESS { - Self::Deposit - } else { - Self::Message - } - } -} - -impl Display for L1ToL2MessageKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - L1ToL2MessageKind::Deposit => write!(f, "Deposit"), - L1ToL2MessageKind::Message => write!(f, "Message"), - } - } -} - -impl L1ToL2MessagesTable { - pub fn new(common_bridge_address: Address) -> Self { - Self { - common_bridge_address, - ..Default::default() - } - } - - pub async fn on_tick( - &mut self, - eth_client: &EthClient, - store: &Store, - ) -> Result<(), MonitorError> { - let mut new_l1_to_l2_messages = Self::fetch_new_items( - &mut self.last_l1_block_fetched, - self.common_bridge_address, - eth_client, - store, - ) - .await?; - new_l1_to_l2_messages.drain(..new_l1_to_l2_messages.len().saturating_sub(50)); - - let n_new = new_l1_to_l2_messages.len(); - let items_to_keep = 50usize.saturating_sub(n_new); - self.items - .drain(..self.items.len().saturating_sub(items_to_keep)); - self.refresh_items(eth_client, store).await?; - self.items.extend_from_slice(&new_l1_to_l2_messages); - self.items.rotate_right(n_new); - Ok(()) - } - - async fn refresh_items( - &mut self, - eth_client: &EthClient, - store: &Store, - ) -> Result<(), MonitorError> { - for (_kind, status, _l1_tx_hash, l2_tx_hash, ..) in self.items.iter_mut() { - *status = L1ToL2MessageStatus::for_tx( - *l2_tx_hash, - self.common_bridge_address, - eth_client, - store, - ) - .await?; - } - Ok(()) - } - - async fn fetch_new_items( - last_l1_block_fetched: &mut U256, - common_bridge_address: Address, - eth_client: &EthClient, - store: &Store, - ) -> Result, MonitorError> { - let logs = crate::utils::get_logs( - last_l1_block_fetched, - common_bridge_address, - vec!["PrivilegedTxSent(address,address,uint256,uint256,uint256,bytes)"], - eth_client, - ) - .await?; - Self::process_logs(&logs, common_bridge_address, eth_client, store).await - } - - async fn process_logs( - logs: &[RpcLog], - common_bridge_address: Address, - eth_client: &EthClient, - store: &Store, - ) -> Result, MonitorError> { - let mut processed_logs = Vec::new(); - - for log in logs { - let l1_to_l2_message = PrivilegedTransactionData::from_log(log.log.clone()) - .map_err(|_| MonitorError::PrivilegedTxParseError)?; - - let l1_to_l2_message_hash = keccak( - [ - l1_to_l2_message.from.as_bytes(), - l1_to_l2_message.to_address.as_bytes(), - &l1_to_l2_message.transaction_id.to_big_endian(), - &l1_to_l2_message.value.to_big_endian(), - &l1_to_l2_message.gas_limit.to_big_endian(), - keccak(&l1_to_l2_message.calldata).as_bytes(), - ] - .concat(), - ); - - processed_logs.push(( - L1ToL2MessageKind::from(&l1_to_l2_message), - L1ToL2MessageStatus::for_tx( - l1_to_l2_message_hash, - common_bridge_address, - eth_client, - store, - ) - .await?, - log.transaction_hash, - l1_to_l2_message_hash, - l1_to_l2_message.value, - )); - } - - Ok(processed_logs) - } -} - -impl StatefulWidget for &mut L1ToL2MessagesTable { - type State = TableState; - - fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) - where - Self: Sized, - { - let constraints = vec![ - Constraint::Length(10), - Constraint::Length(15), - Constraint::Length(HASH_LENGTH_IN_DIGITS), - Constraint::Length(HASH_LENGTH_IN_DIGITS), - Constraint::Fill(1), - ]; - - let rows = self - .items - .iter() - .map(|(kind, status, l1_tx_hash, l2_tx_hash, amount)| { - Row::new(vec![ - Span::styled(format!("{kind}"), Style::default()), - Span::styled(format!("{status}"), Style::default()), - Span::styled(format!("{l1_tx_hash:#x}"), Style::default()), - Span::styled(format!("{l2_tx_hash:#x}"), Style::default()), - Span::styled(amount.to_string(), Style::default()), - ]) - }); - - let l1_to_l2_messages_table = Table::new(rows, constraints) - .header( - Row::new(vec!["Kind", "Status", "L1 Tx Hash", "L2 Tx Hash", "Value"]) - .style(Style::default()), - ) - .block( - Block::bordered() - .border_style(Style::default().fg(if self.selected { - Color::Magenta - } else { - Color::Cyan - })) - .title(Span::styled( - "L1 to L2 Messages", - Style::default().add_modifier(Modifier::BOLD), - )), - ); - - l1_to_l2_messages_table.render(area, buf, state); - } -} - -impl SelectableScroller for L1ToL2MessagesTable { - fn selected(&mut self, is_selected: bool) { - self.selected = is_selected; - } - fn scroll_up(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some(selected.unwrap_or(0).saturating_sub(1)) - } - fn scroll_down(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some( - selected - .unwrap_or(0) - .saturating_add(1) - .min(self.items.len().saturating_sub(1)), - ) - } -} diff --git a/tooling/monitor/src/widget/l2_to_l1_messages.rs b/tooling/monitor/src/widget/l2_to_l1_messages.rs deleted file mode 100644 index 32b3de313a7..00000000000 --- a/tooling/monitor/src/widget/l2_to_l1_messages.rs +++ /dev/null @@ -1,391 +0,0 @@ -use std::fmt::Display; - -use ethrex_common::utils::keccak; -use ethrex_common::{Address, H256, U256}; -use ethrex_l2_common::{calldata::Value, messages::MESSENGER_ADDRESS}; -use ethrex_l2_sdk::{COMMON_BRIDGE_L2_ADDRESS, calldata::encode_calldata}; -use ethrex_rpc::{EthClient, clients::Overrides, types::receipt::RpcLog}; -use ratatui::{ - buffer::Buffer, - layout::{Constraint, Rect}, - style::{Color, Modifier, Style}, - text::Span, - widgets::{Block, Row, StatefulWidget, Table, TableState}, -}; - -use crate::{ - error::MonitorError, - utils::SelectableScroller, - widget::{ADDRESS_LENGTH_IN_DIGITS, HASH_LENGTH_IN_DIGITS, NUMBER_LENGTH_IN_DIGITS}, -}; - -/* -event WithdrawalInitiated( - address indexed senderOnL2, => topic 1 - address indexed receiverOnL1, => topic 2 - uint256 indexed amount => topic 3 -); -*/ -const WITHDRAWAL_ETH_RECEIVER_TOPIC_IDX: usize = 2; -const WITHDRAWAL_ETH_AMOUNT_TOPIC_IDX: usize = 3; -/* -event ERC20WithdrawalInitiated( - address indexed tokenL1, => topic 1 - address indexed tokenL2, => topic 2 - address indexed receiverOnL1, => topic 3 - uint256 amount => data 0..32 -); -*/ -const WITHDRAWAL_ERC20_TOKEN_L1_TOPIC_IDX: usize = 1; -const WITHDRAWAL_ERC20_TOKEN_L2_TOPIC_IDX: usize = 2; -const WITHDRAWAL_ERC20_RECEIVER_TOPIC_IDX: usize = 3; -/* -event L1Message( - address indexed senderOnL2, => topic 1 - bytes32 indexed data, => topic 2 - uint256 indexed messageId => topic 3 -); -*/ -const L1MESSAGE_MESSAGE_ID_TOPIC_IDX: usize = 3; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum L2ToL1MessageStatus { - WithdrawalInitiated, - WithdrawalClaimed, - Sent, - Delivered, -} - -impl L2ToL1MessageStatus { - pub async fn for_tx( - l2_tx_hash: H256, - common_bridge_address: Address, - l1_client: &EthClient, - l2_client: &EthClient, - ) -> Result { - let tx_receipt = l2_client - .get_transaction_receipt(l2_tx_hash) - .await? - .ok_or(MonitorError::ReceiptError)?; - let l1message_log = tx_receipt - .logs - .iter() - .find(|log| log.log.address == MESSENGER_ADDRESS) - .ok_or(MonitorError::NoLogs)?; - let msg_id = l1message_log - .log - .topics - .get(L1MESSAGE_MESSAGE_ID_TOPIC_IDX) - .ok_or(MonitorError::LogsTopics(L1MESSAGE_MESSAGE_ID_TOPIC_IDX))?; - let withdrawal_is_claimed = { - let calldata = encode_calldata( - "claimedWithdrawalIDs(uint256)", - &[Value::FixedBytes(msg_id.as_bytes().to_vec().into())], - ) - .map_err(MonitorError::CalldataEncodeError)?; - - let raw_withdrawal_is_claimed: H256 = l1_client - .call(common_bridge_address, calldata.into(), Overrides::default()) - .await - .map_err(MonitorError::EthClientError)? - .parse() - .unwrap_or_default(); - - U256::from_big_endian(raw_withdrawal_is_claimed.as_fixed_bytes()) == U256::one() - }; - - if withdrawal_is_claimed { - Ok(Self::WithdrawalClaimed) - } else { - Ok(Self::WithdrawalInitiated) - } - } -} - -impl Display for L2ToL1MessageStatus { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - L2ToL1MessageStatus::WithdrawalInitiated => write!(f, "Initiated"), - L2ToL1MessageStatus::WithdrawalClaimed => write!(f, "Claimed"), - L2ToL1MessageStatus::Sent => write!(f, "Sent"), - L2ToL1MessageStatus::Delivered => write!(f, "Delivered"), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum L2ToL1MessageKind { - ETHWithdraw, - ERC20Withdraw, - Message, -} - -impl Display for L2ToL1MessageKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - L2ToL1MessageKind::ETHWithdraw => write!(f, "Withdraw (ETH)"), - L2ToL1MessageKind::ERC20Withdraw => write!(f, "Withdraw (ERC20)"), - L2ToL1MessageKind::Message => write!(f, "Message"), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct L2ToL1MessageRow { - pub kind: L2ToL1MessageKind, - pub status: L2ToL1MessageStatus, - pub receiver: Address, - pub value: U256, - pub token_l1: Address, - pub token_l2: Address, - pub l2_tx_hash: H256, -} - -#[derive(Clone, Default)] -pub struct L2ToL1MessagesTable { - pub state: TableState, - pub items: Vec, - last_l2_block_fetched: U256, - common_bridge_address: Address, - selected: bool, -} - -impl L2ToL1MessagesTable { - pub fn new(common_bridge_address: Address) -> Self { - Self { - common_bridge_address, - ..Default::default() - } - } - - pub async fn on_tick( - &mut self, - eth_client: &EthClient, - rollup_client: &EthClient, - ) -> Result<(), MonitorError> { - let mut new_l2_to_l1_messages = Self::fetch_new_items( - &mut self.last_l2_block_fetched, - self.common_bridge_address, - eth_client, - rollup_client, - ) - .await?; - new_l2_to_l1_messages.drain(..new_l2_to_l1_messages.len().saturating_sub(50)); - - let n_new = new_l2_to_l1_messages.len(); - let items_to_keep = 50usize.saturating_sub(n_new); - self.items - .drain(..self.items.len().saturating_sub(items_to_keep)); - self.refresh_items(eth_client, rollup_client).await?; - self.items.extend_from_slice(&new_l2_to_l1_messages); - self.items.rotate_right(n_new); - - Ok(()) - } - - async fn refresh_items( - &mut self, - l1_client: &EthClient, - l2_client: &EthClient, - ) -> Result<(), MonitorError> { - for row in self.items.iter_mut() { - row.status = L2ToL1MessageStatus::for_tx( - row.l2_tx_hash, - self.common_bridge_address, - l1_client, - l2_client, - ) - .await?; - } - Ok(()) - } - - async fn fetch_new_items( - last_l2_block_fetched: &mut U256, - common_bridge_address: Address, - eth_client: &EthClient, - rollup_client: &EthClient, - ) -> Result, MonitorError> { - let logs = crate::utils::get_logs( - last_l2_block_fetched, - COMMON_BRIDGE_L2_ADDRESS, - vec![], - rollup_client, - ) - .await?; - Self::process_logs(&logs, common_bridge_address, eth_client, rollup_client).await - } - - async fn process_logs( - logs: &[RpcLog], - common_bridge_address: Address, - l1_client: &EthClient, - l2_client: &EthClient, - ) -> Result, MonitorError> { - let mut processed_logs = Vec::new(); - - let eth_withdrawal_topic = keccak(b"WithdrawalInitiated(address,address,uint256)"); - let erc20_withdrawal_topic = - keccak(b"ERC20WithdrawalInitiated(address,address,address,uint256)"); - - for log in logs { - let withdrawal_status = match L2ToL1MessageStatus::for_tx( - log.transaction_hash, - common_bridge_address, - l1_client, - l2_client, - ) - .await - { - Ok(status) => status, - Err(MonitorError::NoLogs) => continue, - Err(e) => return Err(e), - }; - match *log.log.topics.first().ok_or(MonitorError::LogsTopics(0))? { - topic if topic == eth_withdrawal_topic => { - processed_logs.push(L2ToL1MessageRow { - kind: L2ToL1MessageKind::ETHWithdraw, - status: withdrawal_status, - receiver: Address::from_slice( - &log.log - .topics - .get(WITHDRAWAL_ETH_RECEIVER_TOPIC_IDX) - .ok_or(MonitorError::LogsTopics(WITHDRAWAL_ETH_RECEIVER_TOPIC_IDX))? - .as_fixed_bytes()[12..], - ), - value: U256::from_big_endian( - log.log - .topics - .get(WITHDRAWAL_ETH_AMOUNT_TOPIC_IDX) - .ok_or(MonitorError::LogsTopics(WITHDRAWAL_ETH_AMOUNT_TOPIC_IDX))? - .as_fixed_bytes(), - ), - token_l1: Address::default(), - token_l2: Address::default(), - l2_tx_hash: log.transaction_hash, - }); - } - topic if topic == erc20_withdrawal_topic => { - processed_logs.push(L2ToL1MessageRow { - kind: L2ToL1MessageKind::ERC20Withdraw, - status: withdrawal_status, - receiver: Address::from_slice( - &log.log - .topics - .get(WITHDRAWAL_ERC20_RECEIVER_TOPIC_IDX) - .ok_or(MonitorError::LogsTopics( - WITHDRAWAL_ERC20_RECEIVER_TOPIC_IDX, - ))? - .as_fixed_bytes()[12..], - ), - value: U256::from_big_endian( - log.log.data.get(0..32).ok_or(MonitorError::LogsData(32))?, - ), - token_l1: Address::from_slice( - &log.log - .topics - .get(WITHDRAWAL_ERC20_TOKEN_L1_TOPIC_IDX) - .ok_or(MonitorError::LogsTopics( - WITHDRAWAL_ERC20_TOKEN_L1_TOPIC_IDX, - ))? - .as_fixed_bytes()[12..], - ), - token_l2: Address::from_slice( - &log.log - .topics - .get(WITHDRAWAL_ERC20_TOKEN_L2_TOPIC_IDX) - .ok_or(MonitorError::LogsTopics( - WITHDRAWAL_ERC20_TOKEN_L2_TOPIC_IDX, - ))? - .as_fixed_bytes()[12..], - ), - l2_tx_hash: log.transaction_hash, - }); - } - _ => { - continue; - } - } - } - - Ok(processed_logs) - } -} - -impl StatefulWidget for &mut L2ToL1MessagesTable { - type State = TableState; - - fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) - where - Self: Sized, - { - let constraints = vec![ - Constraint::Length(16), - Constraint::Length(9), - Constraint::Length(ADDRESS_LENGTH_IN_DIGITS), - Constraint::Length(NUMBER_LENGTH_IN_DIGITS), - Constraint::Length(ADDRESS_LENGTH_IN_DIGITS), - Constraint::Length(ADDRESS_LENGTH_IN_DIGITS), - Constraint::Length(HASH_LENGTH_IN_DIGITS), - ]; - - let rows = self.items.iter().map(|row| { - Row::new(vec![ - Span::styled(format!("{}", row.kind), Style::default()), - Span::styled(format!("{}", row.status), Style::default()), - Span::styled(format!("{:#x}", row.receiver), Style::default()), - Span::styled(row.value.to_string(), Style::default()), - Span::styled(format!("{:#x}", row.token_l1), Style::default()), - Span::styled(format!("{:#x}", row.token_l2), Style::default()), - Span::styled(format!("{:#x}", row.l2_tx_hash), Style::default()), - ]) - }); - - let l1_to_l2_messages_table = Table::new(rows, constraints) - .header( - Row::new(vec![ - "Kind", - "Status", - "Receiver on L1", - "Value", - "Token L1", - "Token L2", - "L2 Tx Hash", - ]) - .style(Style::default()), - ) - .block( - Block::bordered() - .border_style(Style::default().fg(if self.selected { - Color::Magenta - } else { - Color::Cyan - })) - .title(Span::styled( - "L2 to L1 Messages", - Style::default().add_modifier(Modifier::BOLD), - )), - ); - - l1_to_l2_messages_table.render(area, buf, state); - } -} - -impl SelectableScroller for L2ToL1MessagesTable { - fn selected(&mut self, is_selected: bool) { - self.selected = is_selected; - } - fn scroll_up(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some(selected.unwrap_or(0).saturating_sub(1)) - } - fn scroll_down(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some( - selected - .unwrap_or(0) - .saturating_add(1) - .min(self.items.len().saturating_sub(1)), - ) - } -} diff --git a/tooling/monitor/src/widget/mempool.rs b/tooling/monitor/src/widget/mempool.rs deleted file mode 100644 index 60303c10969..00000000000 --- a/tooling/monitor/src/widget/mempool.rs +++ /dev/null @@ -1,124 +0,0 @@ -use ethrex_rpc::EthClient; -use ratatui::{ - buffer::Buffer, - layout::{Constraint, Rect}, - style::{Color, Modifier, Style}, - text::Span, - widgets::{Block, Row, StatefulWidget, Table, TableState}, -}; - -use crate::{ - error::MonitorError, - utils::SelectableScroller, - widget::{ADDRESS_LENGTH_IN_DIGITS, HASH_LENGTH_IN_DIGITS, NUMBER_LENGTH_IN_DIGITS}, -}; - -#[derive(Clone, Default)] -pub struct MempoolTable { - pub state: TableState, - // type | hash | sender | nonce - pub items: Vec<(String, String, String, String)>, - selected: bool, -} - -impl MempoolTable { - pub fn new() -> Self { - Default::default() - } - - pub async fn on_tick(&mut self, rollup_client: &EthClient) -> Result<(), MonitorError> { - self.items = Self::refresh_items(rollup_client).await?; - Ok(()) - } - - async fn refresh_items( - rollup_client: &EthClient, - ) -> Result, MonitorError> { - let mempool = rollup_client - .tx_pool_content() - .await - .map_err(|_| MonitorError::TxPoolError)?; - - let mut pending_txs = mempool - .pending - .iter() - .flat_map(|(sender, txs_sorted_by_nonce)| { - txs_sorted_by_nonce.iter().map(|(nonce, tx)| { - ( - format!("{}", tx.tx.tx_type()), - format!("{:#x}", tx.hash), - format!("{:#x}", *sender), - format!("{nonce}"), - ) - }) - }) - .collect::>(); - - pending_txs.sort_by( - |(_tx_type_a, _, sender_a, nonce_a), (_tx_type_b, _, sender_b, nonce_b)| { - sender_a.cmp(sender_b).then(nonce_a.cmp(nonce_b)) - }, - ); - - Ok(pending_txs) - } -} - -impl StatefulWidget for &mut MempoolTable { - type State = TableState; - - fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) - where - Self: Sized, - { - let constraints = vec![ - Constraint::Length(10), // tx_type - Constraint::Length(HASH_LENGTH_IN_DIGITS), - Constraint::Length(ADDRESS_LENGTH_IN_DIGITS), - Constraint::Length(NUMBER_LENGTH_IN_DIGITS), - ]; - let rows = self.items.iter().map(|(tx_type, hash, sender, nonce)| { - Row::new(vec![ - Span::styled(tx_type, Style::default()), - Span::styled(hash, Style::default()), - Span::styled(sender, Style::default()), - Span::styled(nonce, Style::default()), - ]) - }); - let mempool_table = Table::new(rows, constraints) - .header(Row::new(vec!["Type", "Hash", "Sender", "Nonce"]).style(Style::default())) - .block( - Block::bordered() - .border_style(Style::default().fg(if self.selected { - Color::Magenta - } else { - Color::Cyan - })) - .title(Span::styled( - "Mempool", - Style::default().add_modifier(Modifier::BOLD), - )), - ); - - mempool_table.render(area, buf, state); - } -} - -impl SelectableScroller for MempoolTable { - fn selected(&mut self, is_selected: bool) { - self.selected = is_selected; - } - fn scroll_up(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some(selected.unwrap_or(0).saturating_sub(1)) - } - fn scroll_down(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some( - selected - .unwrap_or(0) - .saturating_add(1) - .min(self.items.len().saturating_sub(1)), - ) - } -} diff --git a/tooling/monitor/src/widget/mod.rs b/tooling/monitor/src/widget/mod.rs deleted file mode 100644 index 5205dad2eab..00000000000 --- a/tooling/monitor/src/widget/mod.rs +++ /dev/null @@ -1,40 +0,0 @@ -pub mod batches; -pub mod blocks; -pub mod chain_status; -pub mod l1_to_l2_messages; -pub mod l2_to_l1_messages; -pub mod mempool; -pub mod node_status; -pub mod rich_accounts; -pub mod tabs; - -pub use batches::BatchesTable; -pub use blocks::BlocksTable; -pub use chain_status::GlobalChainStatusTable; -pub use l1_to_l2_messages::L1ToL2MessagesTable; -pub use l2_to_l1_messages::L2ToL1MessagesTable; -pub use mempool::MempoolTable; -pub use node_status::NodeStatusTable; - -pub const ETHREX_LOGO: &str = r#" -███████╗████████╗██╗░░██╗██████╗░███████╗██╗░░██╗ -██╔════╝╚══██╔══╝██║░░██║██╔══██╗██╔════╝╚██╗██╔╝ -█████╗░░░░░██║░░░███████║██████╔╝█████╗░░░╚███╔╝░ -██╔══╝░░░░░██║░░░██╔══██║██╔══██╗██╔══╝░░░██╔██╗░ -███████╗░░░██║░░░██║░░██║██║░░██║███████╗██╔╝╚██╗ -╚══════╝░░░╚═╝░░░╚═╝░░╚═╝╚═╝░░╚═╝╚══════╝╚═╝░░╚═╝"#; - -pub const HASH_LENGTH_IN_DIGITS: u16 = 66; // 64 hex characters + 2 for "0x" prefix -pub const ADDRESS_LENGTH_IN_DIGITS: u16 = 42; // 40 hex characters + 2 for "0x" prefix -pub const NUMBER_LENGTH_IN_DIGITS: u16 = 9; // 1e8 -pub const TX_NUMBER_LENGTH_IN_DIGITS: u16 = 4; -pub const GAS_USED_LENGTH_IN_DIGITS: u16 = 8; // 1e7 -pub const BLOCK_SIZE_LENGTH_IN_DIGITS: u16 = 6; // 1e6 - -pub const LATEST_BLOCK_STATUS_TABLE_LENGTH_IN_DIGITS: u16 = NUMBER_LENGTH_IN_DIGITS - + TX_NUMBER_LENGTH_IN_DIGITS - + HASH_LENGTH_IN_DIGITS - + ADDRESS_LENGTH_IN_DIGITS - + GAS_USED_LENGTH_IN_DIGITS - + GAS_USED_LENGTH_IN_DIGITS - + BLOCK_SIZE_LENGTH_IN_DIGITS; diff --git a/tooling/monitor/src/widget/node_status.rs b/tooling/monitor/src/widget/node_status.rs deleted file mode 100644 index 15895f9c0d3..00000000000 --- a/tooling/monitor/src/widget/node_status.rs +++ /dev/null @@ -1,104 +0,0 @@ -use ethrex_l2_common::sequencer_state::SequencerState; -use ethrex_rpc::EthClient; -use ethrex_storage::Store; -use ratatui::{ - buffer::Buffer, - layout::{Constraint, Rect}, - style::{Color, Modifier, Style}, - text::Span, - widgets::{Block, Row, StatefulWidget, Table, TableState}, -}; - -use crate::error::MonitorError; - -#[derive(Clone)] -pub struct NodeStatusTable { - pub state: TableState, - pub items: [(String, String); 5], - sequencer_state: SequencerState, - is_based: bool, -} - -impl NodeStatusTable { - pub fn new(sequencer_state: SequencerState, is_based: bool) -> Self { - Self { - state: TableState::default(), - items: Default::default(), - sequencer_state, - is_based, - } - } - - pub async fn on_tick( - &mut self, - store: &Store, - l2_client: &EthClient, - ) -> Result<(), MonitorError> { - self.items = - Self::refresh_items(&self.sequencer_state, store, l2_client, self.is_based).await?; - Ok(()) - } - - async fn refresh_items( - sequencer_state: &SequencerState, - store: &Store, - l2_client: &EthClient, - is_based: bool, - ) -> Result<[(String, String); 5], MonitorError> { - let last_update = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); - let status = sequencer_state.status(); - let last_known_batch = "NaN"; // TODO: Implement last known batch retrieval - let last_known_block = store - .get_latest_block_number() - .await - .map_err(|_| MonitorError::GetLatestBlock)?; - let peers = if is_based { - Some(l2_client.peer_count().await?) - } else { - None - }; - - Ok([ - ("Last Update:".to_string(), last_update), - ("Status:".to_string(), status.to_string()), - ( - "Last Known Batch:".to_string(), - last_known_batch.to_string(), - ), - ( - "Last Known Block:".to_string(), - last_known_block.to_string(), - ), - match peers { - Some(peer_count) => ("Peers:".to_string(), peer_count.to_string()), - None => ("".to_string(), "".to_string()), - }, - ]) - } -} - -impl StatefulWidget for &mut NodeStatusTable { - type State = TableState; - - fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { - let constraints = vec![Constraint::Percentage(50), Constraint::Percentage(50)]; - - let rows = self.items.iter().map(|(key, value)| { - Row::new(vec![ - Span::styled(key, Style::default()), - Span::styled(value, Style::default()), - ]) - }); - - let node_status_table = Table::new(rows, constraints).block( - Block::bordered() - .border_style(Style::default().fg(Color::Cyan)) - .title(Span::styled( - "Node Status", - Style::default().add_modifier(Modifier::BOLD), - )), - ); - - node_status_table.render(area, buf, state); - } -} diff --git a/tooling/monitor/src/widget/rich_accounts.rs b/tooling/monitor/src/widget/rich_accounts.rs deleted file mode 100644 index ee13d9add88..00000000000 --- a/tooling/monitor/src/widget/rich_accounts.rs +++ /dev/null @@ -1,167 +0,0 @@ -use bytes::Bytes; -use ethrex_common::{Address, U256}; -use ethrex_config::networks::LOCAL_DEVNET_PRIVATE_KEYS; -use ethrex_l2_common::utils::get_address_from_secret_key; -use ethrex_rpc::{EthClient, types::block_identifier::BlockIdentifier}; -use hex::FromHexError; -use ratatui::{ - buffer::Buffer, - layout::{Constraint, Rect}, - style::{Color, Modifier, Style}, - text::Span, - widgets::{Block, Row, StatefulWidget, Table, TableState}, -}; -use secp256k1::SecretKey; - -use crate::{error::MonitorError, utils::SelectableScroller, widget::HASH_LENGTH_IN_DIGITS}; - -// address | private key | balance -pub type RichAccountRow = (Address, SecretKey, U256); - -#[derive(Clone, Default)] -pub struct RichAccountsTable { - pub state: TableState, - pub items: Vec, - last_block_fetched: u64, - - selected: bool, -} - -impl RichAccountsTable { - pub async fn new(rollup_client: &EthClient) -> Result { - let last_block_fetched = rollup_client - .get_block_number() - .await - .map_err(|_| MonitorError::GetLatestBlock)?; - let items = Self::get_accounts(rollup_client, last_block_fetched).await?; - Ok(Self { - items, - last_block_fetched, - selected: true, - ..Default::default() - }) - } - async fn get_accounts( - rollup_client: &EthClient, - last_block_fetched: u64, - ) -> Result, MonitorError> { - // TODO: enable custom private keys - let private_keys: Vec = LOCAL_DEVNET_PRIVATE_KEYS - .lines() - .filter(|line| !line.trim().is_empty()) - .map(|line| line.trim().to_string()) - .collect(); - - let mut accounts = Vec::with_capacity(private_keys.len()); - for pk in private_keys.iter() { - let secret_key = SecretKey::from_slice(&parse_hex(pk)?) - .map_err(|e| MonitorError::DecodingError(format!("Invalid private key: {e}")))?; - let address = get_address_from_secret_key(&secret_key.secret_bytes()).map_err(|e| { - MonitorError::DecodingError(format!("Failed to get address from private key: {e}")) - })?; - let get_balance = rollup_client - .get_balance(address, BlockIdentifier::Number(last_block_fetched)) - .await?; - accounts.push((address, secret_key, get_balance)); - } - Ok(accounts) - } - - pub async fn on_tick(&mut self, rollup_client: &EthClient) -> Result<(), MonitorError> { - let latest_block = rollup_client - .get_block_number() - .await - .map_err(|_| MonitorError::GetLatestBlock)?; - if latest_block == self.last_block_fetched { - return Ok(()); - } - for (address, _private_key, balance) in self.items.iter_mut() { - *balance = rollup_client - .get_balance(*address, BlockIdentifier::Number(latest_block)) - .await?; - } - self.last_block_fetched = latest_block; - Ok(()) - } -} - -pub fn parse_hex(s: &str) -> Result { - match s.strip_prefix("0x") { - Some(s) => hex::decode(s).map(Into::into), - None => hex::decode(s).map(Into::into), - } -} - -impl StatefulWidget for &mut RichAccountsTable { - type State = TableState; - - fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) - where - Self: Sized, - { - let constraints = vec![ - Constraint::Fill(1), - Constraint::Length(HASH_LENGTH_IN_DIGITS), - Constraint::Fill(1), - ]; - - let selected_index = state.selected(); - - let rows = self - .items - .iter() - .enumerate() - .map(|(i, (address, private_key, balance))| { - let mut row = Row::new(vec![ - Span::styled(format!("0x{address:x}"), Style::default()), - Span::styled( - format!("0x{}", private_key.display_secret()), - Style::default(), - ), - Span::styled(balance.to_string(), Style::default()), - ]); - - if Some(i) == selected_index { - row = row.style(Style::default().bg(Color::Blue)); - } - - row - }); - - let rich_accounts_table = Table::new(rows, constraints) - .header(Row::new(vec!["Address", "Private Key", "Balance"]).style(Style::default())) - .block( - Block::bordered() - .border_style(Style::default().fg(if self.selected { - Color::Magenta - } else { - Color::Cyan - })) - .title(Span::styled( - "Rich Accounts", - Style::default().add_modifier(Modifier::BOLD), - )), - ); - - rich_accounts_table.render(area, buf, state); - } -} - -impl SelectableScroller for RichAccountsTable { - fn selected(&mut self, is_selected: bool) { - self.selected = is_selected; - } - fn scroll_up(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some(selected.unwrap_or(0).saturating_sub(1)) - } - fn scroll_down(&mut self) { - let selected = self.state.selected_mut(); - *selected = Some( - selected - .unwrap_or(0) - .saturating_add(1) - .min(self.items.len().saturating_sub(1)), - ) - } -} diff --git a/tooling/monitor/src/widget/tabs.rs b/tooling/monitor/src/widget/tabs.rs deleted file mode 100644 index debbba1cc32..00000000000 --- a/tooling/monitor/src/widget/tabs.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::fmt::Display; - -#[derive(Debug, Clone, Default)] -pub enum TabsState { - #[default] - Overview = 0, - Logs = 1, - RichAccounts = 2, -} - -impl TabsState { - pub fn next(&mut self) { - match self { - TabsState::Overview => *self = TabsState::Logs, - TabsState::Logs => *self = TabsState::RichAccounts, - TabsState::RichAccounts => *self = TabsState::Overview, - } - } - - pub fn previous(&mut self) { - match self { - TabsState::Overview => *self = TabsState::Logs, - TabsState::Logs => *self = TabsState::Overview, - TabsState::RichAccounts => *self = TabsState::Logs, - } - } -} - -impl Display for TabsState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - TabsState::Overview => write!(f, "Overview"), - TabsState::Logs => write!(f, "Logs"), - TabsState::RichAccounts => write!(f, "Rich Accounts"), - } - } -} - -impl From for Option { - fn from(state: TabsState) -> Self { - match state { - TabsState::Overview => Some(0), - TabsState::Logs => Some(1), - TabsState::RichAccounts => Some(2), - } - } -} diff --git a/tooling/reorgs/Cargo.toml b/tooling/reorgs/Cargo.toml deleted file mode 100644 index 0364281c1db..00000000000 --- a/tooling/reorgs/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "reorgs" -version.workspace = true -edition.workspace = true -license.workspace = true - -[dependencies] -ethrex = { workspace = true, features = ["l2"] } -ethrex-common.workspace = true -ethrex-blockchain.workspace = true -ethrex-rpc.workspace = true -ethrex-config.workspace = true -ethrex-l2-common.workspace = true -ethrex-l2-rpc.workspace = true -ethrex-p2p.workspace = true - -tokio.workspace = true -tokio-util.workspace = true -tracing.workspace = true -rand.workspace = true -sha2.workspace = true -hex.workspace = true -nix = { version = "0.30", features = ["signal"] } -secp256k1.workspace = true -url.workspace = true diff --git a/tooling/reorgs/README.md b/tooling/reorgs/README.md deleted file mode 100644 index 37688a95409..00000000000 --- a/tooling/reorgs/README.md +++ /dev/null @@ -1,23 +0,0 @@ -# Reorg integration tests - -This directory contains tests for chain reorganization. - -## How to run - -First, compile the `ethrex` binary if you haven't already: - -```bash -cargo build --workspace --bin ethrex -``` - -Then, run the reorg tests using: - -```bash -cargo run -``` - -You can run a custom binary by specifying the path: - -```bash -cargo run -- /path/to/your/binary -``` diff --git a/tooling/reorgs/src/main.rs b/tooling/reorgs/src/main.rs deleted file mode 100644 index 866f5abb473..00000000000 --- a/tooling/reorgs/src/main.rs +++ /dev/null @@ -1,409 +0,0 @@ -use std::{ - path::{Path, PathBuf}, - process::Command, - sync::Arc, -}; - -use ethrex::{cli::Options, initializers::init_tracing}; -use ethrex_common::U256; -use ethrex_l2_rpc::signer::{LocalSigner, Signer}; -use tokio::sync::Mutex; -use tracing::{error, info, warn}; - -use crate::simulator::Simulator; - -mod simulator; - -#[tokio::main] -async fn main() { - // Setup logging - init_tracing(&Options::default_l1()); - - // Fetch the path to the ethrex binary from the command line arguments - // If not provided, use the default path - let cmd_path: PathBuf = std::env::args() - .nth(1) - .map(|o| o.parse().unwrap()) - .unwrap_or_else(|| "../../target/debug/ethrex".parse().unwrap()); - - let version = get_ethrex_version(&cmd_path).await; - - info!(%version, binary_path = %cmd_path.display(), "Fetched ethrex binary version"); - info!("Starting test run"); - info!(""); - - run_test(&cmd_path, no_reorgs_full_sync_smoke_test).await; - run_test(&cmd_path, test_reorg_back_to_base).await; - - run_test(&cmd_path, test_chain_split).await; - - run_test(&cmd_path, test_one_block_reorg_and_back).await; - run_test(&cmd_path, test_reorg_back_to_base_with_common_ancestor).await; - run_test(&cmd_path, test_storage_slots_reorg).await; - run_test(&cmd_path, test_many_blocks_reorg).await; -} - -async fn get_ethrex_version(cmd_path: &Path) -> String { - let version_output = Command::new(cmd_path) - .arg("--version") - .output() - .expect("failed to get ethrex version"); - String::from_utf8(version_output.stdout).expect("failed to parse version output") -} - -async fn run_test(cmd_path: &Path, test_fn: F) -where - F: Fn(Arc>) -> Fut, - Fut: Future + Send + 'static, -{ - let test_name = std::any::type_name::(); - let start = std::time::Instant::now(); - - info!(test=%test_name, "Running test"); - let simulator = Arc::new(Mutex::new(Simulator::new( - cmd_path.to_path_buf(), - test_name.to_string(), - ))); - - // Run in another task to clean up properly on panic - let result = tokio::spawn(test_fn(simulator.clone())).await; - - simulator.lock_owned().await.stop().await; - - match result { - Ok(_) => info!(test=%test_name, elapsed=?start.elapsed(), "test completed successfully"), - Err(err) if err.is_panic() => { - error!(test=%test_name, %err, "test panicked"); - std::process::exit(1); - } - Err(err) => { - warn!(test=%test_name, %err, "test task was cancelled"); - } - } - // Add a blank line after each test for readability - info!(""); -} - -async fn no_reorgs_full_sync_smoke_test(simulator: Arc>) { - let mut simulator = simulator.lock().await; - - // Start two ethrex nodes - let node0 = simulator.start_node().await; - let node1 = simulator.start_node().await; - - // Create a chain and extend it with a few empty blocks - let base_chain = node0.extend_chain(simulator.get_base_chain(), 10).await; - - // Try to fully sync node1 (which is a peer of node0) - node1.update_forkchoice(&base_chain).await; -} - -async fn test_reorg_back_to_base(simulator: Arc>) { - let mut simulator = simulator.lock().await; - - // Start two ethrex nodes - let node0 = simulator.start_node().await; // Test_node - let node1 = simulator.start_node().await; - - let base_chain = simulator.get_base_chain(); - let side_chain = base_chain.fork(); - // Create a chain and extend it with a few empty blocks - let base_chain = node1.extend_chain(base_chain, 10).await; - - // Create another chain (a fork of the first one) and extend it with a few empty blocks - let _ = node0.extend_chain(side_chain, 10).await; - - // Try to fully sync node0 to base chain - node0.update_forkchoice(&base_chain).await; -} - -async fn test_reorg_back_to_base_with_common_ancestor(simulator: Arc>) { - let mut simulator = simulator.lock().await; - - // Start two ethrex nodes - let node0 = simulator.start_node().await; // Test_node - let node1 = simulator.start_node().await; - - let base_chain = simulator.get_base_chain(); - // Create a chain and extend it with a few empty blocks - let base_chain = node1.extend_chain(base_chain, 10).await; - - // Update node0 to the base chain - node0.update_forkchoice(&base_chain).await; - - let side_chain = base_chain.fork(); - // Extend the base chain - let base_chain = node1.extend_chain(base_chain, 10).await; - // Create another chain (a fork of the first one) and extend it with a few empty blocks - let _ = node0.extend_chain(side_chain, 10).await; - - // Try to fully sync node0 to base chain - node0.update_forkchoice(&base_chain).await; -} - -async fn test_chain_split(simulator: Arc>) { - let mut simulator = simulator.lock().await; - - // Start three ethrex nodes - let node0 = simulator.start_node().await; // Test_node - let node1 = simulator.start_node().await; - let node2 = simulator.start_node().await; - - let base_chain = simulator.get_base_chain(); - let side_chain = base_chain.fork(); - // Create a chain and extend it with a few empty blocks - let base_chain = node1.extend_chain(base_chain, 10).await; - - // Create another chain (a fork of the first one) and extend it with a few empty blocks - let _ = node2.extend_chain(side_chain, 10).await; - - // Try to fully sync node0 to base chain (which is a peer of node1 and node2) - // It will ask peer1 sometimes and peer2 others, it has to work with both - // So if one fails, this test will fail 50% of the time - node0.update_forkchoice(&base_chain).await; -} - -async fn test_one_block_reorg_and_back(simulator: Arc>) { - let mut simulator = simulator.lock().await; - let signer: Signer = LocalSigner::new( - "941e103320615d394a55708be13e45994c7d93b932b064dbcb2b511fe3254e2e" - .parse() - .unwrap(), - ) - .into(); - // Some random address - let recipient = "941e103320615d394a55708be13e45994c7d93b0".parse().unwrap(); - let transfer_amount = 1000000; - - let node0 = simulator.start_node().await; - let node1 = simulator.start_node().await; - - // Create a chain with a few empty blocks - let mut base_chain = simulator.get_base_chain(); - for _ in 0..10 { - let extended_base_chain = node0.build_payload(base_chain).await; - node0.notify_new_payload(&extended_base_chain).await; - node0.update_forkchoice(&extended_base_chain).await; - - node1.notify_new_payload(&extended_base_chain).await; - node1.update_forkchoice(&extended_base_chain).await; - base_chain = extended_base_chain; - } - - let initial_balance = node0.get_balance(recipient).await; - - // Fork the chain - let side_chain = base_chain.fork(); - - // Mine a new block in the base chain - let base_chain = node0.build_payload(base_chain).await; - node0.notify_new_payload(&base_chain).await; - node0.update_forkchoice(&base_chain).await; - - // Mine a new block in the base chain (but don't announce it yet) - let extended_base_chain = node0.build_payload(base_chain).await; - - // In parallel, mine a block in the side chain, with an ETH transfer - node1 - .send_eth_transfer(&signer, recipient, transfer_amount) - .await; - - let side_chain = node1.build_payload(side_chain).await; - node1.notify_new_payload(&side_chain).await; - node1.update_forkchoice(&side_chain).await; - - // Sanity check: balance hasn't changed - let same_balance = node0.get_balance(recipient).await; - assert_eq!(same_balance, initial_balance); - - // Notify the first node of the side chain block, it should reorg - node0.notify_new_payload(&side_chain).await; - node0.update_forkchoice(&side_chain).await; - - // Check the transfer has been processed - let new_balance = node0.get_balance(recipient).await; - assert_eq!(new_balance, initial_balance + transfer_amount); - - // Finally, move to the extended base chain, it should reorg back - node0.notify_new_payload(&extended_base_chain).await; - node0.update_forkchoice(&extended_base_chain).await; - - // Check the transfer has been reverted - let new_balance = node0.get_balance(recipient).await; - assert_eq!(new_balance, initial_balance); -} - -async fn test_many_blocks_reorg(simulator: Arc>) { - let mut simulator = simulator.lock().await; - let signer: Signer = LocalSigner::new( - "941e103320615d394a55708be13e45994c7d93b932b064dbcb2b511fe3254e2e" - .parse() - .unwrap(), - ) - .into(); - // Some random address - let recipient = "941e103320615d394a55708be13e45994c7d93b0".parse().unwrap(); - let transfer_amount = 1000000; - - let node0 = simulator.start_node().await; - let node1 = simulator.start_node().await; - - // Create a chain with a few empty blocks - let mut base_chain = simulator.get_base_chain(); - for _ in 0..10 { - let extended_base_chain = node0.build_payload(base_chain).await; - node0.notify_new_payload(&extended_base_chain).await; - node0.update_forkchoice(&extended_base_chain).await; - - node1.notify_new_payload(&extended_base_chain).await; - node1.update_forkchoice(&extended_base_chain).await; - base_chain = extended_base_chain; - } - - let initial_balance = node0.get_balance(recipient).await; - - // Fork the chain - let mut side_chain = base_chain.fork(); - - // Create a side chain with multiple blocks only known to node0 - for _ in 0..10 { - side_chain = node0.build_payload(side_chain).await; - node0.notify_new_payload(&side_chain).await; - node0.update_forkchoice(&side_chain).await; - } - - // Sanity check: balance hasn't changed - let same_balance = node0.get_balance(recipient).await; - assert_eq!(same_balance, initial_balance); - - // Advance the base chain with multiple blocks only known to node1 - for _ in 0..10 { - base_chain = node1.build_payload(base_chain).await; - node1.notify_new_payload(&base_chain).await; - node1.update_forkchoice(&base_chain).await; - } - - // Sanity check: balance hasn't changed - let same_balance = node0.get_balance(recipient).await; - assert_eq!(same_balance, initial_balance); - - // Advance the side chain with one more block and an ETH transfer - node1 - .send_eth_transfer(&signer, recipient, transfer_amount) - .await; - base_chain = node1.build_payload(base_chain).await; - node1.notify_new_payload(&base_chain).await; - node1.update_forkchoice(&base_chain).await; - - // Bring node0 again to the base chain, it should reorg - node0.notify_new_payload(&base_chain).await; - node0.update_forkchoice(&base_chain).await; - - // Check the transfer has been processed - let new_balance = node0.get_balance(recipient).await; - assert_eq!(new_balance, initial_balance + transfer_amount); -} - -async fn test_storage_slots_reorg(simulator: Arc>) { - let mut simulator = simulator.lock().await; - // Initcode for deploying a contract that receives two `bytes32` parameters and sets `storage[param0] = param1` - let contract_deploy_bytecode = hex::decode("656020355f35555f526006601af3").unwrap().into(); - let signer: Signer = LocalSigner::new( - "941e103320615d394a55708be13e45994c7d93b932b064dbcb2b511fe3254e2e" - .parse() - .unwrap(), - ) - .into(); - - let slot_key0 = U256::from(42); - let slot_value0 = U256::from(1163); - let slot_key1 = U256::from(25); - let slot_value1 = U256::from(7474); - - let node0 = simulator.start_node().await; - let node1 = simulator.start_node().await; - - // Create a chain with a few empty blocks - let mut base_chain = simulator.get_base_chain(); - - // Send a deploy tx for a contract which receives: `(bytes32 key, bytes32 value)` as parameters - let contract_address = node0 - .send_contract_deploy(&signer, contract_deploy_bytecode) - .await; - - for _ in 0..10 { - let extended_base_chain = node0.build_payload(base_chain).await; - node0.notify_new_payload(&extended_base_chain).await; - node0.update_forkchoice(&extended_base_chain).await; - - node1.notify_new_payload(&extended_base_chain).await; - node1.update_forkchoice(&extended_base_chain).await; - base_chain = extended_base_chain; - } - - // Sanity check: storage slots are initially empty - let initial_value = node0.get_storage_at(contract_address, slot_key0).await; - assert_eq!(initial_value, U256::zero()); - let initial_value = node0.get_storage_at(contract_address, slot_key1).await; - assert_eq!(initial_value, U256::zero()); - - // Fork the chain - let mut side_chain = base_chain.fork(); - - // Create a side chain with multiple blocks only known to node0 - for _ in 0..10 { - side_chain = node0.build_payload(side_chain).await; - node0.notify_new_payload(&side_chain).await; - node0.update_forkchoice(&side_chain).await; - } - - // Advance the base chain with multiple blocks only known to node1 - for _ in 0..10 { - base_chain = node1.build_payload(base_chain).await; - node1.notify_new_payload(&base_chain).await; - node1.update_forkchoice(&base_chain).await; - } - - // Set a storage slot in the contract in node0 - let calldata0 = [slot_key0.to_big_endian(), slot_value0.to_big_endian()] - .concat() - .into(); - node0.send_call(&signer, contract_address, calldata0).await; - - // Set another storage slot in the contract in node1 - let calldata1 = [slot_key1.to_big_endian(), slot_value1.to_big_endian()] - .concat() - .into(); - node1.send_call(&signer, contract_address, calldata1).await; - - // Build a block in the side chain - side_chain = node0.build_payload(side_chain).await; - node0.notify_new_payload(&side_chain).await; - node0.update_forkchoice(&side_chain).await; - - // Build a block in the base chain - base_chain = node1.build_payload(base_chain).await; - node1.notify_new_payload(&base_chain).await; - node1.update_forkchoice(&base_chain).await; - - // Assert the storage slots are as expected in both forks - let value_slot0 = node0.get_storage_at(contract_address, slot_key0).await; - assert_eq!(value_slot0, slot_value0); - let value_slot1 = node0.get_storage_at(contract_address, slot_key1).await; - assert_eq!(value_slot1, U256::zero()); - - let value_slot0 = node1.get_storage_at(contract_address, slot_key0).await; - assert_eq!(value_slot0, U256::zero()); - let value_slot1 = node1.get_storage_at(contract_address, slot_key1).await; - assert_eq!(value_slot1, slot_value1); - - // Reorg the node0 to the base chain - node0.notify_new_payload(&base_chain).await; - node0.update_forkchoice(&base_chain).await; - - // Check the storage slots are as expected after the reorg - let value_slot0 = node0.get_storage_at(contract_address, slot_key0).await; - assert_eq!(value_slot0, U256::zero()); - let value_slot1 = node0.get_storage_at(contract_address, slot_key1).await; - assert_eq!(value_slot1, slot_value1); -} diff --git a/tooling/reorgs/src/simulator.rs b/tooling/reorgs/src/simulator.rs deleted file mode 100644 index 73145490c43..00000000000 --- a/tooling/reorgs/src/simulator.rs +++ /dev/null @@ -1,578 +0,0 @@ -use std::{ - fs::File, - io::Read, - path::PathBuf, - process::Stdio, - sync::atomic::AtomicU16, - time::{Duration, SystemTime}, -}; - -use ethrex::{cli::Options, initializers::get_network}; -use ethrex_common::{ - Address, Bytes, H160, H256, U256, - evm::calculate_create_address, - types::{ - Block, EIP1559Transaction, Genesis, Transaction, TxKind, requests::compute_requests_hash, - }, -}; -use ethrex_config::networks::Network; -use ethrex_l2_rpc::signer::{Signable, Signer}; -use ethrex_p2p::snap::constants::PEER_REPLY_TIMEOUT; -use ethrex_p2p::sync::SyncMode; -use ethrex_rpc::{ - EngineClient, EthClient, - types::{ - block_identifier::{BlockIdentifier, BlockTag}, - fork_choice::{ForkChoiceState, PayloadAttributesV3}, - payload::{ExecutionPayload, PayloadValidationStatus}, - }, -}; -use nix::sys::signal::{self, Signal}; -use nix::unistd::Pid; -use sha2::{Digest, Sha256}; -use tokio::process::Command; -use tokio_util::sync::CancellationToken; -use tracing::{error, info}; -use url::Url; - -pub struct Simulator { - cmd_path: PathBuf, - test_name: String, - - base_opts: Options, - jwt_secret: Bytes, - genesis_path: PathBuf, - configs: Vec, - enodes: Vec, - cancellation_tokens: Vec<(CancellationToken, tokio::task::JoinHandle<()>)>, -} - -impl Simulator { - pub fn new(cmd_path: PathBuf, test_name: String) -> Self { - let mut opts = Options::default_l1(); - let jwt_secret = generate_jwt_secret(); - std::fs::write("jwt.hex", hex::encode(&jwt_secret)).unwrap(); - - let genesis_path = std::path::absolute("../../fixtures/genesis/l1.json") - .unwrap() - .canonicalize() - .unwrap(); - - opts.authrpc_jwtsecret = "jwt.hex".to_string(); - opts.dev = false; - opts.http_addr = "localhost".to_string(); - opts.authrpc_addr = "localhost".to_string(); - opts.network = Some(Network::GenesisPath(genesis_path.clone())); - Self { - cmd_path, - test_name, - base_opts: opts, - genesis_path, - jwt_secret, - configs: vec![], - cancellation_tokens: vec![], - enodes: vec![], - } - } - - pub fn get_base_chain(&self) -> Chain { - let network = get_network(&self.base_opts); - let genesis = network.get_genesis().unwrap(); - Chain::new(genesis) - } - - pub async fn start_node(&mut self) -> Node { - let n = self.configs.len(); - let test_name = &self.test_name; - info!(node = n, "Starting node"); - let mut opts = self.base_opts.clone(); - opts.datadir = format!("data/{test_name}/node{n}").into(); - - opts.http_port = get_next_port().to_string(); - opts.authrpc_port = get_next_port().to_string(); - - // These are one TCP and one UDP - let p2p_port = get_next_port(); - opts.p2p_port = p2p_port.to_string(); - opts.discovery_port = p2p_port.to_string(); - - opts.syncmode = SyncMode::Full; - - if opts.datadir.exists() { - std::fs::remove_dir_all(&opts.datadir) - .expect("Failed to remove existing data directory"); - } - std::fs::create_dir_all(&opts.datadir).expect("Failed to create data directory"); - - let now = SystemTime::now() - .duration_since(SystemTime::UNIX_EPOCH) - .unwrap() - .as_secs(); - let logs_file_path = format!("data/{test_name}/node{n}_{now}.log"); - let logs_file = File::create(&logs_file_path).expect("Failed to create logs file"); - - let cancel = CancellationToken::new(); - - self.configs.push(opts.clone()); - - let mut cmd = Command::new(&self.cmd_path); - cmd.args([ - format!("--http.addr={}", opts.http_addr), - format!("--http.port={}", opts.http_port), - format!("--authrpc.addr={}", opts.authrpc_addr), - format!("--authrpc.port={}", opts.authrpc_port), - format!("--p2p.port={}", opts.p2p_port), - format!("--discovery.port={}", opts.discovery_port), - format!("--datadir={}", opts.datadir.display()), - format!("--network={}", self.genesis_path.display()), - format!("--syncmode={:?}", opts.syncmode).to_lowercase(), - "--force".to_string(), - ]) - .stdin(Stdio::null()) - .stdout(logs_file.try_clone().unwrap()) - .stderr(logs_file); - - if !self.enodes.is_empty() { - cmd.arg(format!("--bootnodes={}", self.enodes.join(","))); - } - - let child = cmd.spawn().expect("Failed to start ethrex process"); - - let logs_file = File::open(&logs_file_path).expect("Failed to open logs file"); - let enode = - tokio::time::timeout(Duration::from_secs(5), wait_for_initialization(logs_file)) - .await - .expect("node initialization timed out"); - self.enodes.push(enode); - - let waiter = tokio::spawn({ - let cancel = cancel.clone(); - async move { - let mut child = child; - tokio::select! { - _ = cancel.cancelled() => { - if let Some(pid) = child.id() { - // NOTE: we use SIGTERM instead of child.kill() so sockets are closed - signal::kill(Pid::from_raw(pid as i32), Signal::SIGTERM).unwrap(); - } - } - res = child.wait() => { - assert!(res.unwrap().success()); - } - } - // Ignore any errors on shutdown - let _ = child.wait().await.unwrap(); - } - }); - self.cancellation_tokens.push((cancel, waiter)); - - info!( - "Started node {n} at http://{}:{}", - opts.http_addr, opts.http_port - ); - - self.get_node(n) - } - - pub async fn stop(&mut self) { - for (token, waiter) in self.cancellation_tokens.drain(..) { - token.cancel(); - waiter.await.unwrap(); - } - self.enodes.clear(); - self.configs.clear(); - } - - fn get_http_url(&self, index: usize) -> Url { - let opts = &self.configs[index]; - Url::parse(&format!("http://{}:{}", opts.http_addr, opts.http_port)) - .expect("Error parsing RPC URL") - } - - fn get_auth_url(&self, index: usize) -> String { - let opts = &self.configs[index]; - format!("http://{}:{}", opts.authrpc_addr, opts.authrpc_port) - } - - fn get_node(&self, index: usize) -> Node { - let auth_url = self.get_auth_url(index); - let engine_client = EngineClient::new(&auth_url, self.jwt_secret.clone()); - - let http_url = self.get_http_url(index); - let rpc_client = EthClient::new(http_url).unwrap(); - - Node { - index, - engine_client, - rpc_client, - } - } -} - -/// Waits until the node is initialized by reading its logs. -/// Returns the enode URL of the node. -async fn wait_for_initialization(mut logs_file: File) -> String { - const NODE_STARTED_LOG: &str = "Starting Auth-RPC server at"; - - let mut file_contents = String::new(); - - // Wait a bit until the node starts - loop { - tokio::time::sleep(Duration::from_millis(100)).await; - - logs_file.read_to_string(&mut file_contents).unwrap(); - - if file_contents.contains(NODE_STARTED_LOG) { - break; - } - } - let node_enode_log = file_contents - .lines() - .find(|line| line.contains("Local node initialized")) - .unwrap(); - // Look for the "enode://node_id@host:port" part - let prefix = "enode://"; - let node_enode = node_enode_log.split_once(prefix).unwrap().1; - format!("{prefix}{}", node_enode.trim_end()) -} - -pub struct Node { - index: usize, - engine_client: EngineClient, - rpc_client: EthClient, -} - -impl Node { - pub async fn update_forkchoice(&self, chain: &Chain) { - let fork_choice_state = chain.get_fork_choice_state(); - info!( - node = self.index, - head = %fork_choice_state.head_block_hash, - "Updating fork choice" - ); - let syncing_fut = wait_until_synced(&self.engine_client, fork_choice_state); - - // Needs to be at least 2x the p2p peer reply timeout so that if the - // node queries the wrong peer first (e.g. one with a different chain), - // it has time to retry with the correct peer. Extra 10s of slack for - // slow CI environments. - tokio::time::timeout( - PEER_REPLY_TIMEOUT * 2 + Duration::from_secs(10), - syncing_fut, - ) - .await - .inspect_err(|_| { - error!(node = self.index, "Timed out waiting for node to sync"); - }) - .expect("timed out waiting for node to sync"); - } - - pub async fn build_payload(&self, mut chain: Chain) -> Chain { - let fork_choice_state = chain.get_fork_choice_state(); - let mut payload_attributes = chain.get_next_payload_attributes(); - // Set index as fee recipient to differentiate between nodes - payload_attributes.suggested_fee_recipient = H160::from_low_u64_be(self.index as u64); - let head = fork_choice_state.head_block_hash; - - let parent_beacon_block_root = payload_attributes.parent_beacon_block_root; - - info!( - node = self.index, - %head, - "Starting payload build" - ); - - let fork_choice_response = self - .engine_client - .engine_forkchoice_updated_v3(fork_choice_state, Some(payload_attributes)) - .await - .unwrap(); - - assert_eq!( - fork_choice_response.payload_status.status, - PayloadValidationStatus::Valid, - "Validation failed with error: {:?}", - fork_choice_response.payload_status.validation_error - ); - let payload_id = fork_choice_response.payload_id.unwrap(); - - let payload_response = self - .engine_client - .engine_get_payload_v5(payload_id) - .await - .unwrap(); - - let requests_hash = compute_requests_hash(&payload_response.execution_requests.unwrap()); - let block_access_list_hash = payload_response - .execution_payload - .block_access_list - .as_ref() - .map(|bal| bal.compute_hash()); - let block = payload_response - .execution_payload - .into_block( - parent_beacon_block_root, - Some(requests_hash), - block_access_list_hash, - ) - .unwrap(); - - info!( - node = self.index, - %head, - block = %block.hash(), - "#txs"=%block.body.transactions.len(), - "Built payload" - ); - chain.append_block(block); - chain - } - - pub async fn extend_chain(&self, mut chain: Chain, num_blocks: usize) -> Chain { - for _ in 0..num_blocks { - chain = self.build_payload(chain).await; - self.notify_new_payload(&chain).await; - } - self.update_forkchoice(&chain).await; - chain - } - - pub async fn notify_new_payload(&self, chain: &Chain) { - let head = chain.blocks.last().unwrap(); - let execution_payload = ExecutionPayload::from_block(head.clone(), None); - // Support blobs - // let commitments = execution_payload_response - // .blobs_bundle - // .unwrap_or_default() - // .commitments - // .iter() - // .map(|commitment| { - // let mut hash = keccak256(commitment).0; - // // https://eips.ethereum.org/EIPS/eip-4844 -> kzg_to_versioned_hash - // hash[0] = 0x01; - // H256::from_slice(&hash) - // }) - // .collect(); - let commitments = vec![]; - let parent_beacon_block_root = head.header.parent_beacon_block_root.unwrap(); - let _payload_status = self - .engine_client - .engine_new_payload_v4(execution_payload, commitments, parent_beacon_block_root) - .await - .unwrap(); - } - - pub async fn send_eth_transfer(&self, signer: &Signer, recipient: H160, amount: u64) { - info!(node = self.index, sender=%signer.address(), %recipient, amount, "Sending ETH transfer tx"); - let chain_id = self - .rpc_client - .get_chain_id() - .await - .unwrap() - .try_into() - .unwrap(); - let sender_address = signer.address(); - let nonce = self - .rpc_client - .get_nonce(sender_address, BlockIdentifier::Tag(BlockTag::Latest)) - .await - .unwrap(); - let tx = EIP1559Transaction { - chain_id, - nonce, - max_priority_fee_per_gas: 0, - max_fee_per_gas: 1_000_000_000, - gas_limit: 50_000, - to: TxKind::Call(recipient), - value: amount.into(), - ..Default::default() - }; - let mut tx = Transaction::EIP1559Transaction(tx); - tx.sign_inplace(signer).await.unwrap(); - let encoded_tx = tx.encode_canonical_to_vec(); - self.rpc_client - .send_raw_transaction(&encoded_tx) - .await - .unwrap(); - } - - pub async fn send_call(&self, signer: &Signer, contract: H160, data: Bytes) { - info!(node = self.index, sender=%signer.address(), %contract, "Sending contract call"); - let chain_id = self - .rpc_client - .get_chain_id() - .await - .unwrap() - .try_into() - .unwrap(); - let sender_address = signer.address(); - let nonce = self - .rpc_client - .get_nonce(sender_address, BlockIdentifier::Tag(BlockTag::Latest)) - .await - .unwrap(); - let tx = EIP1559Transaction { - chain_id, - nonce, - max_priority_fee_per_gas: 0, - max_fee_per_gas: 1_000_000_000, - gas_limit: 50_000, - to: TxKind::Call(contract), - data, - ..Default::default() - }; - let mut tx = Transaction::EIP1559Transaction(tx); - tx.sign_inplace(signer).await.unwrap(); - let encoded_tx = tx.encode_canonical_to_vec(); - self.rpc_client - .send_raw_transaction(&encoded_tx) - .await - .unwrap(); - } - - pub async fn send_contract_deploy( - &self, - signer: &Signer, - contract_deploy_bytecode: Bytes, - ) -> Address { - info!(node = self.index, sender=%signer.address(), "Deploying contract"); - let chain_id = self - .rpc_client - .get_chain_id() - .await - .unwrap() - .try_into() - .unwrap(); - let sender_address = signer.address(); - let nonce = self - .rpc_client - .get_nonce(sender_address, BlockIdentifier::Tag(BlockTag::Latest)) - .await - .unwrap(); - let tx = EIP1559Transaction { - chain_id, - nonce, - max_priority_fee_per_gas: 0, - max_fee_per_gas: 1_000_000_000, - gas_limit: 100_000, - to: TxKind::Create, - data: contract_deploy_bytecode, - ..Default::default() - }; - let mut tx = Transaction::EIP1559Transaction(tx); - tx.sign_inplace(signer).await.unwrap(); - let encoded_tx = tx.encode_canonical_to_vec(); - self.rpc_client - .send_raw_transaction(&encoded_tx) - .await - .unwrap(); - - calculate_create_address(sender_address, nonce) - } - - pub async fn get_balance(&self, address: H160) -> U256 { - self.rpc_client - .get_balance(address, Default::default()) - .await - .unwrap() - } - - pub async fn get_storage_at(&self, address: H160, key: U256) -> U256 { - self.rpc_client - .get_storage_at(address, key, Default::default()) - .await - .unwrap() - } -} - -#[derive(Debug)] -pub struct Chain { - block_hashes: Vec, - blocks: Vec, - safe_height: usize, -} - -impl Chain { - fn new(genesis: Genesis) -> Self { - let genesis_block = genesis.get_block(); - Self { - block_hashes: vec![genesis_block.hash()], - blocks: vec![genesis_block], - safe_height: 0, - } - } - - fn append_block(&mut self, block: Block) { - self.block_hashes.push(block.hash()); - self.blocks.push(block); - } - - pub fn fork(&self) -> Self { - Self { - block_hashes: self.block_hashes.clone(), - blocks: self.blocks.clone(), - safe_height: self.safe_height, - } - } - - fn get_fork_choice_state(&self) -> ForkChoiceState { - let head_block_hash = *self.block_hashes.last().unwrap(); - let finalized_block_hash = self.block_hashes[self.safe_height]; - ForkChoiceState { - head_block_hash, - safe_block_hash: finalized_block_hash, - finalized_block_hash, - } - } - - fn get_next_payload_attributes(&self) -> PayloadAttributesV3 { - let timestamp = self.blocks.last().unwrap().header.timestamp + 12; - let head_hash = self.get_fork_choice_state().head_block_hash; - // Generate dummy values by hashing multiple times - let parent_beacon_block_root = keccak256(&head_hash.0); - let prev_randao = keccak256(&parent_beacon_block_root.0); - let suggested_fee_recipient = Default::default(); - // TODO: add withdrawals - let withdrawals = vec![]; - PayloadAttributesV3 { - timestamp, - prev_randao, - suggested_fee_recipient, - parent_beacon_block_root: Some(parent_beacon_block_root), - withdrawals: Some(withdrawals), - } - } -} - -fn generate_jwt_secret() -> Bytes { - use rand::Rng; - let mut rng = rand::thread_rng(); - let mut secret = [0u8; 32]; - rng.fill(&mut secret); - Bytes::from(secret.to_vec()) -} - -fn keccak256(data: &[u8]) -> H256 { - let digest = Sha256::new_with_prefix(data).finalize(); - H256::from_slice(digest.as_ref()) -} - -async fn wait_until_synced(engine_client: &EngineClient, fork_choice_state: ForkChoiceState) { - loop { - let fork_choice_response = engine_client - .engine_forkchoice_updated_v3(fork_choice_state, None) - .await - .unwrap(); - - let status = fork_choice_response.payload_status.status; - if status == PayloadValidationStatus::Valid { - break; - } - tokio::time::sleep(Duration::from_millis(100)).await; - } -} - -fn get_next_port() -> u16 { - static NEXT_PORT: AtomicU16 = AtomicU16::new(8560); - NEXT_PORT.fetch_add(1, std::sync::atomic::Ordering::Relaxed) -} diff --git a/tooling/repl/Cargo.toml b/tooling/repl/Cargo.toml deleted file mode 100644 index 850e680f91b..00000000000 --- a/tooling/repl/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "ethrex-repl" -version.workspace = true -edition.workspace = true -authors.workspace = true -documentation.workspace = true -license.workspace = true - -[lib] -name = "ethrex_repl" -path = "src/lib.rs" - -[[bin]] -name = "ethrex-repl" -path = "src/main.rs" - -[dependencies] -rustyline = { version = "15", features = ["derive"] } -reqwest.workspace = true -clap.workspace = true -serde.workspace = true -serde_json.workspace = true -tokio = { workspace = true, features = ["full"] } -colored = "2.1.0" -sha3.workspace = true -hex.workspace = true -thiserror.workspace = true -ethereum-types.workspace = true -jsonwebtoken = "9" diff --git a/tooling/repl/README.md b/tooling/repl/README.md deleted file mode 100644 index c2207c1cc14..00000000000 --- a/tooling/repl/README.md +++ /dev/null @@ -1,141 +0,0 @@ -# Ethrex REPL - -Interactive Read-Eval-Print Loop for Ethereum JSON-RPC, integrated as the `ethrex repl` subcommand. - -## Quick Start - -```bash -# Via the ethrex binary -ethrex repl - -# Or directly -cargo run -p ethrex-repl - -# Connect to a specific endpoint -ethrex repl -e https://eth.llamarpc.com - -# Execute a single command and exit -ethrex repl -x "eth.blockNumber" -``` - -## Usage - -``` -ethrex-repl [OPTIONS] - -Options: - -e, --endpoint JSON-RPC endpoint [default: http://localhost:8545] - --history-file Path to command history file [default: ~/.ethrex/history] - -x, --execute Execute a single command and exit -``` - -## Features - -### RPC Commands - -Type `namespace.method` with arguments separated by spaces or in parentheses: - -``` -> eth.blockNumber -68943 - -> eth.getBalance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -1000000000000000000 - -> eth.getBalance("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest") -1000000000000000000 - -> eth.getBlockByNumber 100 true -┌─────────────────────────────────────────┐ -│ number 100 │ -│ timestamp 1438270128 │ -│ ... │ -└─────────────────────────────────────────┘ -``` - -### Supported Namespaces - -| Namespace | Methods | Description | -|-----------|--------:|-------------| -| `eth` | 30 | Accounts, blocks, transactions, filters, gas, proofs | -| `debug` | 8 | Raw headers/blocks/transactions/receipts, tracing | -| `admin` | 4 | Node info, peers, log level, add peer | -| `net` | 2 | Network ID, peer count | -| `web3` | 1 | Client version | -| `txpool` | 2 | Transaction pool content and status | - -Type `.help` to list all namespaces, `.help eth` to list methods in a namespace, or `.help eth.getBalance` for detailed method documentation. - -### ENS Name Resolution - -Any command that accepts an address also accepts ENS names: - -``` -> eth.getBalance vitalik.eth -Resolved vitalik.eth -> 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 -1000000000000000000 -``` - -Resolution is done on-chain by querying the ENS registry at `0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e`. - -### Utility Functions - -| Function | Example | Description | -|----------|---------|-------------| -| `toWei` | `toWei 1.5 ether` → `1500000000000000000` | Convert to wei | -| `fromWei` | `fromWei 1000000000 gwei` → `1` | Convert from wei | -| `toHex` | `toHex 255` → `0xff` | Decimal to hex | -| `fromHex` | `fromHex 0xff` → `255` | Hex to decimal | -| `keccak256` | `keccak256 0x68656c6c6f` → `0x1c8a...` | Keccak-256 hash | -| `toChecksumAddress` | `toChecksumAddress 0xd8da...` → `0xd8dA...` | EIP-55 checksum | -| `isAddress` | `isAddress 0xd8dA...` → `true` | Validate address format | - -Units for `toWei`/`fromWei`: `wei`, `gwei`, `ether` (or `eth`). - -### Built-in Commands - -| Command | Description | -|---------|-------------| -| `.help [namespace\|command]` | Show help | -| `.exit` / `.quit` | Exit the REPL | -| `.clear` | Clear the screen | -| `.connect ` | Show or change endpoint | -| `.history` | Show history file path | - -### Other Features - -- **Tab completion** for namespaces, methods, block tags, and utilities -- **Parameter hints** shown after typing a full method name -- **Multi-line input** — unbalanced `{}` or `[]` automatically continues to the next line -- **Persistent history** saved to `~/.ethrex/history` -- **Formatted output** — addresses, hashes, hex quantities, and nested objects are colored and auto-formatted - -## Architecture - -``` -tooling/repl/src/ -├── lib.rs Entry point, run() function -├── main.rs Standalone binary CLI -├── repl.rs REPL loop, command dispatch, utility functions -├── parser.rs Tokenizer and command parser -├── client.rs JSON-RPC HTTP client -├── commands/ Command definitions per namespace -│ ├── mod.rs CommandDef, ParamDef, validation, registry -│ ├── eth.rs 30 eth_* methods -│ ├── debug.rs 8 debug_* methods -│ ├── admin.rs 4 admin_* methods -│ ├── net.rs 2 net_* methods -│ ├── web3.rs 1 web3_* method -│ └── txpool.rs 2 txpool_* methods -├── completer.rs Tab completion and parameter hints -├── formatter.rs Output formatting and colorization -└── ens.rs ENS name resolution (namehash + on-chain lookup) -``` - -## Running Tests - -```bash -cargo test -p ethrex-repl -``` - -The test suite includes 189 unit tests covering all modules and 22 end-to-end tests using a mock JSON-RPC server. diff --git a/tooling/repl/src/client.rs b/tooling/repl/src/client.rs deleted file mode 100644 index 11ca551dbeb..00000000000 --- a/tooling/repl/src/client.rs +++ /dev/null @@ -1,114 +0,0 @@ -use std::sync::atomic::{AtomicU64, Ordering}; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; - -use reqwest::Client; -use serde_json::{Value, json}; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum RpcError { - #[error("transport error: {0}")] - Transport(String), - #[error("JSON-RPC error (code {code}): {message}")] - JsonRpc { code: i64, message: String }, - #[error("parse error: {0}")] - Parse(String), -} - -pub struct RpcClient { - endpoint: String, - client: Client, - request_id: AtomicU64, - jwt_secret: Option>, -} - -impl RpcClient { - pub fn new(endpoint: String) -> Self { - Self { - endpoint, - client: Client::builder() - .timeout(Duration::from_secs(30)) - .build() - .expect("Failed to build HTTP client"), - request_id: AtomicU64::new(1), - jwt_secret: None, - } - } - - pub fn new_with_jwt(endpoint: String, jwt_secret: Vec) -> Self { - Self { - endpoint, - client: Client::builder() - .timeout(Duration::from_secs(30)) - .build() - .expect("Failed to build HTTP client"), - request_id: AtomicU64::new(1), - jwt_secret: Some(jwt_secret), - } - } - - pub fn endpoint(&self) -> &str { - &self.endpoint - } - - pub async fn send_request(&self, method: &str, params: Vec) -> Result { - let id = self.request_id.fetch_add(1, Ordering::Relaxed); - - let request_body = json!({ - "jsonrpc": "2.0", - "method": method, - "params": params, - "id": id, - }); - - let mut builder = self - .client - .post(&self.endpoint) - .header("Content-Type", "application/json"); - - if let Some(secret) = &self.jwt_secret { - let token = Self::auth_token(secret) - .map_err(|e| RpcError::Transport(format!("JWT error: {e}")))?; - builder = builder.bearer_auth(token); - } - - let response = builder - .json(&request_body) - .send() - .await - .map_err(|e| RpcError::Transport(e.to_string()))? - .error_for_status() - .map_err(|e| RpcError::Transport(e.to_string()))?; - - let response_body: Value = response - .json() - .await - .map_err(|e| RpcError::Parse(e.to_string()))?; - - if let Some(error) = response_body.get("error") { - let code = error.get("code").and_then(Value::as_i64).unwrap_or(-1); - let message = error - .get("message") - .and_then(Value::as_str) - .unwrap_or("unknown error") - .to_string(); - return Err(RpcError::JsonRpc { code, message }); - } - - response_body - .get("result") - .cloned() - .ok_or_else(|| RpcError::Parse("response missing 'result' field".to_string())) - } - - fn auth_token(secret: &[u8]) -> Result { - let header = jsonwebtoken::Header::default(); - let valid_iat = SystemTime::now() - .duration_since(UNIX_EPOCH) - .map_err(|e| e.to_string())? - .as_secs(); - let claims = json!({"iat": valid_iat}); - let encoding_key = jsonwebtoken::EncodingKey::from_secret(secret); - jsonwebtoken::encode(&header, &claims, &encoding_key).map_err(|e| e.to_string()) - } -} diff --git a/tooling/repl/src/commands/admin.rs b/tooling/repl/src/commands/admin.rs deleted file mode 100644 index f1ae2c7d599..00000000000 --- a/tooling/repl/src/commands/admin.rs +++ /dev/null @@ -1,52 +0,0 @@ -use super::{CommandDef, ParamDef, ParamType}; - -const NO_PARAMS: &[ParamDef] = &[]; - -const LEVEL: &[ParamDef] = &[ParamDef { - name: "level", - param_type: ParamType::StringParam, - required: true, - default_value: None, - description: "Log level (trace, debug, info, warn, error)", -}]; - -const ENODE: &[ParamDef] = &[ParamDef { - name: "enode", - param_type: ParamType::StringParam, - required: true, - default_value: None, - description: "Enode URL of peer to add", -}]; - -pub fn commands() -> Vec { - vec![ - CommandDef { - namespace: "admin", - name: "nodeInfo", - rpc_method: "admin_nodeInfo", - params: NO_PARAMS, - description: "Returns node information", - }, - CommandDef { - namespace: "admin", - name: "peers", - rpc_method: "admin_peers", - params: NO_PARAMS, - description: "Returns connected peers", - }, - CommandDef { - namespace: "admin", - name: "setLogLevel", - rpc_method: "admin_setLogLevel", - params: LEVEL, - description: "Sets the node log level", - }, - CommandDef { - namespace: "admin", - name: "addPeer", - rpc_method: "admin_addPeer", - params: ENODE, - description: "Adds a peer by enode URL", - }, - ] -} diff --git a/tooling/repl/src/commands/debug.rs b/tooling/repl/src/commands/debug.rs deleted file mode 100644 index 0c3cef66d41..00000000000 --- a/tooling/repl/src/commands/debug.rs +++ /dev/null @@ -1,112 +0,0 @@ -use super::{CommandDef, ParamDef, ParamType}; - -const BLOCK_ONLY: &[ParamDef] = &[ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: true, - default_value: None, - description: "Block identifier", -}]; - -const HASH_ONLY: &[ParamDef] = &[ParamDef { - name: "hash", - param_type: ParamType::Hash, - required: true, - default_value: None, - description: "Transaction hash", -}]; - -const HASH_OPTIONS: &[ParamDef] = &[ - ParamDef { - name: "hash", - param_type: ParamType::Hash, - required: true, - default_value: None, - description: "Transaction hash", - }, - ParamDef { - name: "options", - param_type: ParamType::Object, - required: false, - default_value: None, - description: "Trace options", - }, -]; - -const BLOCK_OPTIONS: &[ParamDef] = &[ - ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: true, - default_value: None, - description: "Block identifier", - }, - ParamDef { - name: "options", - param_type: ParamType::Object, - required: false, - default_value: None, - description: "Trace options", - }, -]; - -pub fn commands() -> Vec { - vec![ - CommandDef { - namespace: "debug", - name: "getRawHeader", - rpc_method: "debug_getRawHeader", - params: BLOCK_ONLY, - description: "Returns the RLP-encoded block header", - }, - CommandDef { - namespace: "debug", - name: "getRawBlock", - rpc_method: "debug_getRawBlock", - params: BLOCK_ONLY, - description: "Returns the RLP-encoded block", - }, - CommandDef { - namespace: "debug", - name: "getRawTransaction", - rpc_method: "debug_getRawTransaction", - params: HASH_ONLY, - description: "Returns the RLP-encoded transaction", - }, - CommandDef { - namespace: "debug", - name: "getRawReceipts", - rpc_method: "debug_getRawReceipts", - params: BLOCK_ONLY, - description: "Returns the RLP-encoded receipts for a block", - }, - CommandDef { - namespace: "debug", - name: "executionWitness", - rpc_method: "debug_executionWitness", - params: BLOCK_ONLY, - description: "Returns the execution witness for a block", - }, - CommandDef { - namespace: "debug", - name: "getBlockAccessList", - rpc_method: "debug_getBlockAccessList", - params: BLOCK_ONLY, - description: "Returns the access list for a block", - }, - CommandDef { - namespace: "debug", - name: "traceTransaction", - rpc_method: "debug_traceTransaction", - params: HASH_OPTIONS, - description: "Traces a transaction execution", - }, - CommandDef { - namespace: "debug", - name: "traceBlockByNumber", - rpc_method: "debug_traceBlockByNumber", - params: BLOCK_OPTIONS, - description: "Traces all transactions in a block", - }, - ] -} diff --git a/tooling/repl/src/commands/engine.rs b/tooling/repl/src/commands/engine.rs deleted file mode 100644 index 107200f6b78..00000000000 --- a/tooling/repl/src/commands/engine.rs +++ /dev/null @@ -1,83 +0,0 @@ -use super::{CommandDef, ParamDef, ParamType}; - -const FORK_CHOICE_UPDATED_V3: &[ParamDef] = &[ - ParamDef { - name: "fork_choice_state", - param_type: ParamType::Object, - required: true, - default_value: None, - description: "ForkChoiceState {headBlockHash, safeBlockHash, finalizedBlockHash}", - }, - ParamDef { - name: "payload_attributes", - param_type: ParamType::Object, - required: false, - default_value: None, - description: "PayloadAttributesV3 {timestamp, prevRandao, suggestedFeeRecipient, parentBeaconBlockRoot, withdrawals}", - }, -]; - -const GET_PAYLOAD_V5: &[ParamDef] = &[ParamDef { - name: "payload_id", - param_type: ParamType::HexData, - required: true, - default_value: None, - description: "Payload identifier returned by forkchoiceUpdated", -}]; - -const NEW_PAYLOAD_V4: &[ParamDef] = &[ - ParamDef { - name: "execution_payload", - param_type: ParamType::Object, - required: true, - default_value: None, - description: "ExecutionPayload object", - }, - ParamDef { - name: "versioned_hashes", - param_type: ParamType::Array, - required: true, - default_value: None, - description: "Array of blob versioned hashes", - }, - ParamDef { - name: "parent_beacon_block_root", - param_type: ParamType::Hash, - required: true, - default_value: None, - description: "Parent beacon block root", - }, - ParamDef { - name: "execution_requests", - param_type: ParamType::Array, - required: true, - default_value: None, - description: "Array of execution requests (EIP-7685)", - }, -]; - -pub fn commands() -> Vec { - vec![ - CommandDef { - namespace: "engine", - name: "forkchoiceUpdatedV3", - rpc_method: "engine_forkchoiceUpdatedV3", - params: FORK_CHOICE_UPDATED_V3, - description: "Update fork choice state and optionally trigger payload building", - }, - CommandDef { - namespace: "engine", - name: "getPayloadV5", - rpc_method: "engine_getPayloadV5", - params: GET_PAYLOAD_V5, - description: "Get execution payload by ID", - }, - CommandDef { - namespace: "engine", - name: "newPayloadV4", - rpc_method: "engine_newPayloadV4", - params: NEW_PAYLOAD_V4, - description: "Submit a new execution payload for validation", - }, - ] -} diff --git a/tooling/repl/src/commands/eth.rs b/tooling/repl/src/commands/eth.rs deleted file mode 100644 index 43155d261f2..00000000000 --- a/tooling/repl/src/commands/eth.rs +++ /dev/null @@ -1,440 +0,0 @@ -use super::{CommandDef, ParamDef, ParamType}; - -const NO_PARAMS: &[ParamDef] = &[]; - -const ADDR_BLOCK: &[ParamDef] = &[ - ParamDef { - name: "address", - param_type: ParamType::Address, - required: true, - default_value: None, - description: "Account address", - }, - ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: false, - default_value: Some("latest"), - description: "Block identifier", - }, -]; - -const BLOCK_FULLTXS: &[ParamDef] = &[ - ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: true, - default_value: None, - description: "Block identifier", - }, - ParamDef { - name: "full_txs", - param_type: ParamType::Bool, - required: false, - default_value: Some("false"), - description: "Return full transactions if true", - }, -]; - -const HASH_FULLTXS: &[ParamDef] = &[ - ParamDef { - name: "hash", - param_type: ParamType::Hash, - required: true, - default_value: None, - description: "Block hash", - }, - ParamDef { - name: "full_txs", - param_type: ParamType::Bool, - required: false, - default_value: Some("false"), - description: "Return full transactions if true", - }, -]; - -const BLOCK_ONLY: &[ParamDef] = &[ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: true, - default_value: None, - description: "Block identifier", -}]; - -const HASH_ONLY: &[ParamDef] = &[ParamDef { - name: "hash", - param_type: ParamType::Hash, - required: true, - default_value: None, - description: "Transaction hash", -}]; - -const TX_OBJECT_BLOCK: &[ParamDef] = &[ - ParamDef { - name: "tx_object", - param_type: ParamType::Object, - required: true, - default_value: None, - description: "Transaction call object", - }, - ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: false, - default_value: Some("latest"), - description: "Block identifier", - }, -]; - -const FILTER_OBJECT: &[ParamDef] = &[ParamDef { - name: "filter", - param_type: ParamType::Object, - required: true, - default_value: None, - description: "Filter object", -}]; - -const FILTER_ID: &[ParamDef] = &[ParamDef { - name: "filter_id", - param_type: ParamType::Uint, - required: true, - default_value: None, - description: "Filter identifier", -}]; - -const GET_STORAGE_AT: &[ParamDef] = &[ - ParamDef { - name: "address", - param_type: ParamType::Address, - required: true, - default_value: None, - description: "Account address", - }, - ParamDef { - name: "slot", - param_type: ParamType::Hash, - required: true, - default_value: None, - description: "Storage slot", - }, - ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: false, - default_value: Some("latest"), - description: "Block identifier", - }, -]; - -const BLOCK_INDEX: &[ParamDef] = &[ - ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: true, - default_value: None, - description: "Block identifier", - }, - ParamDef { - name: "index", - param_type: ParamType::Uint, - required: true, - default_value: None, - description: "Transaction index", - }, -]; - -const HASH_INDEX: &[ParamDef] = &[ - ParamDef { - name: "hash", - param_type: ParamType::Hash, - required: true, - default_value: None, - description: "Block hash", - }, - ParamDef { - name: "index", - param_type: ParamType::Uint, - required: true, - default_value: None, - description: "Transaction index", - }, -]; - -const SEND_RAW_TX: &[ParamDef] = &[ParamDef { - name: "data", - param_type: ParamType::HexData, - required: true, - default_value: None, - description: "Signed transaction data", -}]; - -const FEE_HISTORY: &[ParamDef] = &[ - ParamDef { - name: "block_count", - param_type: ParamType::Uint, - required: true, - default_value: None, - description: "Number of blocks", - }, - ParamDef { - name: "newest_block", - param_type: ParamType::BlockId, - required: true, - default_value: None, - description: "Newest block", - }, - ParamDef { - name: "reward_percentiles", - param_type: ParamType::Array, - required: false, - default_value: None, - description: "Reward percentile values", - }, -]; - -const BLOCK_HASH_ONLY: &[ParamDef] = &[ParamDef { - name: "hash", - param_type: ParamType::Hash, - required: true, - default_value: None, - description: "Block hash", -}]; - -const GET_PROOF: &[ParamDef] = &[ - ParamDef { - name: "address", - param_type: ParamType::Address, - required: true, - default_value: None, - description: "Account address", - }, - ParamDef { - name: "storage_keys", - param_type: ParamType::Array, - required: true, - default_value: None, - description: "Storage keys to prove", - }, - ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: false, - default_value: Some("latest"), - description: "Block identifier", - }, -]; - -pub fn commands() -> Vec { - vec![ - CommandDef { - namespace: "eth", - name: "blockNumber", - rpc_method: "eth_blockNumber", - params: NO_PARAMS, - description: "Returns the current block number", - }, - CommandDef { - namespace: "eth", - name: "chainId", - rpc_method: "eth_chainId", - params: NO_PARAMS, - description: "Returns the chain ID", - }, - CommandDef { - namespace: "eth", - name: "syncing", - rpc_method: "eth_syncing", - params: NO_PARAMS, - description: "Returns syncing status or false", - }, - CommandDef { - namespace: "eth", - name: "gasPrice", - rpc_method: "eth_gasPrice", - params: NO_PARAMS, - description: "Returns the current gas price in wei", - }, - CommandDef { - namespace: "eth", - name: "maxPriorityFeePerGas", - rpc_method: "eth_maxPriorityFeePerGas", - params: NO_PARAMS, - description: "Returns the current max priority fee per gas", - }, - CommandDef { - namespace: "eth", - name: "blobBaseFee", - rpc_method: "eth_blobBaseFee", - params: NO_PARAMS, - description: "Returns the current blob base fee", - }, - CommandDef { - namespace: "eth", - name: "accounts", - rpc_method: "eth_accounts", - params: NO_PARAMS, - description: "Returns list of addresses owned by the client", - }, - CommandDef { - namespace: "eth", - name: "getBalance", - rpc_method: "eth_getBalance", - params: ADDR_BLOCK, - description: "Returns the balance of an account", - }, - CommandDef { - namespace: "eth", - name: "getCode", - rpc_method: "eth_getCode", - params: ADDR_BLOCK, - description: "Returns the code at an address", - }, - CommandDef { - namespace: "eth", - name: "getStorageAt", - rpc_method: "eth_getStorageAt", - params: GET_STORAGE_AT, - description: "Returns the value at a storage slot", - }, - CommandDef { - namespace: "eth", - name: "getTransactionCount", - rpc_method: "eth_getTransactionCount", - params: ADDR_BLOCK, - description: "Returns the number of transactions sent from an address", - }, - CommandDef { - namespace: "eth", - name: "getBlockByNumber", - rpc_method: "eth_getBlockByNumber", - params: BLOCK_FULLTXS, - description: "Returns a block by its number", - }, - CommandDef { - namespace: "eth", - name: "getBlockByHash", - rpc_method: "eth_getBlockByHash", - params: HASH_FULLTXS, - description: "Returns a block by its hash", - }, - CommandDef { - namespace: "eth", - name: "getBlockTransactionCountByNumber", - rpc_method: "eth_getBlockTransactionCountByNumber", - params: BLOCK_ONLY, - description: "Returns transaction count in a block by number", - }, - CommandDef { - namespace: "eth", - name: "getBlockTransactionCountByHash", - rpc_method: "eth_getBlockTransactionCountByHash", - params: BLOCK_HASH_ONLY, - description: "Returns transaction count in a block by hash", - }, - CommandDef { - namespace: "eth", - name: "getTransactionByBlockNumberAndIndex", - rpc_method: "eth_getTransactionByBlockNumberAndIndex", - params: BLOCK_INDEX, - description: "Returns a transaction by block number and index", - }, - CommandDef { - namespace: "eth", - name: "getTransactionByBlockHashAndIndex", - rpc_method: "eth_getTransactionByBlockHashAndIndex", - params: HASH_INDEX, - description: "Returns a transaction by block hash and index", - }, - CommandDef { - namespace: "eth", - name: "getTransactionByHash", - rpc_method: "eth_getTransactionByHash", - params: HASH_ONLY, - description: "Returns a transaction by its hash", - }, - CommandDef { - namespace: "eth", - name: "getTransactionReceipt", - rpc_method: "eth_getTransactionReceipt", - params: HASH_ONLY, - description: "Returns the receipt of a transaction", - }, - CommandDef { - namespace: "eth", - name: "getBlockReceipts", - rpc_method: "eth_getBlockReceipts", - params: BLOCK_ONLY, - description: "Returns all receipts for a block", - }, - CommandDef { - namespace: "eth", - name: "call", - rpc_method: "eth_call", - params: TX_OBJECT_BLOCK, - description: "Executes a call without creating a transaction", - }, - CommandDef { - namespace: "eth", - name: "estimateGas", - rpc_method: "eth_estimateGas", - params: TX_OBJECT_BLOCK, - description: "Estimates gas needed for a transaction", - }, - CommandDef { - namespace: "eth", - name: "sendRawTransaction", - rpc_method: "eth_sendRawTransaction", - params: SEND_RAW_TX, - description: "Submits a signed transaction", - }, - CommandDef { - namespace: "eth", - name: "feeHistory", - rpc_method: "eth_feeHistory", - params: FEE_HISTORY, - description: "Returns fee history for a range of blocks", - }, - CommandDef { - namespace: "eth", - name: "getLogs", - rpc_method: "eth_getLogs", - params: FILTER_OBJECT, - description: "Returns logs matching a filter", - }, - CommandDef { - namespace: "eth", - name: "newFilter", - rpc_method: "eth_newFilter", - params: FILTER_OBJECT, - description: "Creates a new log filter", - }, - CommandDef { - namespace: "eth", - name: "getFilterChanges", - rpc_method: "eth_getFilterChanges", - params: FILTER_ID, - description: "Returns filter changes since last poll", - }, - CommandDef { - namespace: "eth", - name: "uninstallFilter", - rpc_method: "eth_uninstallFilter", - params: FILTER_ID, - description: "Removes a filter", - }, - CommandDef { - namespace: "eth", - name: "createAccessList", - rpc_method: "eth_createAccessList", - params: TX_OBJECT_BLOCK, - description: "Creates an EIP-2930 access list", - }, - CommandDef { - namespace: "eth", - name: "getProof", - rpc_method: "eth_getProof", - params: GET_PROOF, - description: "Returns the Merkle proof for an account", - }, - ] -} diff --git a/tooling/repl/src/commands/mod.rs b/tooling/repl/src/commands/mod.rs deleted file mode 100644 index eb123f825a3..00000000000 --- a/tooling/repl/src/commands/mod.rs +++ /dev/null @@ -1,626 +0,0 @@ -mod admin; -mod debug; -mod engine; -mod eth; -mod net; -mod txpool; -mod web3; - -use serde_json::Value; - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum ParamType { - Address, - BlockId, - Hash, - HexData, - Uint, - Bool, - Object, - Array, - StringParam, -} - -#[derive(Debug, Clone)] -pub struct ParamDef { - pub name: &'static str, - pub param_type: ParamType, - pub required: bool, - pub default_value: Option<&'static str>, - pub description: &'static str, -} - -#[derive(Debug, Clone)] -pub struct CommandDef { - pub namespace: &'static str, - pub name: &'static str, - pub rpc_method: &'static str, - pub params: &'static [ParamDef], - pub description: &'static str, -} - -impl CommandDef { - pub fn full_name(&self) -> String { - format!("{}.{}", self.namespace, self.name) - } - - pub fn usage(&self) -> String { - let params: Vec = self - .params - .iter() - .map(|p| { - if p.required { - format!("<{}>", p.name) - } else if let Some(def) = p.default_value { - format!("[{}={}]", p.name, def) - } else { - format!("[{}]", p.name) - } - }) - .collect(); - format!("{}.{} {}", self.namespace, self.name, params.join(" ")) - } - - pub fn build_params(&self, args: &[Value]) -> Result, String> { - let required_count = self.params.iter().filter(|p| p.required).count(); - - if args.len() < required_count { - return Err(format!( - "{} requires at least {} argument(s), got {}", - self.rpc_method, - required_count, - args.len() - )); - } - - if args.len() > self.params.len() { - return Err(format!( - "{} accepts at most {} argument(s), got {}", - self.rpc_method, - self.params.len(), - args.len() - )); - } - - let mut result = Vec::with_capacity(self.params.len()); - - for (i, param_def) in self.params.iter().enumerate() { - let value = if let Some(arg) = args.get(i) { - validate_and_convert(arg, param_def)? - } else if let Some(default) = param_def.default_value { - Value::String(default.to_string()) - } else { - // Optional param with no default and no value provided — stop here - break; - }; - result.push(value); - } - - Ok(result) - } -} - -fn validate_and_convert(value: &Value, param_def: &ParamDef) -> Result { - match param_def.param_type { - ParamType::Address => { - let s = value_as_str(value)?; - if !is_valid_address(&s) { - return Err(format!( - "'{}': expected a 0x-prefixed 20-byte hex address", - param_def.name - )); - } - Ok(Value::String(s)) - } - ParamType::Hash => { - let s = value_as_str(value)?; - if !is_valid_hash(&s) { - return Err(format!( - "'{}': expected a 0x-prefixed 32-byte hex hash", - param_def.name - )); - } - Ok(Value::String(s)) - } - ParamType::BlockId => { - let s = value_as_str(value)?; - Ok(Value::String(normalize_block_id(&s))) - } - ParamType::HexData => { - let s = value_as_str(value)?; - if !s.starts_with("0x") { - return Err(format!( - "'{}': expected 0x-prefixed hex data", - param_def.name - )); - } - Ok(Value::String(s)) - } - ParamType::Uint => { - let s = value_as_str(value)?; - Ok(Value::String(normalize_uint(&s)?)) - } - ParamType::Bool => match value { - Value::Bool(b) => Ok(Value::Bool(*b)), - Value::String(s) => match s.as_str() { - "true" => Ok(Value::Bool(true)), - "false" => Ok(Value::Bool(false)), - _ => Err(format!("'{}': expected true or false", param_def.name)), - }, - _ => Err(format!("'{}': expected a boolean", param_def.name)), - }, - ParamType::Object => match value { - Value::Object(_) => Ok(value.clone()), - Value::String(s) => serde_json::from_str(s) - .map_err(|e| format!("'{}': invalid JSON object: {}", param_def.name, e)), - _ => Err(format!("'{}': expected a JSON object", param_def.name)), - }, - ParamType::Array => match value { - Value::Array(_) => Ok(value.clone()), - Value::String(s) => serde_json::from_str(s) - .map_err(|e| format!("'{}': invalid JSON array: {}", param_def.name, e)), - _ => Err(format!("'{}': expected a JSON array", param_def.name)), - }, - ParamType::StringParam => { - let s = value_as_str(value)?; - Ok(Value::String(s)) - } - } -} - -fn value_as_str(value: &Value) -> Result { - match value { - Value::String(s) => Ok(s.clone()), - Value::Number(n) => Ok(n.to_string()), - Value::Bool(b) => Ok(b.to_string()), - _ => Ok(value.to_string()), - } -} - -fn is_valid_address(s: &str) -> bool { - s.starts_with("0x") && s.len() == 42 && s[2..].chars().all(|c| c.is_ascii_hexdigit()) -} - -fn is_valid_hash(s: &str) -> bool { - s.starts_with("0x") && s.len() == 66 && s[2..].chars().all(|c| c.is_ascii_hexdigit()) -} - -fn normalize_block_id(s: &str) -> String { - match s { - "latest" | "earliest" | "pending" | "finalized" | "safe" => s.to_string(), - _ if s.starts_with("0x") => s.to_string(), - _ => { - // Try parsing as decimal and converting to hex - if let Ok(n) = s.parse::() { - format!("0x{n:x}") - } else { - s.to_string() - } - } - } -} - -fn normalize_uint(s: &str) -> Result { - if s.starts_with("0x") { - // Already hex - Ok(s.to_string()) - } else if let Ok(n) = s.parse::() { - Ok(format!("0x{n:x}")) - } else { - Err(format!("invalid uint: {s}")) - } -} - -pub struct CommandRegistry { - commands: Vec, -} - -impl CommandRegistry { - pub fn new() -> Self { - let mut commands = Vec::new(); - commands.extend(eth::commands()); - commands.extend(engine::commands()); - commands.extend(debug::commands()); - commands.extend(admin::commands()); - commands.extend(net::commands()); - commands.extend(web3::commands()); - commands.extend(txpool::commands()); - Self { commands } - } - - pub fn find(&self, namespace: &str, method: &str) -> Option<&CommandDef> { - self.commands - .iter() - .find(|c| c.namespace == namespace && c.name == method) - } - - pub fn namespaces(&self) -> Vec<&str> { - let mut ns: Vec<&str> = self.commands.iter().map(|c| c.namespace).collect(); - ns.sort(); - ns.dedup(); - ns - } - - pub fn methods_in_namespace(&self, namespace: &str) -> Vec<&CommandDef> { - self.commands - .iter() - .filter(|c| c.namespace == namespace) - .collect() - } - - #[cfg(test)] - pub fn all_commands(&self) -> &[CommandDef] { - &self.commands - } -} - -impl Default for CommandRegistry { - fn default() -> Self { - Self::new() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde_json::json; - - fn make_params(defs: Vec) -> &'static [ParamDef] { - Box::leak(defs.into_boxed_slice()) - } - - fn make_cmd(params: &'static [ParamDef]) -> CommandDef { - CommandDef { - namespace: "test", - name: "test", - rpc_method: "test_test", - params, - description: "test command", - } - } - - /// Create a required ParamDef with the given name and type. Reduces repetition - /// in tests that only vary these two fields. - fn required_param(name: &'static str, param_type: ParamType) -> ParamDef { - ParamDef { - name, - param_type, - required: true, - default_value: None, - description: name, - } - } - - /// Shorthand: create a CommandDef with a single required param. - fn cmd_with_param(name: &'static str, param_type: ParamType) -> CommandDef { - make_cmd(make_params(vec![required_param(name, param_type)])) - } - - // --- build_params --- - - #[test] - fn test_build_params_correct_number() { - let cmd = cmd_with_param("addr", ParamType::Address); - let result = cmd - .build_params(&[json!("0x1234567890abcdef1234567890abcdef12345678")]) - .unwrap(); - assert_eq!(result.len(), 1); - } - - #[test] - fn test_build_params_too_few_required() { - let cmd = cmd_with_param("addr", ParamType::Address); - let err = cmd.build_params(&[]).unwrap_err(); - assert!(err.contains("requires at least 1")); - } - - #[test] - fn test_build_params_too_many_args() { - let cmd = cmd_with_param("addr", ParamType::Address); - let err = cmd - .build_params(&[ - json!("0x1234567890abcdef1234567890abcdef12345678"), - json!("extra"), - ]) - .unwrap_err(); - assert!(err.contains("accepts at most 1")); - } - - #[test] - fn test_build_params_optional_with_default() { - let cmd = make_cmd(make_params(vec![ - required_param("addr", ParamType::Address), - ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: false, - default_value: Some("latest"), - description: "block id", - }, - ])); - let result = cmd - .build_params(&[json!("0x1234567890abcdef1234567890abcdef12345678")]) - .unwrap(); - assert_eq!(result.len(), 2); - assert_eq!(result[1], json!("latest")); - } - - #[test] - fn test_build_params_optional_without_default_stops() { - let cmd = make_cmd(make_params(vec![ - required_param("addr", ParamType::Address), - ParamDef { - name: "extra", - param_type: ParamType::StringParam, - required: false, - default_value: None, - description: "extra", - }, - ])); - let result = cmd - .build_params(&[json!("0x1234567890abcdef1234567890abcdef12345678")]) - .unwrap(); - assert_eq!(result.len(), 1); - } - - // --- validate_and_convert via build_params --- - - #[test] - fn test_address_valid() { - let cmd = cmd_with_param("addr", ParamType::Address); - let result = cmd - .build_params(&[json!("0x1234567890abcdef1234567890abcdef12345678")]) - .unwrap(); - assert_eq!( - result[0], - json!("0x1234567890abcdef1234567890abcdef12345678") - ); - } - - #[test] - fn test_address_invalid_length() { - let cmd = cmd_with_param("addr", ParamType::Address); - let err = cmd.build_params(&[json!("0x1234")]).unwrap_err(); - assert!(err.contains("20-byte hex address")); - } - - #[test] - fn test_address_no_0x_prefix() { - let cmd = cmd_with_param("addr", ParamType::Address); - let err = cmd - .build_params(&[json!("1234567890abcdef1234567890abcdef12345678")]) - .unwrap_err(); - assert!(err.contains("20-byte hex address")); - } - - #[test] - fn test_hash_valid() { - let cmd = cmd_with_param("hash", ParamType::Hash); - let valid_hash = "0x".to_string() + &"ab".repeat(32); - let result = cmd.build_params(&[json!(valid_hash)]).unwrap(); - assert_eq!(result[0], json!(valid_hash)); - } - - #[test] - fn test_hash_invalid() { - let cmd = cmd_with_param("hash", ParamType::Hash); - let err = cmd.build_params(&[json!("0x1234")]).unwrap_err(); - assert!(err.contains("32-byte hex hash")); - } - - #[test] - fn test_block_id_named_tags() { - let cmd = cmd_with_param("block", ParamType::BlockId); - for tag in &["latest", "earliest", "pending", "finalized", "safe"] { - let result = cmd.build_params(&[json!(tag)]).unwrap(); - assert_eq!(result[0], json!(tag)); - } - } - - #[test] - fn test_block_id_decimal_to_hex() { - let cmd = cmd_with_param("block", ParamType::BlockId); - let result = cmd.build_params(&[json!("100")]).unwrap(); - assert_eq!(result[0], json!("0x64")); - } - - #[test] - fn test_block_id_hex_passthrough() { - let cmd = cmd_with_param("block", ParamType::BlockId); - let result = cmd.build_params(&[json!("0x64")]).unwrap(); - assert_eq!(result[0], json!("0x64")); - } - - #[test] - fn test_hex_data_valid() { - let cmd = cmd_with_param("data", ParamType::HexData); - let result = cmd.build_params(&[json!("0xdeadbeef")]).unwrap(); - assert_eq!(result[0], json!("0xdeadbeef")); - } - - #[test] - fn test_hex_data_missing_prefix() { - let cmd = cmd_with_param("data", ParamType::HexData); - let err = cmd.build_params(&[json!("deadbeef")]).unwrap_err(); - assert!(err.contains("0x-prefixed hex data")); - } - - #[test] - fn test_uint_decimal_to_hex() { - let cmd = cmd_with_param("val", ParamType::Uint); - let result = cmd.build_params(&[json!("255")]).unwrap(); - assert_eq!(result[0], json!("0xff")); - } - - #[test] - fn test_uint_hex_passthrough() { - let cmd = cmd_with_param("val", ParamType::Uint); - let result = cmd.build_params(&[json!("0xff")]).unwrap(); - assert_eq!(result[0], json!("0xff")); - } - - #[test] - fn test_uint_invalid() { - let cmd = cmd_with_param("val", ParamType::Uint); - let err = cmd.build_params(&[json!("abc")]).unwrap_err(); - assert!(err.contains("invalid uint")); - } - - #[test] - fn test_bool_true_string() { - let cmd = cmd_with_param("flag", ParamType::Bool); - let result = cmd.build_params(&[json!("true")]).unwrap(); - assert_eq!(result[0], json!(true)); - } - - #[test] - fn test_bool_false_string() { - let cmd = cmd_with_param("flag", ParamType::Bool); - let result = cmd.build_params(&[json!("false")]).unwrap(); - assert_eq!(result[0], json!(false)); - } - - #[test] - fn test_bool_actual_bool() { - let cmd = cmd_with_param("flag", ParamType::Bool); - let result = cmd.build_params(&[json!(true)]).unwrap(); - assert_eq!(result[0], json!(true)); - } - - #[test] - fn test_bool_invalid() { - let cmd = cmd_with_param("flag", ParamType::Bool); - let err = cmd.build_params(&[json!("maybe")]).unwrap_err(); - assert!(err.contains("expected true or false")); - } - - #[test] - fn test_object_from_json_string() { - let cmd = cmd_with_param("obj", ParamType::Object); - let result = cmd.build_params(&[json!(r#"{"to":"0xabc"}"#)]).unwrap(); - assert!(result[0].is_object()); - assert_eq!(result[0]["to"], json!("0xabc")); - } - - #[test] - fn test_object_passthrough() { - let cmd = cmd_with_param("obj", ParamType::Object); - let obj = json!({"to": "0xabc"}); - let result = cmd.build_params(std::slice::from_ref(&obj)).unwrap(); - assert_eq!(result[0], obj); - } - - #[test] - fn test_object_invalid_json() { - let cmd = cmd_with_param("obj", ParamType::Object); - let err = cmd.build_params(&[json!("not json")]).unwrap_err(); - assert!(err.contains("invalid JSON object")); - } - - #[test] - fn test_array_from_json_string() { - let cmd = cmd_with_param("arr", ParamType::Array); - let result = cmd.build_params(&[json!(r#"[1, 2, 3]"#)]).unwrap(); - assert!(result[0].is_array()); - } - - #[test] - fn test_array_passthrough() { - let cmd = cmd_with_param("arr", ParamType::Array); - let arr = json!([1, 2, 3]); - let result = cmd.build_params(std::slice::from_ref(&arr)).unwrap(); - assert_eq!(result[0], arr); - } - - #[test] - fn test_string_param_any_value() { - let cmd = cmd_with_param("s", ParamType::StringParam); - let result = cmd.build_params(&[json!("anything")]).unwrap(); - assert_eq!(result[0], json!("anything")); - } - - #[test] - fn test_string_param_number_converted() { - let cmd = cmd_with_param("s", ParamType::StringParam); - let result = cmd.build_params(&[json!(42)]).unwrap(); - assert_eq!(result[0], json!("42")); - } - - // --- CommandRegistry --- - - #[test] - fn test_registry_find_known_command() { - let registry = CommandRegistry::new(); - let cmd = registry.find("eth", "blockNumber"); - assert!(cmd.is_some()); - assert_eq!(cmd.unwrap().rpc_method, "eth_blockNumber"); - } - - #[test] - fn test_registry_find_unknown_command() { - let registry = CommandRegistry::new(); - assert!(registry.find("eth", "nonExistentMethod").is_none()); - } - - #[test] - fn test_registry_namespaces() { - let registry = CommandRegistry::new(); - let ns = registry.namespaces(); - assert!(ns.contains(&"eth")); - assert!(ns.contains(&"net")); - assert!(ns.contains(&"web3")); - } - - #[test] - fn test_registry_methods_in_namespace() { - let registry = CommandRegistry::new(); - let methods = registry.methods_in_namespace("eth"); - assert!(methods.len() > 1); - assert!(methods.iter().all(|c| c.namespace == "eth")); - } - - #[test] - fn test_registry_all_commands_non_empty() { - let registry = CommandRegistry::new(); - assert!(!registry.all_commands().is_empty()); - } - - // --- CommandDef helpers --- - - #[test] - fn test_full_name() { - let cmd = make_cmd(&[]); - assert_eq!(cmd.full_name(), "test.test"); - } - - #[test] - fn test_usage_format() { - let cmd = make_cmd(make_params(vec![ - required_param("addr", ParamType::Address), - ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: false, - default_value: Some("latest"), - description: "block id", - }, - ])); - let usage = cmd.usage(); - assert!(usage.contains("")); - assert!(usage.contains("[block=latest]")); - } - - #[test] - fn test_usage_optional_no_default() { - let cmd = make_cmd(make_params(vec![ParamDef { - name: "extra", - param_type: ParamType::StringParam, - required: false, - default_value: None, - description: "extra", - }])); - let usage = cmd.usage(); - assert!(usage.contains("[extra]")); - assert!(!usage.contains("=")); - } -} diff --git a/tooling/repl/src/commands/net.rs b/tooling/repl/src/commands/net.rs deleted file mode 100644 index b4c4e1a9a7a..00000000000 --- a/tooling/repl/src/commands/net.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::{CommandDef, ParamDef}; - -const NO_PARAMS: &[ParamDef] = &[]; - -pub fn commands() -> Vec { - vec![ - CommandDef { - namespace: "net", - name: "version", - rpc_method: "net_version", - params: NO_PARAMS, - description: "Returns the network ID", - }, - CommandDef { - namespace: "net", - name: "peerCount", - rpc_method: "net_peerCount", - params: NO_PARAMS, - description: "Returns the number of connected peers", - }, - ] -} diff --git a/tooling/repl/src/commands/txpool.rs b/tooling/repl/src/commands/txpool.rs deleted file mode 100644 index 559598c04dd..00000000000 --- a/tooling/repl/src/commands/txpool.rs +++ /dev/null @@ -1,22 +0,0 @@ -use super::{CommandDef, ParamDef}; - -const NO_PARAMS: &[ParamDef] = &[]; - -pub fn commands() -> Vec { - vec![ - CommandDef { - namespace: "txpool", - name: "content", - rpc_method: "txpool_content", - params: NO_PARAMS, - description: "Returns all pending and queued transactions", - }, - CommandDef { - namespace: "txpool", - name: "status", - rpc_method: "txpool_status", - params: NO_PARAMS, - description: "Returns the number of pending and queued transactions", - }, - ] -} diff --git a/tooling/repl/src/commands/web3.rs b/tooling/repl/src/commands/web3.rs deleted file mode 100644 index 60fa3c9dd9d..00000000000 --- a/tooling/repl/src/commands/web3.rs +++ /dev/null @@ -1,13 +0,0 @@ -use super::{CommandDef, ParamDef}; - -const NO_PARAMS: &[ParamDef] = &[]; - -pub fn commands() -> Vec { - vec![CommandDef { - namespace: "web3", - name: "clientVersion", - rpc_method: "web3_clientVersion", - params: NO_PARAMS, - description: "Returns the client version", - }] -} diff --git a/tooling/repl/src/completer.rs b/tooling/repl/src/completer.rs deleted file mode 100644 index d965c0cd9a8..00000000000 --- a/tooling/repl/src/completer.rs +++ /dev/null @@ -1,550 +0,0 @@ -use std::sync::Arc; - -use rustyline::completion::{Completer, Pair}; -use rustyline::highlight::Highlighter; -use rustyline::hint::Hinter; -use rustyline::validate::Validator; -use rustyline::{Context, Helper}; - -use crate::commands::CommandRegistry; -use crate::parser::UTILITY_NAMES; -use crate::variables::VariableStore; - -pub struct ReplHelper { - registry: Arc, - variables: VariableStore, -} - -impl ReplHelper { - pub fn new(registry: Arc, variables: VariableStore) -> Self { - Self { - registry, - variables, - } - } -} - -impl ReplHelper { - /// Complete variable references starting with `$`. - /// - /// Handles three cases: - /// - `$` or `$par` → complete variable names - /// - `$name.` or `$name.fi` → complete top-level field names - /// - `$name.field.` or `$name.field.sub` → complete nested field names - /// - /// Returns `None` if the current word doesn't contain a `$` reference. - fn complete_variable(&self, input: &str) -> Option> { - // Find the last '$' that could be the start of a variable reference. - // It must be preceded by whitespace, '(' , ',' , or be at position 0 - // of the current "word" (i.e. not inside a quoted string). - let dollar_pos = input.rfind('$')?; - if dollar_pos > 0 { - let before = input.as_bytes()[dollar_pos - 1]; - if before != b' ' && before != b'(' && before != b',' && before != b'\t' { - return None; - } - } - - let var_text = &input[dollar_pos + 1..]; // everything after '$' - - // Split into segments by '.' - let segments: Vec<&str> = var_text.split('.').collect(); - - match segments.len() { - // `$` or `$par` → complete variable names - 0 | 1 => { - let partial = segments.first().unwrap_or(&""); - let names = self.variables.names(); - let matches: Vec = names - .iter() - .filter(|n| n.starts_with(partial)) - .map(|n| Pair { - display: format!("${n}"), - replacement: format!("${n}"), - }) - .collect(); - if matches.is_empty() { - None - } else { - Some(matches) - } - } - // `$name.field...partial` → complete field at the deepest level - _ => { - let var_name = segments[0]; - let partial = segments.last().unwrap_or(&""); - let path: Vec<&str> = segments[1..segments.len() - 1].to_vec(); - - let field_names = self.variables.nested_field_names(var_name, &path); - let prefix = if path.is_empty() { - format!("${var_name}.") - } else { - format!("${var_name}.{}.", path.join(".")) - }; - - let matches: Vec = field_names - .iter() - .filter(|f| f.starts_with(partial)) - .map(|f| Pair { - display: format!("{prefix}{f}"), - replacement: format!("{prefix}{f}"), - }) - .collect(); - if matches.is_empty() { - None - } else { - Some(matches) - } - } - } - } -} - -impl Helper for ReplHelper {} - -impl Completer for ReplHelper { - type Candidate = Pair; - - fn complete( - &self, - line: &str, - pos: usize, - _ctx: &Context<'_>, - ) -> rustyline::Result<(usize, Vec)> { - let input = &line[..pos]; - - // Built-in dot commands - if input.starts_with('.') { - let builtins = [ - ".help", ".exit", ".quit", ".clear", ".connect", ".history", ".vars", - ]; - let matches: Vec = builtins - .iter() - .filter(|b| b.starts_with(input)) - .map(|b| Pair { - display: b.to_string(), - replacement: b.to_string(), - }) - .collect(); - return Ok((0, matches)); - } - - // Variable completion: find the current word containing '$' - if let Some(var_completions) = self.complete_variable(input) { - // The replacement starts at the '$' position - let dollar_pos = input.rfind('$').unwrap_or(0); - return Ok((dollar_pos, var_completions)); - } - - // If we have "namespace." pattern, complete methods - if let Some(dot_pos) = input.rfind('.') { - let namespace = &input[..dot_pos]; - let partial_method = &input[dot_pos + 1..]; - let methods = self.registry.methods_in_namespace(namespace); - let matches: Vec = methods - .iter() - .filter(|m| m.name.starts_with(partial_method)) - .map(|m| { - let replacement = format!("{}.{}", namespace, m.name); - let display = format!("{}.{} - {}", namespace, m.name, m.description); - Pair { - display, - replacement, - } - }) - .collect(); - return Ok((0, matches)); - } - - // Complete namespaces and utilities - let mut matches: Vec = Vec::new(); - - for ns in self.registry.namespaces() { - if ns.starts_with(input) { - matches.push(Pair { - display: format!("{}.", ns), - replacement: format!("{}.", ns), - }); - } - } - - for name in UTILITY_NAMES { - if name.starts_with(input) || name.to_lowercase().starts_with(&input.to_lowercase()) { - matches.push(Pair { - display: name.to_string(), - replacement: name.to_string(), - }); - } - } - - Ok((0, matches)) - } -} - -impl Hinter for ReplHelper { - type Hint = String; - - fn hint(&self, line: &str, _pos: usize, _ctx: &Context<'_>) -> Option { - // Show method signature as hint when a full namespace.method is typed - if let Some(dot_pos) = line.rfind('.') { - let namespace = &line[..dot_pos]; - let method = &line[dot_pos + 1..]; - if let Some(cmd) = self.registry.find(namespace, method) - && !cmd.params.is_empty() - { - let params: Vec = cmd - .params - .iter() - .map(|p| { - if p.required { - format!("<{}>", p.name) - } else { - format!("[{}]", p.name) - } - }) - .collect(); - return Some(format!(" {}", params.join(" "))); - } - } - None - } -} - -impl Highlighter for ReplHelper {} -impl Validator for ReplHelper {} - -#[cfg(test)] -mod tests { - use super::*; - - fn make_helper() -> ReplHelper { - ReplHelper::new(Arc::new(CommandRegistry::new()), VariableStore::new()) - } - - fn make_helper_with_vars() -> ReplHelper { - let vars = VariableStore::new(); - vars.insert( - "head".to_string(), - serde_json::json!({ - "hash": "0xabc123", - "number": "0x1", - "nested": { "deep": "value" } - }), - ); - vars.insert( - "payload".to_string(), - serde_json::json!({ - "executionPayload": { - "blockHash": "0xdef456", - "stateRoot": "0x789" - } - }), - ); - vars.insert("ts".to_string(), serde_json::json!("0x69b9a63d")); - ReplHelper::new(Arc::new(CommandRegistry::new()), vars) - } - - /// Get completions without needing a rustyline Context. - /// Replicates the completion logic since Context is hard to construct. - fn get_completions(helper: &ReplHelper, input: &str) -> Vec { - // Dot commands - if input.starts_with('.') { - let builtins = [ - ".help", ".exit", ".quit", ".clear", ".connect", ".history", ".vars", - ]; - return builtins - .iter() - .filter(|b| b.starts_with(input)) - .map(|b| b.to_string()) - .collect(); - } - - // Variable completion - if let Some(completions) = helper.complete_variable(input) { - return completions.into_iter().map(|p| p.replacement).collect(); - } - - // Method completion after namespace. - if let Some(dot_pos) = input.rfind('.') { - let namespace = &input[..dot_pos]; - let partial_method = &input[dot_pos + 1..]; - let methods = helper.registry.methods_in_namespace(namespace); - return methods - .iter() - .filter(|m| m.name.starts_with(partial_method)) - .map(|m| format!("{}.{}", namespace, m.name)) - .collect(); - } - - // Namespace + utility completion - let mut matches = Vec::new(); - for ns in helper.registry.namespaces() { - if ns.starts_with(input) { - matches.push(format!("{}.", ns)); - } - } - for name in UTILITY_NAMES { - if name.starts_with(input) || name.to_lowercase().starts_with(&input.to_lowercase()) { - matches.push(name.to_string()); - } - } - matches - } - - /// Get hints without needing a rustyline Context. - fn get_hint(helper: &ReplHelper, line: &str) -> Option { - if let Some(dot_pos) = line.rfind('.') { - let namespace = &line[..dot_pos]; - let method = &line[dot_pos + 1..]; - if let Some(cmd) = helper.registry.find(namespace, method) - && !cmd.params.is_empty() - { - let params: Vec = cmd - .params - .iter() - .map(|p| { - if p.required { - format!("<{}>", p.name) - } else { - format!("[{}]", p.name) - } - }) - .collect(); - return Some(format!(" {}", params.join(" "))); - } - } - None - } - - // --- Dot command tests --- - - #[test] - fn complete_dot_h() { - let helper = make_helper(); - let matches = get_completions(&helper, ".h"); - assert!(matches.contains(&".help".to_string())); - assert!(matches.contains(&".history".to_string())); - assert!(!matches.contains(&".exit".to_string())); - } - - #[test] - fn complete_dot_e() { - let helper = make_helper(); - let matches = get_completions(&helper, ".e"); - assert!(matches.contains(&".exit".to_string())); - assert!(!matches.contains(&".help".to_string())); - } - - #[test] - fn complete_dot_x_no_matches() { - let helper = make_helper(); - let matches = get_completions(&helper, ".x"); - assert!(matches.is_empty()); - } - - // --- Namespace completion tests --- - - #[test] - fn complete_namespace_eth() { - let helper = make_helper(); - let matches = get_completions(&helper, "et"); - assert!(matches.contains(&"eth.".to_string())); - } - - #[test] - fn complete_namespace_net() { - let helper = make_helper(); - let matches = get_completions(&helper, "ne"); - assert!(matches.contains(&"net.".to_string())); - } - - #[test] - fn complete_empty_input_shows_all() { - let helper = make_helper(); - let matches = get_completions(&helper, ""); - // Should include all namespaces - for ns in helper.registry.namespaces() { - assert!( - matches.contains(&format!("{}.", ns)), - "missing namespace: {ns}" - ); - } - // Should include all utilities - for name in UTILITY_NAMES { - assert!( - matches.contains(&name.to_string()), - "missing utility: {name}" - ); - } - } - - // --- Method completion tests --- - - #[test] - fn complete_eth_get_methods() { - let helper = make_helper(); - let matches = get_completions(&helper, "eth.get"); - // All results should start with "eth.get" - assert!(!matches.is_empty()); - for m in &matches { - assert!(m.starts_with("eth.get"), "unexpected match: {m}"); - } - } - - #[test] - fn complete_eth_block_number_exact() { - let helper = make_helper(); - let matches = get_completions(&helper, "eth.blockNumber"); - assert_eq!(matches.len(), 1); - assert_eq!(matches[0], "eth.blockNumber"); - } - - #[test] - fn complete_eth_zzz_no_match() { - let helper = make_helper(); - let matches = get_completions(&helper, "eth.zzz"); - assert!(matches.is_empty()); - } - - // --- Utility completion tests --- - - #[test] - fn complete_utility_to() { - let helper = make_helper(); - let matches = get_completions(&helper, "to"); - assert!(matches.contains(&"toWei".to_string())); - assert!(matches.contains(&"toHex".to_string())); - assert!(matches.contains(&"toChecksumAddress".to_string())); - } - - #[test] - fn complete_utility_keccak() { - let helper = make_helper(); - let matches = get_completions(&helper, "keccak"); - assert!(matches.contains(&"keccak256".to_string())); - } - - #[test] - fn complete_utility_case_insensitive() { - let helper = make_helper(); - let matches = get_completions(&helper, "towei"); - // Case-insensitive matching should find "toWei" - assert!(matches.contains(&"toWei".to_string())); - } - - #[test] - fn complete_utility_towel_no_match() { - let helper = make_helper(); - let matches = get_completions(&helper, "towel"); - assert!(matches.is_empty()); - } - - // --- Hinter tests --- - - #[test] - fn hint_eth_get_balance_shows_params() { - let helper = make_helper(); - let hint = get_hint(&helper, "eth.getBalance"); - // eth.getBalance takes
[block], so hint should be present - assert!(hint.is_some()); - let hint_str = hint.unwrap(); - assert!( - hint_str.contains("address"), - "hint should contain address param" - ); - } - - #[test] - fn hint_eth_block_number_no_params() { - let helper = make_helper(); - let hint = get_hint(&helper, "eth.blockNumber"); - // blockNumber takes no params, so no hint - assert!(hint.is_none()); - } - - #[test] - fn hint_unknown_method_returns_none() { - let helper = make_helper(); - let hint = get_hint(&helper, "unknown.method"); - assert!(hint.is_none()); - } - - // --- Variable completion tests --- - - #[test] - fn complete_dollar_lists_all_vars() { - let helper = make_helper_with_vars(); - let matches = get_completions(&helper, "$"); - assert!(matches.contains(&"$head".to_string())); - assert!(matches.contains(&"$payload".to_string())); - assert!(matches.contains(&"$ts".to_string())); - } - - #[test] - fn complete_dollar_partial_name() { - let helper = make_helper_with_vars(); - let matches = get_completions(&helper, "$he"); - assert_eq!(matches, vec!["$head"]); - } - - #[test] - fn complete_dollar_name_dot_lists_fields() { - let helper = make_helper_with_vars(); - let matches = get_completions(&helper, "$head."); - assert!(matches.contains(&"$head.hash".to_string())); - assert!(matches.contains(&"$head.number".to_string())); - assert!(matches.contains(&"$head.nested".to_string())); - } - - #[test] - fn complete_dollar_name_dot_partial_field() { - let helper = make_helper_with_vars(); - let matches = get_completions(&helper, "$head.ha"); - assert_eq!(matches, vec!["$head.hash"]); - } - - #[test] - fn complete_nested_field() { - let helper = make_helper_with_vars(); - let matches = get_completions(&helper, "$payload.executionPayload."); - assert!(matches.contains(&"$payload.executionPayload.blockHash".to_string())); - assert!(matches.contains(&"$payload.executionPayload.stateRoot".to_string())); - } - - #[test] - fn complete_deeply_nested() { - let helper = make_helper_with_vars(); - let matches = get_completions(&helper, "$head.nested."); - assert_eq!(matches, vec!["$head.nested.deep"]); - } - - #[test] - fn complete_var_no_fields_on_string() { - let helper = make_helper_with_vars(); - // $ts is a string, not an object — no field completions - let matches = get_completions(&helper, "$ts."); - assert!(matches.is_empty()); - } - - #[test] - fn complete_var_unknown_name() { - let helper = make_helper_with_vars(); - let matches = get_completions(&helper, "$zzz"); - assert!(matches.is_empty()); - } - - #[test] - fn complete_var_mid_command() { - let helper = make_helper_with_vars(); - // Variable completion in the middle of a command line - let matches = get_completions(&helper, "engine.newPayloadV4 $pay"); - assert_eq!(matches, vec!["$payload"]); - } - - #[test] - fn complete_var_mid_command_field() { - let helper = make_helper_with_vars(); - let matches = get_completions(&helper, "engine.newPayloadV4 $payload.execution"); - assert_eq!(matches, vec!["$payload.executionPayload"]); - } -} diff --git a/tooling/repl/src/ens.rs b/tooling/repl/src/ens.rs deleted file mode 100644 index 30ce95680ef..00000000000 --- a/tooling/repl/src/ens.rs +++ /dev/null @@ -1,315 +0,0 @@ -use serde_json::{Value, json}; -use sha3::{Digest, Keccak256}; - -use crate::client::RpcClient; - -/// ENS registry contract address (same on mainnet and Sepolia). -const ENS_REGISTRY: &str = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"; - -/// Compute the ENS namehash for a domain name. -/// -/// namehash("") = [0u8; 32] -/// namehash("vitalik.eth") = keccak256(namehash("eth") ++ keccak256("vitalik")) -fn namehash(name: &str) -> [u8; 32] { - if name.is_empty() { - return [0u8; 32]; - } - - let mut node = [0u8; 32]; - for label in name.rsplit('.') { - let label_lower = label.to_lowercase(); - let label_hash = Keccak256::digest(label_lower.as_bytes()); - let mut data = [0u8; 64]; - data[..32].copy_from_slice(&node); - data[32..].copy_from_slice(&label_hash); - node = Keccak256::digest(data).into(); - } - node -} - -/// Returns true if the string looks like an ENS name (contains `.` and doesn't start with `0x`). -pub fn looks_like_ens_name(s: &str) -> bool { - !s.starts_with("0x") && s.contains('.') -} - -/// Resolve an ENS name to a checksummed `0x`-prefixed address. -pub async fn resolve(client: &RpcClient, name: &str) -> Result { - let node = namehash(name); - let node_hex = hex::encode(node); - - // Call resolver(bytes32) on the ENS registry — selector 0x0178b8bf - let resolver_calldata = format!("0x0178b8bf{node_hex}"); - let resolver_result = eth_call(client, ENS_REGISTRY, &resolver_calldata).await?; - - let resolver_addr = parse_address_from_abi_word(&resolver_result)?; - if resolver_addr == "0x0000000000000000000000000000000000000000" { - return Err(format!("ENS name not found: {name}")); - } - - // Call addr(bytes32) on the resolver — selector 0x3b3b57de - let addr_calldata = format!("0x3b3b57de{node_hex}"); - let addr_result = eth_call(client, &resolver_addr, &addr_calldata).await?; - - let resolved = parse_address_from_abi_word(&addr_result)?; - if resolved == "0x0000000000000000000000000000000000000000" { - return Err(format!("ENS name has no address set: {name}")); - } - - Ok(to_checksum_address(&resolved)) -} - -/// Execute an `eth_call` with the given `to` and `data`, returning the raw hex result. -async fn eth_call(client: &RpcClient, to: &str, data: &str) -> Result { - let params = vec![ - json!({"to": to, "data": data}), - Value::String("latest".to_string()), - ]; - - client - .send_request("eth_call", params) - .await - .map_err(|e| format!("ENS resolution failed: {e}")) - .and_then(|v| { - v.as_str() - .map(String::from) - .ok_or_else(|| "ENS resolution returned non-string result".to_string()) - }) -} - -/// Parse the last 20 bytes of a 32-byte ABI-encoded word as a `0x`-prefixed address. -fn parse_address_from_abi_word(hex_str: &str) -> Result { - let hex = hex_str.strip_prefix("0x").unwrap_or(hex_str); - if hex.len() != 64 || !hex.chars().all(|c| c.is_ascii_hexdigit()) { - return Err(format!( - "unexpected ABI word (expected 64 hex chars): 0x{hex}" - )); - } - // Last 40 hex chars = 20 bytes = address - let addr = &hex[hex.len() - 40..]; - Ok(format!("0x{addr}")) -} - -/// EIP-55 checksum encoding. -pub fn to_checksum_address(addr: &str) -> String { - let addr_lower = addr.strip_prefix("0x").unwrap_or(addr).to_lowercase(); - let hash = Keccak256::digest(addr_lower.as_bytes()); - let hash_hex = hex::encode(hash); - - let mut checksummed = String::from("0x"); - for (i, c) in addr_lower.chars().enumerate() { - if c.is_ascii_alphabetic() { - let nibble = u8::from_str_radix(&hash_hex[i..i + 1], 16).unwrap_or(0); - if nibble >= 8 { - checksummed.push(c.to_ascii_uppercase()); - } else { - checksummed.push(c); - } - } else { - checksummed.push(c); - } - } - checksummed -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn namehash_empty() { - assert_eq!(namehash(""), [0u8; 32]); - } - - #[test] - fn namehash_eth() { - // Well-known: namehash("eth") = 0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae - let result = hex::encode(namehash("eth")); - assert_eq!( - result, - "93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae" - ); - } - - #[test] - fn namehash_vitalik_eth() { - // namehash("vitalik.eth") is a well-known test vector - let result = hex::encode(namehash("vitalik.eth")); - assert_eq!( - result, - "ee6c4522aab0003e8d14cd40a6af439055fd2577951148c14b6cea9a53475835" - ); - } - - #[test] - fn ens_name_detection() { - assert!(looks_like_ens_name("vitalik.eth")); - assert!(looks_like_ens_name("foo.bar.eth")); - assert!(!looks_like_ens_name( - "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" - )); - assert!(!looks_like_ens_name("latest")); - assert!(!looks_like_ens_name("12345")); - } - - #[test] - fn parse_address_from_word() { - // 32-byte ABI word with address in last 20 bytes - let word = "0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045"; - let addr = parse_address_from_abi_word(word).unwrap(); - assert_eq!(addr, "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); - } - - #[test] - fn checksum_address() { - let addr = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"; - assert_eq!( - to_checksum_address(addr), - "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" - ); - } - - #[test] - fn namehash_single_label_com() { - // namehash("com") should be a non-zero hash, different from namehash("eth") - let result = namehash("com"); - assert_ne!(result, [0u8; 32]); - assert_ne!(result, namehash("eth")); - } - - #[test] - fn namehash_deep_nesting() { - let deep = namehash("sub.domain.eth"); - let mid = namehash("domain.eth"); - let top = namehash("eth"); - // All three should be different - assert_ne!(deep, mid); - assert_ne!(deep, top); - assert_ne!(mid, top); - } - - #[test] - fn namehash_case_insensitive() { - // ENS namehash normalizes labels to lowercase before hashing - let lower = namehash("eth"); - let upper = namehash("ETH"); - assert_eq!(lower, upper); - } - - #[test] - fn namehash_trailing_dot() { - // Trailing dot creates an extra empty label at the start of rsplit - let with_dot = namehash("eth."); - let without_dot = namehash("eth"); - // The trailing dot splits into ["", "eth"], so the hash differs - assert_ne!(with_dot, without_dot); - } - - #[test] - fn namehash_unicode_no_panic() { - // Should not panic on unicode input - let _ = namehash("🦀.eth"); - let _ = namehash("café.eth"); - let _ = namehash("日本語.eth"); - } - - #[test] - fn ens_name_empty_string() { - assert!(!looks_like_ens_name("")); - } - - #[test] - fn ens_name_just_dot() { - // Contains '.' and doesn't start with 0x - assert!(looks_like_ens_name(".")); - } - - #[test] - fn ens_name_starts_with_dot() { - assert!(looks_like_ens_name(".eth")); - } - - #[test] - fn ens_name_0x_prefix_with_dot() { - // Starts with 0x, so not an ENS name even though it contains a dot - assert!(!looks_like_ens_name("0x.eth")); - } - - #[test] - fn ens_name_no_dot() { - assert!(!looks_like_ens_name("foo")); - } - - #[test] - fn ens_name_deeply_nested() { - assert!(looks_like_ens_name("foo.bar.baz.eth")); - } - - #[test] - fn parse_address_valid_64_char_hex() { - // 64 hex chars (32 bytes), address in last 20 bytes - let word = "000000000000000000000000abcdefabcdefabcdefabcdefabcdefabcdefabcd"; - let addr = parse_address_from_abi_word(word).unwrap(); - assert_eq!(addr, "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"); - } - - #[test] - fn parse_address_shorter_than_40_chars() { - let word = "0xabcdef"; - assert!(parse_address_from_abi_word(word).is_err()); - } - - #[test] - fn parse_address_without_0x_prefix() { - let word = "000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045"; - let addr = parse_address_from_abi_word(word).unwrap(); - assert_eq!(addr, "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"); - } - - #[test] - fn parse_address_exactly_40_chars_rejected() { - // Exactly 40 hex chars is not a valid ABI word (must be 64) - let word = "d8da6bf26964af9d7eed9e03e53415d37aa96045"; - assert!(parse_address_from_abi_word(word).is_err()); - } - - #[test] - fn checksum_all_lowercase_input() { - let addr = "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359"; - let checksummed = to_checksum_address(addr); - // Should produce a deterministic EIP-55 result - assert!(checksummed.starts_with("0x")); - assert_eq!(checksummed.len(), 42); - } - - #[test] - fn checksum_all_uppercase_input() { - // Uppercase input should produce the same result as lowercase - let addr_upper = "0xD8DA6BF26964AF9D7EED9E03E53415D37AA96045"; - let addr_lower = "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"; - assert_eq!( - to_checksum_address(addr_upper), - to_checksum_address(addr_lower) - ); - } - - #[test] - fn checksum_already_checksummed() { - let addr = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; - assert_eq!(to_checksum_address(addr), addr); - } - - #[test] - fn checksum_zero_address() { - let addr = "0x0000000000000000000000000000000000000000"; - let checksummed = to_checksum_address(addr); - // Zero address has no alpha chars, so it stays all lowercase - assert_eq!(checksummed, "0x0000000000000000000000000000000000000000"); - } - - #[test] - fn checksum_without_0x_prefix() { - let addr = "d8da6bf26964af9d7eed9e03e53415d37aa96045"; - let checksummed = to_checksum_address(addr); - assert_eq!(checksummed, "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); - } -} diff --git a/tooling/repl/src/formatter.rs b/tooling/repl/src/formatter.rs deleted file mode 100644 index b2975a62e13..00000000000 --- a/tooling/repl/src/formatter.rs +++ /dev/null @@ -1,495 +0,0 @@ -use colored::Colorize; -use serde_json::Value; - -use crate::commands::CommandDef; - -const MAX_VALUE_DISPLAY_LEN: usize = 72; - -pub fn format_value(value: &Value) -> String { - match value { - Value::Null => "null".dimmed().to_string(), - Value::Bool(b) => b.to_string().yellow().to_string(), - Value::Number(n) => n.to_string().green().to_string(), - Value::String(s) => format_string_value(s), - Value::Array(arr) => format_array(arr), - Value::Object(map) => format_object_box(map, ""), - } -} - -fn format_string_value(s: &str) -> String { - if s.starts_with("0x") { - if s.len() == 42 { - // Ethereum address - s.cyan().to_string() - } else if s.len() == 66 { - // Transaction/block hash (32 bytes) - s.yellow().to_string() - } else if let Some(decimal) = hex_to_decimal(s) { - // Hex quantity → show as decimal - decimal.green().to_string() - } else { - // Other hex data (bytecode, etc.) - truncate_middle(s, MAX_VALUE_DISPLAY_LEN) - .magenta() - .to_string() - } - } else { - s.white().to_string() - } -} - -fn format_array(arr: &[Value]) -> String { - if arr.is_empty() { - return "[]".to_string(); - } - - if arr.iter().all(|v| v.is_object()) { - let mut out = String::new(); - for (i, v) in arr.iter().enumerate() { - if let Value::Object(map) = v { - out.push_str(&format_object_box(map, &format!(" [{}] ", i))); - } - if i < arr.len() - 1 { - out.push('\n'); - } - } - out - } else { - let items: Vec = arr - .iter() - .map(|v| format!(" {}", format_value(v))) - .collect(); - format!("[\n{}\n]", items.join(",\n")) - } -} - -fn format_object_box(map: &serde_json::Map, title: &str) -> String { - if map.is_empty() { - return "{}".to_string(); - } - - let rows = flatten_object(map, ""); - - let key_w = rows.iter().map(|(k, _)| k.len()).max().unwrap_or(0); - let val_w = rows - .iter() - .map(|(_, v)| v.len()) - .max() - .unwrap_or(0) - .min(MAX_VALUE_DISPLAY_LEN); - let content_w = key_w + 3 + val_w; - let box_w = content_w + 4; // "│ " + content + " │" - - let mut out = String::new(); - - // Top border - if title.is_empty() { - out.push_str(&format!("┌{}┐\n", "─".repeat(box_w - 2))); - } else { - let fill = (box_w - 2).saturating_sub(title.len() + 1); - out.push_str(&format!("┌─{}{}┐\n", title.bold(), "─".repeat(fill))); - } - - // Rows - for (key, value) in &rows { - let display_val = truncate_middle(value, val_w); - let key_pad = " ".repeat(key_w.saturating_sub(key.len())); - let val_pad = " ".repeat(val_w.saturating_sub(display_val.len())); - out.push_str(&format!( - "│ {}{} {}{} │\n", - key_pad, - key.cyan(), - colorize_inline(&display_val), - val_pad, - )); - } - - // Bottom border - out.push_str(&format!("└{}┘", "─".repeat(box_w - 2))); - - out -} - -/// Flatten a JSON object into (key, plain-text-value) pairs. -/// Nested objects are expanded with dot-separated keys. -fn flatten_object(map: &serde_json::Map, prefix: &str) -> Vec<(String, String)> { - let mut rows = Vec::new(); - for (key, value) in map { - let full_key = if prefix.is_empty() { - key.clone() - } else { - format!("{prefix}.{key}") - }; - match value { - Value::Object(nested) if !nested.is_empty() => { - rows.extend(flatten_object(nested, &full_key)); - } - Value::Array(arr) => { - let items: Vec = arr.iter().map(inline_value).collect(); - rows.push((full_key, items.join(", "))); - } - _ => { - rows.push((full_key, inline_value(value))); - } - } - } - rows -} - -/// Convert a Value to a plain-text string for table cells. -fn inline_value(value: &Value) -> String { - match value { - Value::Null => "null".to_string(), - Value::Bool(b) => b.to_string(), - Value::Number(n) => n.to_string(), - Value::String(s) => { - // Convert hex quantities to decimal - if s.starts_with("0x") - && s.len() != 42 - && s.len() != 66 - && let Some(decimal) = hex_to_decimal(s) - { - return decimal; - } - s.clone() - } - Value::Array(arr) => { - let items: Vec = arr.iter().map(inline_value).collect(); - format!("[{}]", items.join(", ")) - } - Value::Object(map) => { - let items: Vec = map - .iter() - .map(|(k, v)| format!("{k}: {}", inline_value(v))) - .collect(); - format!("{{{}}}", items.join(", ")) - } - } -} - -/// Apply color to a plain-text value based on its content. -fn colorize_inline(s: &str) -> String { - if s == "true" || s == "false" { - s.yellow().to_string() - } else if s == "null" { - s.dimmed().to_string() - } else if s.starts_with("0x") && s.len() == 42 { - s.cyan().to_string() - } else if s.starts_with("0x") { - s.yellow().to_string() - } else if !s.is_empty() && (s.chars().all(|c| c.is_ascii_digit()) || is_decimal_float(s)) { - s.green().to_string() - } else { - s.to_string() - } -} - -fn is_decimal_float(s: &str) -> bool { - let mut has_dot = false; - for c in s.chars() { - if c == '.' { - if has_dot { - return false; - } - has_dot = true; - } else if !c.is_ascii_digit() { - return false; - } - } - has_dot && s.len() > 1 -} - -/// Try to parse a 0x-prefixed hex string as a decimal number. -fn hex_to_decimal(s: &str) -> Option { - let hex = s.strip_prefix("0x")?; - if hex.is_empty() || hex.len() > 32 || !hex.chars().all(|c| c.is_ascii_hexdigit()) { - return None; - } - let n = u128::from_str_radix(hex, 16).ok()?; - Some(n.to_string()) -} - -fn truncate_middle(s: &str, max_len: usize) -> String { - if s.chars().count() <= max_len || max_len < 7 { - return s.to_string(); - } - let keep = (max_len - 3) / 2; - let start: String = s.chars().take(keep).collect(); - let end: String = s - .chars() - .rev() - .take(keep) - .collect::>() - .into_iter() - .rev() - .collect(); - format!("{start}...{end}") -} - -pub fn format_error(msg: &str) -> String { - format!("{} {}", "Error:".red().bold(), msg.red()) -} - -/// Format a command definition as a usage string: `namespace.method [optional]` -pub fn command_usage(cmd: &CommandDef) -> String { - let mut usage = format!("{}.{}", cmd.namespace, cmd.name); - for p in cmd.params { - if p.required { - usage.push_str(&format!(" <{}>", p.name)); - } else { - usage.push_str(&format!(" [{}]", p.name)); - } - } - usage -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::commands::{ParamDef, ParamType}; - use serde_json::json; - - // --- hex_to_decimal --- - - #[test] - fn test_hex_to_decimal_zero() { - assert_eq!(hex_to_decimal("0x0"), Some("0".to_string())); - } - - #[test] - fn test_hex_to_decimal_a() { - assert_eq!(hex_to_decimal("0xa"), Some("10".to_string())); - } - - #[test] - fn test_hex_to_decimal_ff() { - assert_eq!(hex_to_decimal("0xff"), Some("255".to_string())); - } - - #[test] - fn test_hex_to_decimal_empty_hex() { - assert_eq!(hex_to_decimal("0x"), None); - } - - #[test] - fn test_hex_to_decimal_non_hex() { - assert_eq!(hex_to_decimal("0xzz"), None); - } - - #[test] - fn test_hex_to_decimal_too_long() { - let long = format!("0x{}", "f".repeat(33)); - assert_eq!(hex_to_decimal(&long), None); - } - - #[test] - fn test_hex_to_decimal_no_prefix() { - assert_eq!(hex_to_decimal("ff"), None); - } - - // --- truncate_middle --- - - #[test] - fn test_truncate_short_string() { - assert_eq!(truncate_middle("hello", 10), "hello"); - } - - #[test] - fn test_truncate_exact_max() { - assert_eq!(truncate_middle("hello", 5), "hello"); - } - - #[test] - fn test_truncate_longer_than_max() { - let result = truncate_middle("0123456789abcdef", 10); - assert!(result.contains("...")); - assert!(result.len() <= 10); - } - - #[test] - fn test_truncate_max_lt_7_unchanged() { - assert_eq!(truncate_middle("long string here", 6), "long string here"); - } - - // --- inline_value --- - - #[test] - fn test_inline_null() { - assert_eq!(inline_value(&json!(null)), "null"); - } - - #[test] - fn test_inline_bool_true() { - assert_eq!(inline_value(&json!(true)), "true"); - } - - #[test] - fn test_inline_bool_false() { - assert_eq!(inline_value(&json!(false)), "false"); - } - - #[test] - fn test_inline_number() { - assert_eq!(inline_value(&json!(42)), "42"); - } - - #[test] - fn test_inline_hex_string_to_decimal() { - assert_eq!(inline_value(&json!("0xa")), "10"); - } - - #[test] - fn test_inline_address_unchanged() { - let addr = "0x1234567890abcdef1234567890abcdef12345678"; - assert_eq!(inline_value(&json!(addr)), addr); - } - - #[test] - fn test_inline_hash_unchanged() { - let hash = format!("0x{}", "ab".repeat(32)); - assert_eq!(inline_value(&json!(hash)), hash); - } - - #[test] - fn test_inline_nested_array() { - assert_eq!(inline_value(&json!([1, 2])), "[1, 2]"); - } - - #[test] - fn test_inline_nested_object() { - let result = inline_value(&json!({"a": 1})); - assert!(result.contains("a: 1")); - } - - #[test] - fn test_inline_plain_string() { - assert_eq!(inline_value(&json!("hello")), "hello"); - } - - // --- flatten_object --- - - #[test] - fn test_flatten_simple() { - let map = - serde_json::from_str::>(r#"{"a":"1","b":"2"}"#).unwrap(); - let rows = flatten_object(&map, ""); - assert_eq!(rows.len(), 2); - assert!(rows.iter().any(|(k, _)| k == "a")); - assert!(rows.iter().any(|(k, _)| k == "b")); - } - - #[test] - fn test_flatten_nested() { - let map = - serde_json::from_str::>(r#"{"a":{"b":"1"}}"#).unwrap(); - let rows = flatten_object(&map, ""); - assert_eq!(rows.len(), 1); - assert_eq!(rows[0].0, "a.b"); - assert_eq!(rows[0].1, "1"); - } - - #[test] - fn test_flatten_array_values() { - let map = - serde_json::from_str::>(r#"{"tags":[1,2,3]}"#).unwrap(); - let rows = flatten_object(&map, ""); - assert_eq!(rows.len(), 1); - assert_eq!(rows[0].0, "tags"); - assert_eq!(rows[0].1, "1, 2, 3"); - } - - #[test] - fn test_flatten_empty_nested_object() { - let map = serde_json::from_str::>(r#"{"a":{}}"#).unwrap(); - let rows = flatten_object(&map, ""); - assert_eq!(rows.len(), 1); - assert_eq!(rows[0].0, "a"); - assert_eq!(rows[0].1, "{}"); - } - - #[test] - fn test_flatten_with_prefix() { - let map = serde_json::from_str::>(r#"{"x":"1"}"#).unwrap(); - let rows = flatten_object(&map, "parent"); - assert_eq!(rows[0].0, "parent.x"); - } - - // --- is_decimal_float --- - - #[test] - fn test_is_decimal_float_valid() { - assert!(is_decimal_float("1.5")); - } - - #[test] - fn test_is_decimal_float_integer() { - assert!(!is_decimal_float("1")); - } - - #[test] - fn test_is_decimal_float_double_dot() { - assert!(!is_decimal_float("1.2.3")); - } - - #[test] - fn test_is_decimal_float_dot_prefix() { - assert!(is_decimal_float(".1")); - } - - #[test] - fn test_is_decimal_float_empty() { - assert!(!is_decimal_float("")); - } - - #[test] - fn test_is_decimal_float_only_dot() { - assert!(!is_decimal_float(".")); - } - - // --- command_usage --- - - fn make_params(defs: Vec) -> &'static [ParamDef] { - Box::leak(defs.into_boxed_slice()) - } - - #[test] - fn test_command_usage_required_and_optional() { - let cmd = CommandDef { - namespace: "eth", - name: "getBalance", - rpc_method: "eth_getBalance", - params: make_params(vec![ - ParamDef { - name: "address", - param_type: ParamType::Address, - required: true, - default_value: None, - description: "address", - }, - ParamDef { - name: "block", - param_type: ParamType::BlockId, - required: false, - default_value: Some("latest"), - description: "block id", - }, - ]), - description: "get balance", - }; - let usage = command_usage(&cmd); - assert_eq!(usage, "eth.getBalance
[block]"); - } - - #[test] - fn test_command_usage_no_params() { - let cmd = CommandDef { - namespace: "eth", - name: "blockNumber", - rpc_method: "eth_blockNumber", - params: &[], - description: "block number", - }; - let usage = command_usage(&cmd); - assert_eq!(usage, "eth.blockNumber"); - } -} diff --git a/tooling/repl/src/lib.rs b/tooling/repl/src/lib.rs deleted file mode 100644 index 9a459f1fc56..00000000000 --- a/tooling/repl/src/lib.rs +++ /dev/null @@ -1,59 +0,0 @@ -pub mod client; -pub mod commands; -pub mod completer; -mod ens; -pub mod formatter; -pub mod parser; -pub mod repl; -pub mod variables; - -use client::RpcClient; -use repl::Repl; - -/// Run the REPL with the given configuration. -/// -/// If `execute` is `Some`, runs a single command and exits. -/// Otherwise, starts the interactive REPL loop. -pub async fn run( - endpoint: String, - authrpc_endpoint: String, - authrpc_jwtsecret: Option, - history_file: String, - execute: Option, -) { - let history_path = expand_tilde(&history_file); - let client = RpcClient::new(endpoint); - - let authrpc_client = authrpc_jwtsecret.map(|path| { - let secret = read_jwtsecret_file(&path); - RpcClient::new_with_jwt(authrpc_endpoint, secret) - }); - - if let Some(command) = execute { - let repl = Repl::new(client, authrpc_client, history_path); - let result = repl.execute_command(&command).await; - if !result.is_empty() { - println!("{result}"); - } - return; - } - - let mut repl = Repl::new(client, authrpc_client, history_path); - repl.run().await; -} - -fn read_jwtsecret_file(path: &str) -> Vec { - let content = std::fs::read_to_string(path) - .unwrap_or_else(|e| panic!("Failed to read JWT secret from {path}: {e}")); - let hex_str = content.trim().strip_prefix("0x").unwrap_or(content.trim()); - hex::decode(hex_str).unwrap_or_else(|e| panic!("Invalid hex in JWT secret file: {e}")) -} - -fn expand_tilde(path: &str) -> String { - if path.starts_with('~') - && let Ok(home) = std::env::var("HOME") - { - return path.replacen('~', &home, 1); - } - path.to_string() -} diff --git a/tooling/repl/src/main.rs b/tooling/repl/src/main.rs deleted file mode 100644 index e5598deb5b8..00000000000 --- a/tooling/repl/src/main.rs +++ /dev/null @@ -1,38 +0,0 @@ -use clap::Parser; - -#[derive(Parser)] -#[command(name = "ethrex-repl", about = "Interactive REPL for Ethereum JSON-RPC")] -struct Cli { - /// JSON-RPC endpoint URL - #[arg(short = 'e', long, default_value = "http://localhost:8545")] - endpoint: String, - - /// Authenticated RPC endpoint URL (for engine namespace) - #[arg(long = "authrpc.endpoint", default_value = "http://localhost:8551")] - authrpc_endpoint: String, - - /// Path to JWT secret file for authenticated RPC (hex-encoded) - #[arg(long = "authrpc.jwtsecret")] - authrpc_jwtsecret: Option, - - /// Path to command history file - #[arg(long, default_value = "~/.ethrex/history")] - history_file: String, - - /// Execute a single command and exit - #[arg(short = 'x', long)] - execute: Option, -} - -#[tokio::main] -async fn main() { - let cli = Cli::parse(); - ethrex_repl::run( - cli.endpoint, - cli.authrpc_endpoint, - cli.authrpc_jwtsecret, - cli.history_file, - cli.execute, - ) - .await; -} diff --git a/tooling/repl/src/parser.rs b/tooling/repl/src/parser.rs deleted file mode 100644 index 2a4d9f558b5..00000000000 --- a/tooling/repl/src/parser.rs +++ /dev/null @@ -1,1131 +0,0 @@ -use serde_json::Value; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum ParseError { - #[error("unexpected end of input")] - UnexpectedEof, - #[error("unexpected character: '{0}'")] - UnexpectedChar(char), - #[error("unterminated string")] - UnterminatedString, - #[error("unterminated JSON")] - UnterminatedJson, - #[error("invalid JSON: {0}")] - InvalidJson(String), -} - -#[derive(Debug, Clone, PartialEq)] -pub enum Token { - Ident(String), - Dot, - LParen, - RParen, - Comma, - String(String), - Number(String), - Bool(bool), - JsonObject(String), - JsonArray(String), - Colon, - Equals, - Plus, - Minus, - VarRef { name: String, path: Vec }, -} - -pub const UTILITY_NAMES: &[&str] = &[ - "toWei", - "fromWei", - "toHex", - "fromHex", - "keccak256", - "toChecksumAddress", - "isAddress", -]; - -/// An argument to an RPC call — either a literal JSON value or a variable reference. -#[derive(Debug, Clone)] -pub enum RpcArg { - Literal(Value), - VarRef { - name: String, - path: Vec, - /// Optional arithmetic offset: `$var + 1` or `$var - 5`. - offset: Option, - }, -} - -#[derive(Debug, Clone)] -pub enum ParsedCommand { - RpcCall { - namespace: String, - method: String, - args: Vec, - }, - Assignment { - var_name: String, - command: Box, - }, - PrintVar { - name: String, - path: Vec, - offset: Option, - }, - BuiltinCommand { - name: String, - args: Vec, - }, - UtilityCall { - name: String, - args: Vec, - }, - Empty, -} - -struct Tokenizer<'a> { - input: &'a [u8], - pos: usize, -} - -impl<'a> Tokenizer<'a> { - fn new(input: &'a str) -> Self { - Self { - input: input.as_bytes(), - pos: 0, - } - } - - fn peek(&self) -> Option { - self.input.get(self.pos).copied() - } - - fn advance(&mut self) -> Option { - let ch = self.input.get(self.pos).copied()?; - self.pos += 1; - Some(ch) - } - - fn skip_whitespace(&mut self) { - while let Some(ch) = self.peek() { - if ch == b' ' || ch == b'\t' || ch == b'\r' || ch == b'\n' { - self.pos += 1; - } else { - break; - } - } - } - - fn read_string(&mut self, quote: u8) -> Result { - let mut s = Vec::new(); - loop { - match self.advance() { - None => return Err(ParseError::UnterminatedString), - Some(ch) if ch == quote => return Ok(String::from_utf8_lossy(&s).into_owned()), - Some(b'\\') => match self.advance() { - None => return Err(ParseError::UnterminatedString), - Some(b'n') => s.push(b'\n'), - Some(b't') => s.push(b'\t'), - Some(b'\\') => s.push(b'\\'), - Some(ch) if ch == quote => s.push(quote), - Some(ch) => { - s.push(b'\\'); - s.push(ch); - } - }, - Some(ch) => s.push(ch), - } - } - } - - fn read_json_block(&mut self, open: u8, close: u8) -> Result { - let start = self.pos - 1; // include the opening brace/bracket - let mut depth = 1u32; - while depth > 0 { - match self.advance() { - None => return Err(ParseError::UnterminatedJson), - Some(b'"') => { - // skip string contents inside JSON - loop { - match self.advance() { - None => return Err(ParseError::UnterminatedJson), - Some(b'\\') => { - self.advance(); // skip escaped char - } - Some(b'"') => break, - _ => {} - } - } - } - Some(ch) if ch == open => depth += 1, - Some(ch) if ch == close => depth -= 1, - _ => {} - } - } - let json_str = String::from_utf8_lossy(&self.input[start..self.pos]).into_owned(); - // Validate JSON - serde_json::from_str::(&json_str) - .map_err(|e| ParseError::InvalidJson(e.to_string()))?; - Ok(json_str) - } - - fn read_ident(&mut self) -> String { - let start = self.pos - 1; - while let Some(ch) = self.peek() { - if ch.is_ascii_alphanumeric() || ch == b'_' { - self.pos += 1; - } else { - break; - } - } - String::from_utf8_lossy(&self.input[start..self.pos]).into_owned() - } - - /// Read a variable reference: `$name` or `$name.field.nested` - fn read_var_ref(&mut self) -> Result { - // Read the variable name (must start with letter/underscore) - let name_start = self.pos; - while let Some(ch) = self.peek() { - if ch.is_ascii_alphanumeric() || ch == b'_' { - self.pos += 1; - } else { - break; - } - } - if self.pos == name_start { - return Err(ParseError::UnexpectedChar('$')); - } - let name = String::from_utf8_lossy(&self.input[name_start..self.pos]).into_owned(); - - // Read optional dot-separated path: .field.nested - let mut path = Vec::new(); - while self.peek() == Some(b'.') { - // Check that the char after '.' is alphanumeric (not another dot or operator) - if self - .input - .get(self.pos + 1) - .is_some_and(|ch| ch.is_ascii_alphabetic() || *ch == b'_') - { - self.pos += 1; // consume '.' - let seg_start = self.pos; - while let Some(ch) = self.peek() { - if ch.is_ascii_alphanumeric() || ch == b'_' { - self.pos += 1; - } else { - break; - } - } - path.push(String::from_utf8_lossy(&self.input[seg_start..self.pos]).into_owned()); - } else { - break; - } - } - - Ok(Token::VarRef { name, path }) - } - - fn read_number_or_hex(&mut self, first: u8) -> String { - let start = self.pos - 1; - // Check for 0x prefix - if first == b'0' && self.peek() == Some(b'x') { - self.pos += 1; // consume 'x' - while let Some(ch) = self.peek() { - if ch.is_ascii_hexdigit() { - self.pos += 1; - } else { - break; - } - } - } else { - while let Some(ch) = self.peek() { - if ch.is_ascii_digit() || ch == b'.' { - self.pos += 1; - } else { - break; - } - } - } - String::from_utf8_lossy(&self.input[start..self.pos]).into_owned() - } - - fn tokenize(&mut self) -> Result, ParseError> { - let mut tokens = Vec::new(); - loop { - self.skip_whitespace(); - let ch = match self.advance() { - None => break, - Some(ch) => ch, - }; - let tok = match ch { - b'.' => Token::Dot, - b'(' => Token::LParen, - b')' => Token::RParen, - b',' => Token::Comma, - b':' => Token::Colon, - b'"' | b'\'' => Token::String(self.read_string(ch)?), - b'{' => Token::JsonObject(self.read_json_block(b'{', b'}')?), - b'[' => Token::JsonArray(self.read_json_block(b'[', b']')?), - b'=' => Token::Equals, - b'+' => Token::Plus, - b'-' => Token::Minus, - b'$' => self.read_var_ref()?, - ch if ch.is_ascii_digit() => Token::Number(self.read_number_or_hex(ch)), - ch if ch.is_ascii_alphabetic() || ch == b'_' => { - let ident = self.read_ident(); - match ident.as_str() { - "true" => Token::Bool(true), - "false" => Token::Bool(false), - _ => Token::Ident(ident), - } - } - ch => return Err(ParseError::UnexpectedChar(ch as char)), - }; - tokens.push(tok); - } - Ok(tokens) - } -} - -fn token_to_rpc_arg(token: &Token) -> RpcArg { - match token { - Token::VarRef { name, path } => RpcArg::VarRef { - name: name.clone(), - path: path.clone(), - offset: None, - }, - other => RpcArg::Literal(token_to_value(other)), - } -} - -fn token_to_value(token: &Token) -> Value { - match token { - Token::String(s) => Value::String(s.clone()), - Token::Number(n) => Value::String(n.clone()), - Token::Bool(b) => Value::Bool(*b), - Token::JsonObject(s) | Token::JsonArray(s) => { - serde_json::from_str(s).unwrap_or(Value::String(s.clone())) - } - Token::Ident(s) => Value::String(s.clone()), - _ => Value::Null, - } -} - -fn token_to_string(token: &Token) -> String { - match token { - Token::String(s) | Token::Number(s) | Token::Ident(s) => s.clone(), - Token::Bool(b) => b.to_string(), - Token::JsonObject(s) | Token::JsonArray(s) => s.clone(), - Token::Dot => ".".to_string(), - Token::LParen => "(".to_string(), - Token::RParen => ")".to_string(), - Token::Comma => ",".to_string(), - Token::Colon => ":".to_string(), - Token::Equals => "=".to_string(), - Token::Plus => "+".to_string(), - Token::Minus => "-".to_string(), - Token::VarRef { name, path } => { - if path.is_empty() { - format!("${name}") - } else { - format!("${name}.{}", path.join(".")) - } - } - } -} - -pub fn parse(input: &str) -> Result { - let trimmed = input.trim(); - if trimmed.is_empty() { - return Ok(ParsedCommand::Empty); - } - - // Builtin commands start with "." - if let Some(rest) = trimmed.strip_prefix('.') { - let parts: Vec<&str> = rest.split_whitespace().collect(); - let name = parts.first().unwrap_or(&"").to_string(); - let args = parts[1..].iter().map(|s| s.to_string()).collect(); - return Ok(ParsedCommand::BuiltinCommand { name, args }); - } - - let mut tokenizer = Tokenizer::new(trimmed); - let tokens = tokenizer.tokenize()?; - - if tokens.is_empty() { - return Ok(ParsedCommand::Empty); - } - - // Check for assignment: `name = command...` - if tokens.len() >= 2 - && let Token::Ident(var_name) = &tokens[0] - && tokens[1] == Token::Equals - { - // The rest after `=` is the command to execute - let rest_tokens = &tokens[2..]; - let inner_cmd = parse_tokens_as_command(rest_tokens)?; - return Ok(ParsedCommand::Assignment { - var_name: var_name.clone(), - command: Box::new(inner_cmd), - }); - } - - // Check for variable print: `$name`, `$name.path`, or `$name + N` - if let Some(Token::VarRef { name, path }) = tokens.first() { - if tokens.len() == 1 { - return Ok(ParsedCommand::PrintVar { - name: name.clone(), - path: path.clone(), - offset: None, - }); - } - if tokens.len() == 3 - && matches!(tokens[1], Token::Plus | Token::Minus) - && let Token::Number(n) = &tokens[2] - { - let sign: i64 = if matches!(tokens[1], Token::Minus) { - -1 - } else { - 1 - }; - let value: i64 = n.parse().map_err(|_| { - ParseError::InvalidJson(format!("invalid number in arithmetic: {n}")) - })?; - return Ok(ParsedCommand::PrintVar { - name: name.clone(), - path: path.clone(), - offset: Some(sign * value), - }); - } - } - - // Check for namespace.method pattern (RPC call) - if tokens.len() >= 3 - && let (Token::Ident(ns), Token::Dot, Token::Ident(method)) = - (&tokens[0], &tokens[1], &tokens[2]) - { - let args = parse_rpc_args(&tokens[3..])?; - return Ok(ParsedCommand::RpcCall { - namespace: ns.clone(), - method: method.clone(), - args, - }); - } - - // Check for utility call - if let Token::Ident(name) = &tokens[0] - && UTILITY_NAMES.contains(&name.as_str()) - { - let args = tokens[1..] - .iter() - .filter(|t| !matches!(t, Token::LParen | Token::RParen | Token::Comma)) - .map(token_to_string) - .collect(); - return Ok(ParsedCommand::UtilityCall { - name: name.clone(), - args, - }); - } - - // Fallback: treat as utility call with first ident - if let Token::Ident(name) = &tokens[0] { - let args = tokens[1..] - .iter() - .filter(|t| !matches!(t, Token::LParen | Token::RParen | Token::Comma)) - .map(token_to_string) - .collect(); - return Ok(ParsedCommand::UtilityCall { - name: name.clone(), - args, - }); - } - - Err(ParseError::UnexpectedChar( - trimmed.chars().next().unwrap_or('\0'), - )) -} - -/// Parse a sequence of already-tokenized tokens as a command (for assignment RHS). -fn parse_tokens_as_command(tokens: &[Token]) -> Result { - if tokens.is_empty() { - return Ok(ParsedCommand::Empty); - } - - // Check for variable expression: `$var`, `$var.path`, or `$var + N` - if let Some(Token::VarRef { name, path }) = tokens.first() { - if tokens.len() == 1 { - return Ok(ParsedCommand::PrintVar { - name: name.clone(), - path: path.clone(), - offset: None, - }); - } - if tokens.len() == 3 - && matches!(tokens[1], Token::Plus | Token::Minus) - && let Token::Number(n) = &tokens[2] - { - let sign: i64 = if matches!(tokens[1], Token::Minus) { - -1 - } else { - 1 - }; - let value: i64 = n.parse().map_err(|_| { - ParseError::InvalidJson(format!("invalid number in arithmetic: {n}")) - })?; - return Ok(ParsedCommand::PrintVar { - name: name.clone(), - path: path.clone(), - offset: Some(sign * value), - }); - } - } - - // Check for namespace.method pattern - if tokens.len() >= 3 - && let (Token::Ident(ns), Token::Dot, Token::Ident(method)) = - (&tokens[0], &tokens[1], &tokens[2]) - { - let args = parse_rpc_args(&tokens[3..])?; - return Ok(ParsedCommand::RpcCall { - namespace: ns.clone(), - method: method.clone(), - args, - }); - } - - // Check for utility call - if let Token::Ident(name) = &tokens[0] { - let args = tokens[1..] - .iter() - .filter(|t| !matches!(t, Token::LParen | Token::RParen | Token::Comma)) - .map(token_to_string) - .collect(); - return Ok(ParsedCommand::UtilityCall { - name: name.clone(), - args, - }); - } - - Err(ParseError::UnexpectedEof) -} - -fn parse_rpc_args(tokens: &[Token]) -> Result, ParseError> { - if tokens.is_empty() { - return Ok(Vec::new()); - } - - // Collect raw args first (filtering parens/commas), then merge arithmetic. - let mut raw = Vec::new(); - - if tokens.first() == Some(&Token::LParen) { - let mut found_rparen = false; - for token in &tokens[1..] { - match token { - Token::RParen => { - found_rparen = true; - break; - } - Token::Comma => continue, - t => raw.push(t), - } - } - if !found_rparen { - return Err(ParseError::UnexpectedEof); - } - } else { - for token in tokens { - match token { - Token::Comma => continue, - t => raw.push(t), - } - } - } - - // Merge `VarRef +/- Number` sequences into a single VarRef with offset. - let mut args = Vec::new(); - let mut i = 0; - while i < raw.len() { - if let Token::VarRef { name, path } = raw[i] - && i + 2 < raw.len() - && matches!(raw[i + 1], Token::Plus | Token::Minus) - && let Token::Number(n) = raw[i + 2] - { - let sign: i64 = if matches!(raw[i + 1], Token::Minus) { - -1 - } else { - 1 - }; - let value: i64 = n.parse().map_err(|_| { - ParseError::InvalidJson(format!("invalid number in arithmetic: {n}")) - })?; - args.push(RpcArg::VarRef { - name: name.clone(), - path: path.clone(), - offset: Some(sign * value), - }); - i += 3; - } else { - args.push(token_to_rpc_arg(raw[i])); - i += 1; - } - } - - Ok(args) -} - -#[cfg(test)] -mod tests { - use super::*; - - /// Helper: extract the inner Value from an RpcArg::Literal, panicking if it's a VarRef. - fn lit(arg: &RpcArg) -> &Value { - match arg { - RpcArg::Literal(v) => v, - RpcArg::VarRef { name, path, .. } => { - panic!("expected Literal, got VarRef({name}, {path:?})") - } - } - } - - #[test] - fn test_empty_input() { - let result = parse("").unwrap(); - assert!(matches!(result, ParsedCommand::Empty)); - } - - #[test] - fn test_builtin_command() { - let result = parse(".help").unwrap(); - match result { - ParsedCommand::BuiltinCommand { name, args } => { - assert_eq!(name, "help"); - assert!(args.is_empty()); - } - _ => panic!("expected BuiltinCommand"), - } - } - - #[test] - fn test_rpc_call_no_args() { - let result = parse("eth.blockNumber").unwrap(); - match result { - ParsedCommand::RpcCall { - namespace, - method, - args, - } => { - assert_eq!(namespace, "eth"); - assert_eq!(method, "blockNumber"); - assert!(args.is_empty()); - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_rpc_call_with_parens() { - let result = - parse(r#"eth.getBalance("0x1234567890abcdef1234567890abcdef12345678", "latest")"#) - .unwrap(); - match result { - ParsedCommand::RpcCall { - namespace, - method, - args, - } => { - assert_eq!(namespace, "eth"); - assert_eq!(method, "getBalance"); - assert_eq!(args.len(), 2); - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_rpc_call_space_separated() { - let result = parse("eth.getBalance 0x1234 latest").unwrap(); - match result { - ParsedCommand::RpcCall { - namespace, - method, - args, - } => { - assert_eq!(namespace, "eth"); - assert_eq!(method, "getBalance"); - assert_eq!(args.len(), 2); - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_utility_call() { - let result = parse("toWei 1.5 ether").unwrap(); - match result { - ParsedCommand::UtilityCall { name, args } => { - assert_eq!(name, "toWei"); - assert_eq!(args, vec!["1.5", "ether"]); - } - _ => panic!("expected UtilityCall"), - } - } - - #[test] - fn test_json_object_arg() { - let result = parse(r#"eth.call({"to": "0xabc", "data": "0x1234"}, "latest")"#).unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 2); - assert!(lit(&args[0]).is_object()); - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_bool_arg() { - let result = parse("eth.getBlockByNumber 0x1 true").unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 2); - assert_eq!(lit(&args[1]), &Value::Bool(true)); - } - _ => panic!("expected RpcCall"), - } - } - - // --- Tokenizer edge cases --- - - #[test] - fn test_hex_address_as_bare_arg() { - let result = - parse("eth.getBalance 0x1234567890abcdef1234567890abcdef12345678 latest").unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 2); - assert_eq!( - lit(&args[0]), - &Value::String("0x1234567890abcdef1234567890abcdef12345678".to_string()) - ); - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_number_with_0x_prefix() { - let mut tokenizer = Tokenizer::new("0xff"); - let tokens = tokenizer.tokenize().unwrap(); - assert_eq!(tokens, vec![Token::Number("0xff".to_string())]); - } - - #[test] - fn test_escape_sequences_in_strings() { - let mut tokenizer = Tokenizer::new(r#""hello\nworld\t!\\\"""#); - let tokens = tokenizer.tokenize().unwrap(); - assert_eq!( - tokens, - vec![Token::String("hello\nworld\t!\\\"".to_string())] - ); - } - - #[test] - fn test_single_quoted_strings() { - let mut tokenizer = Tokenizer::new("'hello world'"); - let tokens = tokenizer.tokenize().unwrap(); - assert_eq!(tokens, vec![Token::String("hello world".to_string())]); - } - - #[test] - fn test_single_quoted_escape() { - let mut tokenizer = Tokenizer::new(r"'it\'s'"); - let tokens = tokenizer.tokenize().unwrap(); - assert_eq!(tokens, vec![Token::String("it's".to_string())]); - } - - // --- Multi-line JSON --- - - #[test] - fn test_nested_json_object() { - let input = r#"eth.call({"a": {"b": 1}}, "latest")"#; - let result = parse(input).unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 2); - assert!(lit(&args[0]).is_object()); - assert_eq!(lit(&args[0])["a"]["b"], Value::Number(1.into())); - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_json_array_with_objects() { - let input = r#"eth.call([{"x":1}], "latest")"#; - let result = parse(input).unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 2); - assert!(lit(&args[0]).is_array()); - } - _ => panic!("expected RpcCall"), - } - } - - // --- Error cases --- - - #[test] - fn test_unterminated_string() { - let err = parse(r#"eth.call("hello"#).unwrap_err(); - assert!(matches!(err, ParseError::UnterminatedString)); - } - - #[test] - fn test_unterminated_json() { - let err = parse(r#"eth.call({"a": 1)"#).unwrap_err(); - assert!(matches!(err, ParseError::UnterminatedJson)); - } - - #[test] - fn test_unexpected_char() { - let err = parse("@invalid").unwrap_err(); - assert!(matches!(err, ParseError::UnexpectedChar('@'))); - } - - #[test] - fn test_unexpected_char_hash() { - let err = parse("#comment").unwrap_err(); - assert!(matches!(err, ParseError::UnexpectedChar('#'))); - } - - // --- Builtin commands with args --- - - #[test] - fn test_builtin_help_with_arg() { - let result = parse(".help eth.getBalance").unwrap(); - match result { - ParsedCommand::BuiltinCommand { name, args } => { - assert_eq!(name, "help"); - assert_eq!(args, vec!["eth.getBalance"]); - } - _ => panic!("expected BuiltinCommand"), - } - } - - #[test] - fn test_builtin_connect_with_url() { - let result = parse(".connect http://localhost:8545").unwrap(); - match result { - ParsedCommand::BuiltinCommand { name, args } => { - assert_eq!(name, "connect"); - assert_eq!(args, vec!["http://localhost:8545"]); - } - _ => panic!("expected BuiltinCommand"), - } - } - - // --- RPC call variants --- - - #[test] - fn test_rpc_call_parenthesized_with_commas() { - let result = parse(r#"eth.call("arg1", "arg2")"#).unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 2); - assert_eq!(lit(&args[0]), &Value::String("arg1".to_string())); - assert_eq!(lit(&args[1]), &Value::String("arg2".to_string())); - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_rpc_call_mixed_types() { - let result = parse("eth.getBlockByNumber 0x1 true").unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 2); - assert_eq!(lit(&args[0]), &Value::String("0x1".to_string())); - assert_eq!(lit(&args[1]), &Value::Bool(true)); - } - _ => panic!("expected RpcCall"), - } - } - - // --- Utility calls --- - - #[test] - fn test_all_utility_names_recognized() { - let utility_names = [ - "toWei", - "fromWei", - "toHex", - "fromHex", - "keccak256", - "toChecksumAddress", - "isAddress", - ]; - for name in &utility_names { - let result = parse(name).unwrap(); - match result { - ParsedCommand::UtilityCall { - name: parsed_name, .. - } => { - assert_eq!(&parsed_name, name); - } - _ => panic!("expected UtilityCall for {name}"), - } - } - } - - #[test] - fn test_unknown_ident_falls_through_to_utility_call() { - let result = parse("unknownFunc arg1").unwrap(); - match result { - ParsedCommand::UtilityCall { name, args } => { - assert_eq!(name, "unknownFunc"); - assert_eq!(args, vec!["arg1"]); - } - _ => panic!("expected UtilityCall"), - } - } - - // --- Empty / whitespace --- - - #[test] - fn test_whitespace_only() { - let result = parse(" ").unwrap(); - assert!(matches!(result, ParsedCommand::Empty)); - } - - #[test] - fn test_tabs_and_newlines_only() { - let result = parse("\t\n\r\n").unwrap(); - assert!(matches!(result, ParsedCommand::Empty)); - } - - #[test] - fn test_decimal_number_token() { - let mut tokenizer = Tokenizer::new("12345"); - let tokens = tokenizer.tokenize().unwrap(); - assert_eq!(tokens, vec![Token::Number("12345".to_string())]); - } - - #[test] - fn test_decimal_float_token() { - let mut tokenizer = Tokenizer::new("1.5"); - let tokens = tokenizer.tokenize().unwrap(); - assert_eq!(tokens, vec![Token::Number("1.5".to_string())]); - } - - #[test] - fn test_colon_token() { - let mut tokenizer = Tokenizer::new(":"); - let tokens = tokenizer.tokenize().unwrap(); - assert_eq!(tokens, vec![Token::Colon]); - } - - #[test] - fn test_false_bool_token() { - let mut tokenizer = Tokenizer::new("false"); - let tokens = tokenizer.tokenize().unwrap(); - assert_eq!(tokens, vec![Token::Bool(false)]); - } - - #[test] - fn test_rpc_call_with_json_array_arg() { - let result = parse(r#"eth.call(["0x1", "0x2"])"#).unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 1); - assert!(lit(&args[0]).is_array()); - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_utility_call_with_parens() { - let result = parse("toHex(255)").unwrap(); - match result { - ParsedCommand::UtilityCall { name, args } => { - assert_eq!(name, "toHex"); - assert_eq!(args, vec!["255"]); - } - _ => panic!("expected UtilityCall"), - } - } - - #[test] - fn test_token_to_value_conversions() { - assert_eq!( - token_to_value(&Token::String("hi".to_string())), - Value::String("hi".to_string()) - ); - assert_eq!( - token_to_value(&Token::Number("42".to_string())), - Value::String("42".to_string()) - ); - assert_eq!(token_to_value(&Token::Bool(true)), Value::Bool(true)); - assert_eq!( - token_to_value(&Token::Ident("foo".to_string())), - Value::String("foo".to_string()) - ); - assert_eq!(token_to_value(&Token::Dot), Value::Null); - assert_eq!(token_to_value(&Token::Comma), Value::Null); - } - - #[test] - fn test_token_to_string_conversions() { - assert_eq!(token_to_string(&Token::Dot), "."); - assert_eq!(token_to_string(&Token::LParen), "("); - assert_eq!(token_to_string(&Token::RParen), ")"); - assert_eq!(token_to_string(&Token::Comma), ","); - assert_eq!(token_to_string(&Token::Colon), ":"); - assert_eq!(token_to_string(&Token::Bool(true)), "true"); - assert_eq!(token_to_string(&Token::Bool(false)), "false"); - } - - // --- Variable assignment and reference --- - - #[test] - fn test_assignment_rpc_call() { - let result = parse("head = eth.getBlockByNumber latest false").unwrap(); - match result { - ParsedCommand::Assignment { var_name, command } => { - assert_eq!(var_name, "head"); - match *command { - ParsedCommand::RpcCall { - namespace, method, .. - } => { - assert_eq!(namespace, "eth"); - assert_eq!(method, "getBlockByNumber"); - } - _ => panic!("expected RpcCall inside assignment"), - } - } - _ => panic!("expected Assignment"), - } - } - - #[test] - fn test_assignment_utility_call() { - let result = parse("x = toHex 255").unwrap(); - match result { - ParsedCommand::Assignment { var_name, command } => { - assert_eq!(var_name, "x"); - match *command { - ParsedCommand::UtilityCall { name, args } => { - assert_eq!(name, "toHex"); - assert_eq!(args, vec!["255"]); - } - _ => panic!("expected UtilityCall inside assignment"), - } - } - _ => panic!("expected Assignment"), - } - } - - #[test] - fn test_var_ref_simple() { - let result = parse("eth.getBlockByNumber $blockNum false").unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 2); - match &args[0] { - RpcArg::VarRef { name, path, .. } => { - assert_eq!(name, "blockNum"); - assert!(path.is_empty()); - } - _ => panic!("expected VarRef"), - } - assert_eq!(lit(&args[1]), &Value::Bool(false)); - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_var_ref_nested() { - let result = parse("engine.newPayloadV4 $payload.executionPayload [] 0x00").unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 3); - match &args[0] { - RpcArg::VarRef { name, path, .. } => { - assert_eq!(name, "payload"); - assert_eq!(path, &["executionPayload"]); - } - _ => panic!("expected VarRef"), - } - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_var_ref_deeply_nested() { - let result = parse("eth.call $a.b.c.d").unwrap(); - match result { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 1); - match &args[0] { - RpcArg::VarRef { name, path, .. } => { - assert_eq!(name, "a"); - assert_eq!(path, &["b", "c", "d"]); - } - _ => panic!("expected VarRef"), - } - } - _ => panic!("expected RpcCall"), - } - } - - #[test] - fn test_dollar_without_ident_errors() { - let err = parse("eth.call $").unwrap_err(); - assert!(matches!(err, ParseError::UnexpectedChar('$'))); - } - - #[test] - fn test_equals_token() { - let mut tokenizer = Tokenizer::new("="); - let tokens = tokenizer.tokenize().unwrap(); - assert_eq!(tokens, vec![Token::Equals]); - } - - #[test] - fn test_var_ref_token() { - let mut tokenizer = Tokenizer::new("$foo.bar"); - let tokens = tokenizer.tokenize().unwrap(); - assert_eq!( - tokens, - vec![Token::VarRef { - name: "foo".to_string(), - path: vec!["bar".to_string()], - }] - ); - } - - #[test] - fn test_assignment_with_var_ref_arg() { - let result = parse("id = engine.getPayloadV5 $fcu.payloadId").unwrap(); - match result { - ParsedCommand::Assignment { var_name, command } => { - assert_eq!(var_name, "id"); - match *command { - ParsedCommand::RpcCall { args, .. } => { - assert_eq!(args.len(), 1); - match &args[0] { - RpcArg::VarRef { name, path, .. } => { - assert_eq!(name, "fcu"); - assert_eq!(path, &["payloadId"]); - } - _ => panic!("expected VarRef"), - } - } - _ => panic!("expected RpcCall"), - } - } - _ => panic!("expected Assignment"), - } - } -} diff --git a/tooling/repl/src/repl.rs b/tooling/repl/src/repl.rs deleted file mode 100644 index fedd7b7f120..00000000000 --- a/tooling/repl/src/repl.rs +++ /dev/null @@ -1,1060 +0,0 @@ -use std::sync::Arc; - -use rustyline::error::ReadlineError; -use rustyline::history::DefaultHistory; -use rustyline::{Config, Editor}; -use serde_json::Value; - -use crate::client::RpcClient; -use crate::commands::{CommandDef, CommandRegistry, ParamType}; -use crate::completer::ReplHelper; -use crate::ens; -use crate::formatter; -use crate::parser::{self, ParsedCommand, RpcArg}; -use crate::variables::VariableStore; - -pub struct Repl { - client: RpcClient, - authrpc_client: Option, - registry: Arc, - history_path: String, - variables: VariableStore, -} - -impl Repl { - pub fn new(client: RpcClient, authrpc_client: Option, history_path: String) -> Self { - Self { - client, - authrpc_client, - registry: Arc::new(CommandRegistry::new()), - history_path, - variables: VariableStore::new(), - } - } - - pub async fn run(&mut self) { - let config = Config::builder().auto_add_history(true).build(); - - let helper = ReplHelper::new(Arc::clone(&self.registry), self.variables.clone()); - let mut rl: Editor = - Editor::with_config(config).expect("Failed to create editor"); - rl.set_helper(Some(helper)); - - if let Err(e) = rl.load_history(&self.history_path) - && !matches!(e, ReadlineError::Io(_)) - { - eprintln!("Warning: could not load history: {e}"); - } - - println!("Welcome to the ethrex REPL!"); - println!("Connected to {}", self.client.endpoint()); - println!("Type .help for available commands, .exit to quit.\n"); - - let mut multiline_buffer = String::new(); - - loop { - let prompt = if multiline_buffer.is_empty() { - "> " - } else { - "... " - }; - - match rl.readline(prompt) { - Ok(line) => { - let line = line.trim_end(); - - // Multi-line support: accumulate if braces/brackets are unbalanced - let should_exit = if !multiline_buffer.is_empty() { - multiline_buffer.push(' '); - multiline_buffer.push_str(line); - if !is_balanced(&multiline_buffer) { - continue; - } - let full_input = std::mem::take(&mut multiline_buffer); - self.execute_input(&full_input).await - } else if !is_balanced(line) { - multiline_buffer = line.to_string(); - continue; - } else { - self.execute_input(line).await - }; - if should_exit { - break; - } - } - Err(ReadlineError::Interrupted) => { - multiline_buffer.clear(); - println!("(Use .exit to quit)"); - } - Err(ReadlineError::Eof) => { - println!("Bye!"); - break; - } - Err(err) => { - eprintln!("{}", formatter::format_error(&err.to_string())); - break; - } - } - } - - ensure_parent_dir(&self.history_path); - if let Err(e) = rl.save_history(&self.history_path) { - eprintln!("Warning: could not save history: {e}"); - } - } - - /// Execute a single command and return the result as a string (for -x mode). - /// Does not store variables (one-shot execution). - pub async fn execute_command(&self, input: &str) -> String { - match parser::parse(input) { - Ok(cmd) => match cmd { - ParsedCommand::RpcCall { - namespace, - method, - args, - } => match self.resolve_var_args(&args) { - Ok(resolved) => self.execute_rpc(&namespace, &method, &resolved).await, - Err(e) => formatter::format_error(&e), - }, - ParsedCommand::Assignment { .. } | ParsedCommand::PrintVar { .. } => { - formatter::format_error("variables not available in one-shot mode (-x)") - } - ParsedCommand::UtilityCall { name, args } => execute_utility(&name, &args), - ParsedCommand::BuiltinCommand { name, .. } => { - format!("Built-in command .{name} not available in non-interactive mode") - } - ParsedCommand::Empty => String::new(), - }, - Err(e) => formatter::format_error(&e.to_string()), - } - } - - /// Execute a single input line. Returns `true` if the REPL should exit. - async fn execute_input(&mut self, input: &str) -> bool { - let parsed = match parser::parse(input) { - Ok(p) => p, - Err(e) => { - println!("{}", formatter::format_error(&e.to_string())); - return false; - } - }; - - match parsed { - ParsedCommand::Empty => {} - ParsedCommand::RpcCall { - namespace, - method, - args, - } => { - let resolved = match self.resolve_var_args(&args) { - Ok(r) => r, - Err(e) => { - println!("{}", formatter::format_error(&e)); - return false; - } - }; - let result = self.execute_rpc(&namespace, &method, &resolved).await; - println!("{result}"); - } - ParsedCommand::Assignment { var_name, command } => { - let result = self.execute_assignment(&var_name, &command).await; - println!("{result}"); - } - ParsedCommand::PrintVar { name, path, offset } => { - match self.resolve_var_ref(&name, &path) { - Ok(value) => { - let result = match offset { - Some(off) => apply_offset(&value, off), - None => Ok(value), - }; - match result { - Ok(v) => println!("{}", formatter::format_value(&v)), - Err(e) => println!("{}", formatter::format_error(&e)), - } - } - Err(e) => println!("{}", formatter::format_error(&e)), - } - } - ParsedCommand::BuiltinCommand { name, args } => { - if self.execute_builtin(&name, &args) { - return true; - } - } - ParsedCommand::UtilityCall { name, args } => { - let result = execute_utility(&name, &args); - println!("{result}"); - } - } - false - } - - /// Resolve variable references in RPC arguments to concrete JSON values. - /// Handles both top-level `$var.path` tokens and `$var.path` strings - /// embedded inside JSON objects/arrays. - fn resolve_var_args(&self, args: &[RpcArg]) -> Result, String> { - args.iter() - .map(|arg| match arg { - RpcArg::Literal(v) => self.resolve_vars_in_value(v), - RpcArg::VarRef { name, path, offset } => { - let value = self.resolve_var_ref(name, path)?; - match offset { - Some(off) => apply_offset(&value, *off), - None => Ok(value), - } - } - }) - .collect() - } - - /// Resolve a single `$name.path` variable reference. - fn resolve_var_ref(&self, name: &str, path: &[String]) -> Result { - let value = self - .variables - .get(name) - .ok_or_else(|| format!("undefined variable: {name}"))?; - let mut current = value; - for field in path { - current = current - .get(field) - .ok_or_else(|| format!("field '{field}' not found in variable '{name}'"))? - .clone(); - } - Ok(current) - } - - /// Walk a JSON value tree and resolve any string values that look like - /// `$var` or `$var.field.nested`. - fn resolve_vars_in_value(&self, value: &Value) -> Result { - match value { - Value::String(s) if s.starts_with('$') => { - let ref_str = &s[1..]; // strip '$' - let parts: Vec<&str> = ref_str.split('.').collect(); - let name = parts[0]; - let path: Vec = parts[1..].iter().map(|s| s.to_string()).collect(); - self.resolve_var_ref(name, &path) - } - Value::Object(map) => { - let mut resolved = serde_json::Map::new(); - for (k, v) in map { - resolved.insert(k.clone(), self.resolve_vars_in_value(v)?); - } - Ok(Value::Object(resolved)) - } - Value::Array(arr) => { - let resolved: Result, String> = - arr.iter().map(|v| self.resolve_vars_in_value(v)).collect(); - Ok(Value::Array(resolved?)) - } - other => Ok(other.clone()), - } - } - - /// Execute an assignment: run the inner command and store the result. - async fn execute_assignment(&mut self, var_name: &str, command: &ParsedCommand) -> String { - match command { - ParsedCommand::RpcCall { - namespace, - method, - args, - } => { - let resolved = match self.resolve_var_args(args) { - Ok(r) => r, - Err(e) => return formatter::format_error(&e), - }; - match self.call_rpc_raw(namespace, method, &resolved).await { - Ok((result, _)) => { - let formatted = formatter::format_value(&result); - self.variables.insert(var_name.to_string(), result); - formatted - } - Err(e) => formatter::format_error(&e), - } - } - ParsedCommand::UtilityCall { name, args } => { - let result_str = execute_utility(name, args); - // Try to parse as JSON value for storage; fall back to string - let value: Value = serde_json::from_str(&result_str) - .unwrap_or_else(|_| Value::String(result_str.clone())); - self.variables.insert(var_name.to_string(), value); - result_str - } - ParsedCommand::PrintVar { name, path, offset } => { - match self.resolve_var_ref(name, path) { - Ok(value) => { - let result = match offset { - Some(off) => apply_offset(&value, *off), - None => Ok(value), - }; - match result { - Ok(v) => { - let formatted = formatter::format_value(&v); - self.variables.insert(var_name.to_string(), v); - formatted - } - Err(e) => formatter::format_error(&e), - } - } - Err(e) => formatter::format_error(&e), - } - } - _ => formatter::format_error( - "can only assign from RPC calls, utility functions, or variable expressions", - ), - } - } - - /// Select the appropriate RPC client based on namespace. - fn client_for_namespace(&self, namespace: &str) -> Result<&RpcClient, String> { - if namespace == "engine" { - self.authrpc_client - .as_ref() - .ok_or_else(|| "engine namespace requires --authrpc.jwtsecret".to_string()) - } else { - Ok(&self.client) - } - } - - /// Send an RPC request and return the raw result together with the resolved - /// `CommandDef`. Both `execute_rpc` and `execute_assignment` use this so - /// that the shared plumbing (client selection, registry lookup, ENS - /// resolution, param building, network call) lives in one place. - async fn call_rpc_raw<'a>( - &'a self, - namespace: &str, - method: &str, - args: &[Value], - ) -> Result<(Value, &'a CommandDef), String> { - let client = self.client_for_namespace(namespace)?; - - let cmd = self - .registry - .find(namespace, method) - .ok_or_else(|| format!("unknown command: {namespace}.{method}"))?; - - let resolved_args = self.resolve_ens_in_args(cmd, args).await?; - - let params = cmd - .build_params(&resolved_args) - .map_err(|e| format!("{e}\nUsage: {}", formatter::command_usage(cmd)))?; - - let result = client - .send_request(cmd.rpc_method, params) - .await - .map_err(|e| e.to_string())?; - - Ok((result, cmd)) - } - - async fn execute_rpc(&self, namespace: &str, method: &str, args: &[Value]) -> String { - match self.call_rpc_raw(namespace, method, args).await { - Ok((result, _cmd)) => formatter::format_value(&result), - Err(e) => formatter::format_error(&e), - } - } - - /// Resolve ENS names in arguments that expect an address. - async fn resolve_ens_in_args( - &self, - cmd: &CommandDef, - args: &[serde_json::Value], - ) -> Result, String> { - let mut resolved = args.to_vec(); - - for (i, param_def) in cmd.params.iter().enumerate() { - if param_def.param_type != ParamType::Address { - continue; - } - let Some(value) = resolved.get(i) else { - continue; - }; - let Some(s) = value.as_str() else { - continue; - }; - if !ens::looks_like_ens_name(s) { - continue; - } - - let name = s.to_string(); - let address = ens::resolve(&self.client, &name).await?; - println!("Resolved {name} -> {address}"); - resolved[i] = serde_json::Value::String(address); - } - - Ok(resolved) - } - - /// Execute a built-in command. Returns `true` if the REPL should exit. - fn execute_builtin(&self, name: &str, args: &[String]) -> bool { - match name { - "help" => self.show_help(args), - "exit" | "quit" => { - println!("Bye!"); - return true; - } - "clear" => { - print!("\x1b[2J\x1b[H"); - } - "connect" => { - if let Some(url) = args.first() { - println!("Reconnecting to {url}..."); - println!( - "Note: .connect in interactive mode requires restart. Use --endpoint flag." - ); - } else { - println!("Current endpoint: {}", self.client.endpoint()); - println!("Usage: .connect "); - } - } - "vars" => { - let entries = self.variables.entries(); - if entries.is_empty() { - println!("No variables stored"); - } else { - for (name, value) in &entries { - let s = value.to_string(); - let preview = if s.len() > 80 { - format!("{}...", &s[..77]) - } else { - s - }; - println!(" {name} = {preview}"); - } - } - } - "history" => { - println!("History file: {}", self.history_path); - } - _ => { - println!( - "{}", - formatter::format_error(&format!("unknown command: .{name}")) - ); - } - } - false - } - - fn show_help(&self, args: &[String]) { - if args.is_empty() { - println!("Available namespaces:"); - for ns in self.registry.namespaces() { - let count = self.registry.methods_in_namespace(ns).len(); - println!(" {ns:<10} ({count} methods)"); - } - println!("\nUtility functions:"); - println!(" toWei, fromWei, toHex, fromHex, keccak256, toChecksumAddress, isAddress"); - println!("\nBuilt-in commands:"); - println!(" .help [namespace|command] Show help"); - println!(" .exit / .quit Exit REPL"); - println!(" .clear Clear screen"); - println!(" .connect Show/change endpoint"); - println!(" .vars List stored variables"); - println!(" .history Show history file path"); - println!("\nType .help to list namespace methods."); - println!("Type .help for method details."); - } else { - let arg = &args[0]; - if let Some(dot_pos) = arg.find('.') { - let namespace = &arg[..dot_pos]; - let method = &arg[dot_pos + 1..]; - if let Some(cmd) = self.registry.find(namespace, method) { - println!("{}", formatter::command_usage(cmd)); - println!(" {}", cmd.description); - if !cmd.params.is_empty() { - println!("\nParameters:"); - for p in cmd.params { - let req = if p.required { "required" } else { "optional" }; - let def = p - .default_value - .map(|d| format!(", default: {d}")) - .unwrap_or_default(); - println!( - " {:<20} {:?} ({}{}) - {}", - p.name, p.param_type, req, def, p.description - ); - } - } - } else { - println!( - "{}", - formatter::format_error(&format!("unknown command: {arg}")) - ); - } - } else { - let methods = self.registry.methods_in_namespace(arg); - if methods.is_empty() { - println!( - "{}", - formatter::format_error(&format!("unknown namespace: {arg}")) - ); - } else { - println!("{arg} namespace ({} methods):", methods.len()); - for cmd in methods { - println!( - " {:<45} {}", - formatter::command_usage(cmd), - cmd.description - ); - } - } - } - } - } -} - -/// Apply an arithmetic offset to a JSON value. -/// -/// Handles: -/// - Hex strings (`"0x1a"` + 1 → `"0x1b"`) -/// - Decimal strings (`"100"` + 1 → `"101"`) -/// - JSON numbers (`100` + 1 → `101`) -fn apply_offset(value: &Value, offset: i64) -> Result { - let n: i64 = match value { - Value::String(s) => { - if let Some(hex) = s.strip_prefix("0x") { - i64::from_str_radix(hex, 16) - .map_err(|_| format!("cannot apply arithmetic to non-numeric value: {s}"))? - } else { - s.parse() - .map_err(|_| format!("cannot apply arithmetic to non-numeric value: {s}"))? - } - } - Value::Number(n) => n - .as_i64() - .ok_or_else(|| "cannot apply arithmetic to non-integer number".to_string())?, - other => { - return Err(format!( - "cannot apply arithmetic to {}", - match other { - Value::Object(_) => "object", - Value::Array(_) => "array", - Value::Bool(_) => "boolean", - Value::Null => "null", - _ => "value", - } - )); - } - }; - - let result = n - .checked_add(offset) - .ok_or_else(|| "arithmetic overflow".to_string())?; - - // Preserve the original format: hex in → hex out, decimal in → decimal out - match value { - Value::String(s) if s.starts_with("0x") => Ok(Value::String(format!("0x{result:x}"))), - Value::Number(_) => Ok(Value::Number(result.into())), - _ => Ok(Value::String(result.to_string())), - } -} - -fn execute_utility(name: &str, args: &[String]) -> String { - match name { - "toWei" => { - if args.len() < 2 { - return formatter::format_error( - "Usage: toWei \nUnits: wei, gwei, ether", - ); - } - let decimals = match unit_decimals(&args[1]) { - Ok(d) => d, - Err(e) => return formatter::format_error(&e), - }; - match parse_wei_amount(&args[0], decimals) { - Ok(wei) => format!("{wei}"), - Err(e) => formatter::format_error(&e), - } - } - "fromWei" => { - if args.len() < 2 { - return formatter::format_error( - "Usage: fromWei \nUnits: wei, gwei, ether", - ); - } - let wei: u128 = match args[0].parse() { - Ok(v) => v, - Err(_) => return formatter::format_error(&format!("invalid number: {}", args[0])), - }; - let decimals = match unit_decimals(&args[1]) { - Ok(d) => d, - Err(e) => return formatter::format_error(&e), - }; - format_from_wei(wei, decimals) - } - "toHex" => { - if args.is_empty() { - return formatter::format_error("Usage: toHex "); - } - match args[0].parse::() { - Ok(n) => format!("0x{n:x}"), - Err(_) => formatter::format_error(&format!("invalid number: {}", args[0])), - } - } - "fromHex" => { - if args.is_empty() { - return formatter::format_error("Usage: fromHex "); - } - let hex = args[0].strip_prefix("0x").unwrap_or(&args[0]); - match u128::from_str_radix(hex, 16) { - Ok(n) => format!("{n}"), - Err(_) => formatter::format_error(&format!("invalid hex: {}", args[0])), - } - } - "keccak256" => { - if args.is_empty() { - return formatter::format_error("Usage: keccak256 "); - } - let input = args[0].strip_prefix("0x").unwrap_or(&args[0]); - let data = match hex::decode(input) { - Ok(d) => d, - Err(_) => { - return formatter::format_error(&format!("invalid hex data: {}", args[0])); - } - }; - use sha3::{Digest, Keccak256}; - let hash = Keccak256::digest(&data); - format!("0x{}", hex::encode(hash)) - } - "toChecksumAddress" => { - if args.is_empty() { - return formatter::format_error("Usage: toChecksumAddress
"); - } - let raw = args[0].strip_prefix("0x").unwrap_or(&args[0]); - if raw.len() != 40 { - return formatter::format_error("invalid address length"); - } - ens::to_checksum_address(&args[0]) - } - "isAddress" => { - if args.is_empty() { - return formatter::format_error("Usage: isAddress
"); - } - let addr = &args[0]; - let valid = addr.starts_with("0x") - && addr.len() == 42 - && addr[2..].chars().all(|c| c.is_ascii_hexdigit()); - format!("{valid}") - } - _ => formatter::format_error(&format!("unknown utility: {name}")), - } -} - -fn unit_decimals(unit: &str) -> Result { - match unit.to_lowercase().as_str() { - "wei" => Ok(0), - "gwei" => Ok(9), - "ether" | "eth" => Ok(18), - other => Err(format!("unknown unit: {other}. Use: wei, gwei, ether")), - } -} - -/// Parse an amount string (possibly with decimals) and a unit's decimal count into wei. -/// Uses pure integer arithmetic to avoid f64 precision loss. -fn parse_wei_amount(amount: &str, decimals: u32) -> Result { - let parts: Vec<&str> = amount.split('.').collect(); - if parts.len() > 2 { - return Err(format!("invalid number: {amount}")); - } - - let whole: u128 = if parts[0].is_empty() { - 0 - } else { - parts[0] - .parse() - .map_err(|_| format!("invalid number: {amount}"))? - }; - - let multiplier = 10u128.pow(decimals); - let whole_wei = whole - .checked_mul(multiplier) - .ok_or_else(|| format!("value too large: {amount}"))?; - - if parts.len() == 1 || parts[1].is_empty() { - return Ok(whole_wei); - } - - let frac_str = parts[1]; - if frac_str.len() > decimals as usize { - return Err(format!("too many decimal places for unit (max {decimals})")); - } - - let padded = format!("{frac_str:0 String { - if decimals == 0 { - return wei.to_string(); - } - let divisor = 10u128.pow(decimals); - let whole = wei / divisor; - let frac = wei % divisor; - if frac == 0 { - return whole.to_string(); - } - let frac_str = format!("{frac:0>width$}", width = decimals as usize); - let trimmed = frac_str.trim_end_matches('0'); - format!("{whole}.{trimmed}") -} - -fn is_balanced(s: &str) -> bool { - let mut brace_depth: i32 = 0; - let mut bracket_depth: i32 = 0; - let mut in_string = false; - let mut escape = false; - let mut string_char = '"'; - - for c in s.chars() { - if escape { - escape = false; - continue; - } - if c == '\\' && in_string { - escape = true; - continue; - } - if in_string { - if c == string_char { - in_string = false; - } - continue; - } - match c { - '"' | '\'' => { - in_string = true; - string_char = c; - } - '{' => brace_depth += 1, - '}' => { - brace_depth -= 1; - if brace_depth < 0 { - return false; - } - } - '[' => bracket_depth += 1, - ']' => { - bracket_depth -= 1; - if bracket_depth < 0 { - return false; - } - } - _ => {} - } - } - - brace_depth == 0 && bracket_depth == 0 -} - -fn ensure_parent_dir(path: &str) { - if let Some(parent) = std::path::Path::new(path).parent() { - let _ = std::fs::create_dir_all(parent); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - // --- execute_utility: toWei --- - - #[test] - fn to_wei_ether() { - let result = execute_utility("toWei", &["1".into(), "ether".into()]); - assert_eq!(result, "1000000000000000000"); - } - - #[test] - fn to_wei_gwei() { - let result = execute_utility("toWei", &["1".into(), "gwei".into()]); - assert_eq!(result, "1000000000"); - } - - #[test] - fn to_wei_wei() { - let result = execute_utility("toWei", &["1".into(), "wei".into()]); - assert_eq!(result, "1"); - } - - #[test] - fn to_wei_eth_alias() { - let result = execute_utility("toWei", &["1".into(), "eth".into()]); - assert_eq!(result, "1000000000000000000"); - } - - #[test] - fn to_wei_fractional_ether() { - let result = execute_utility("toWei", &["1.1".into(), "ether".into()]); - assert_eq!(result, "1100000000000000000"); - } - - #[test] - fn to_wei_fractional_gwei() { - let result = execute_utility("toWei", &["0.5".into(), "gwei".into()]); - assert_eq!(result, "500000000"); - } - - #[test] - fn from_wei_fractional_ether() { - let result = execute_utility("fromWei", &["1100000000000000000".into(), "ether".into()]); - assert_eq!(result, "1.1"); - } - - #[test] - fn to_wei_unknown_unit() { - let result = execute_utility("toWei", &["1".into(), "finney".into()]); - assert!( - result.contains("Error"), - "expected error for unknown unit, got: {result}" - ); - } - - #[test] - fn to_wei_missing_args() { - let result = execute_utility("toWei", &["1".into()]); - assert!(result.contains("Error"), "expected error for missing args"); - } - - #[test] - fn to_wei_invalid_number() { - let result = execute_utility("toWei", &["abc".into(), "ether".into()]); - assert!( - result.contains("Error"), - "expected error for invalid number" - ); - } - - // --- execute_utility: fromWei --- - - #[test] - fn from_wei_ether() { - let result = execute_utility("fromWei", &["1000000000000000000".into(), "ether".into()]); - assert_eq!(result, "1"); - } - - #[test] - fn from_wei_gwei() { - let result = execute_utility("fromWei", &["1000000000".into(), "gwei".into()]); - assert_eq!(result, "1"); - } - - #[test] - fn from_wei_missing_args() { - let result = execute_utility("fromWei", &["1000".into()]); - assert!(result.contains("Error")); - } - - #[test] - fn from_wei_invalid_number() { - let result = execute_utility("fromWei", &["notanumber".into(), "ether".into()]); - assert!(result.contains("Error")); - } - - // --- execute_utility: toHex --- - - #[test] - fn to_hex_255() { - let result = execute_utility("toHex", &["255".into()]); - assert_eq!(result, "0xff"); - } - - #[test] - fn to_hex_zero() { - let result = execute_utility("toHex", &["0".into()]); - assert_eq!(result, "0x0"); - } - - #[test] - fn to_hex_invalid() { - let result = execute_utility("toHex", &["xyz".into()]); - assert!(result.contains("Error")); - } - - #[test] - fn to_hex_missing_arg() { - let result = execute_utility("toHex", &[]); - assert!(result.contains("Error")); - } - - // --- execute_utility: fromHex --- - - #[test] - fn from_hex_0xff() { - let result = execute_utility("fromHex", &["0xff".into()]); - assert_eq!(result, "255"); - } - - #[test] - fn from_hex_without_0x() { - let result = execute_utility("fromHex", &["ff".into()]); - assert_eq!(result, "255"); - } - - #[test] - fn from_hex_invalid() { - let result = execute_utility("fromHex", &["zzz".into()]); - assert!(result.contains("Error")); - } - - #[test] - fn from_hex_missing_arg() { - let result = execute_utility("fromHex", &[]); - assert!(result.contains("Error")); - } - - // --- execute_utility: keccak256 --- - - #[test] - fn keccak256_empty_input() { - // keccak256 of empty data (0 bytes) - let result = execute_utility("keccak256", &["0x".into()]); - assert_eq!( - result, - "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" - ); - } - - #[test] - fn keccak256_invalid_hex() { - let result = execute_utility("keccak256", &["0xZZZZ".into()]); - assert!(result.contains("Error")); - } - - #[test] - fn keccak256_missing_arg() { - let result = execute_utility("keccak256", &[]); - assert!(result.contains("Error")); - } - - // --- execute_utility: toChecksumAddress --- - - #[test] - fn to_checksum_address_well_known() { - let result = execute_utility( - "toChecksumAddress", - &["0xd8da6bf26964af9d7eed9e03e53415d37aa96045".into()], - ); - assert_eq!(result, "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"); - } - - #[test] - fn to_checksum_address_invalid_length() { - let result = execute_utility("toChecksumAddress", &["0xabcdef".into()]); - assert!( - result.contains("Error"), - "expected error for invalid length" - ); - } - - #[test] - fn to_checksum_address_missing_arg() { - let result = execute_utility("toChecksumAddress", &[]); - assert!(result.contains("Error")); - } - - // --- execute_utility: isAddress --- - - #[test] - fn is_address_valid() { - let result = execute_utility( - "isAddress", - &["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".into()], - ); - assert_eq!(result, "true"); - } - - #[test] - fn is_address_wrong_length() { - let result = execute_utility("isAddress", &["0xabcdef".into()]); - assert_eq!(result, "false"); - } - - #[test] - fn is_address_no_0x_prefix() { - let result = execute_utility( - "isAddress", - &["d8da6bf26964af9d7eed9e03e53415d37aa96045".into()], - ); - assert_eq!(result, "false"); - } - - #[test] - fn is_address_missing_arg() { - let result = execute_utility("isAddress", &[]); - assert!(result.contains("Error")); - } - - // --- execute_utility: unknown --- - - #[test] - fn unknown_utility() { - let result = execute_utility("nonexistent", &[]); - assert!(result.contains("Error")); - } - - // --- is_balanced --- - - #[test] - fn balanced_braces() { - assert!(is_balanced("{}")); - } - - #[test] - fn balanced_brackets() { - assert!(is_balanced("[]")); - } - - #[test] - fn balanced_nested() { - assert!(is_balanced("{ [ ] }")); - } - - #[test] - fn balanced_empty_string() { - assert!(is_balanced("")); - } - - #[test] - fn balanced_json_object() { - assert!(is_balanced(r#"{"a": "b"}"#)); - } - - #[test] - fn unbalanced_open_brace() { - assert!(!is_balanced("{")); - } - - #[test] - fn unbalanced_open_bracket_brace() { - assert!(!is_balanced("[{")); - } - - #[test] - fn unbalanced_close_brace() { - assert!(!is_balanced("}")); - } - - #[test] - fn balanced_brace_inside_string() { - // The "}" inside the string value should not break balance - assert!(is_balanced(r#"{"a": "}"}"#)); - } - - #[test] - fn balanced_escaped_quote_in_string() { - // Escaped quote inside string should not end the string early - assert!(is_balanced(r#"{"a": "he said \"hi\""}"#)); - } - - #[test] - fn balanced_no_delimiters() { - assert!(is_balanced("eth.getBalance 0xabc")); - } -} diff --git a/tooling/repl/src/variables.rs b/tooling/repl/src/variables.rs deleted file mode 100644 index 397bbc290fc..00000000000 --- a/tooling/repl/src/variables.rs +++ /dev/null @@ -1,81 +0,0 @@ -use std::collections::HashMap; -use std::sync::{Arc, Mutex}; - -use serde_json::Value; - -/// Shared variable store for the REPL. -/// -/// Thread-safe wrapper around a `HashMap` that can be shared -/// between the REPL executor (which writes) and the completer (which reads -/// for tab-completion of `$var.field` paths). -#[derive(Clone, Default)] -pub struct VariableStore { - inner: Arc>>, -} - -impl VariableStore { - pub fn new() -> Self { - Self::default() - } - - pub fn get(&self, name: &str) -> Option { - self.inner.lock().ok()?.get(name).cloned() - } - - pub fn insert(&self, name: String, value: Value) { - if let Ok(mut map) = self.inner.lock() { - map.insert(name, value); - } - } - - pub fn is_empty(&self) -> bool { - self.inner.lock().map_or(true, |m| m.is_empty()) - } - - /// Iterate over all variables. Returns a snapshot. - pub fn entries(&self) -> Vec<(String, Value)> { - self.inner - .lock() - .map(|m| m.iter().map(|(k, v)| (k.clone(), v.clone())).collect()) - .unwrap_or_default() - } - - /// List all variable names. Used by the completer for `$` prefix completion. - pub fn names(&self) -> Vec { - self.inner - .lock() - .map(|m| m.keys().cloned().collect()) - .unwrap_or_default() - } - - /// Given a variable name, list the top-level field names if the value is a JSON object. - /// Used by the completer for `$name.` field completion. - pub fn field_names(&self, name: &str) -> Vec { - self.inner - .lock() - .ok() - .and_then(|m| m.get(name).cloned()) - .and_then(|v| v.as_object().map(|obj| obj.keys().cloned().collect())) - .unwrap_or_default() - } - - /// Resolve a nested path like `["executionPayload", "blockHash"]` on a variable. - /// Returns the field names at the resolved level if it's an object, or empty if not. - /// Used by the completer for deep `$name.field.` completion. - pub fn nested_field_names(&self, name: &str, path: &[&str]) -> Vec { - let value = self.inner.lock().ok().and_then(|m| m.get(name).cloned()); - let Some(mut current) = value else { - return Vec::new(); - }; - for segment in path { - match current.get(*segment) { - Some(v) => current = v.clone(), - None => return Vec::new(), - } - } - current - .as_object() - .map(|obj| obj.keys().cloned().collect()) - .unwrap_or_default() - } -} diff --git a/tooling/repl/tests/e2e.rs b/tooling/repl/tests/e2e.rs deleted file mode 100644 index 64c93ccb30b..00000000000 --- a/tooling/repl/tests/e2e.rs +++ /dev/null @@ -1,330 +0,0 @@ -use ethrex_repl::client::RpcClient; -use ethrex_repl::repl::Repl; -use tokio::io::{AsyncReadExt, AsyncWriteExt}; - -/// A mock JSON-RPC server that returns canned responses. Aborts the server -/// task on drop, so tests don't need explicit cleanup. -struct MockServer { - endpoint: String, - handle: tokio::task::JoinHandle<()>, -} - -impl MockServer { - async fn start() -> Self { - let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); - let endpoint = format!("http://{}", listener.local_addr().unwrap()); - - let handle = tokio::spawn(async move { - loop { - let (mut stream, _) = match listener.accept().await { - Ok(s) => s, - Err(_) => break, - }; - tokio::spawn(async move { - let mut buf = vec![0u8; 8192]; - let n = stream.read(&mut buf).await.unwrap_or(0); - let request = String::from_utf8_lossy(&buf[..n]); - - let response = if let Some(body_start) = request.find("\r\n\r\n") { - make_response(&request[body_start + 4..]) - } else { - r#"{"jsonrpc":"2.0","error":{"code":-32600,"message":"bad request"},"id":1}"# - .to_string() - }; - - let http_response = format!( - "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\n\r\n{}", - response.len(), - response - ); - let _ = stream.write_all(http_response.as_bytes()).await; - }); - } - }); - - Self { endpoint, handle } - } - - fn repl(&self) -> Repl { - Repl::new( - RpcClient::new(self.endpoint.clone()), - None, - "/tmp/ethrex_repl_test_history".to_string(), - ) - } -} - -impl Drop for MockServer { - fn drop(&mut self) { - self.handle.abort(); - } -} - -fn make_response(body: &str) -> String { - let req: serde_json::Value = serde_json::from_str(body).unwrap_or_default(); - let id = req.get("id").cloned().unwrap_or(serde_json::Value::Null); - let method = req.get("method").and_then(|m| m.as_str()).unwrap_or(""); - - let result = match method { - "eth_blockNumber" => serde_json::json!("0x10d4f"), - "eth_chainId" => serde_json::json!("0x1"), - "eth_getBalance" => serde_json::json!("0xde0b6b3a7640000"), - "eth_getTransactionCount" => serde_json::json!("0x5"), - "eth_gasPrice" => serde_json::json!("0x3b9aca00"), - "web3_clientVersion" => serde_json::json!("ethrex/v0.1.0"), - "net_version" => serde_json::json!("1"), - "net_peerCount" => serde_json::json!("0x19"), - "eth_getBlockByNumber" => serde_json::json!(null), - _ => serde_json::json!(null), - }; - - serde_json::json!({ - "jsonrpc": "2.0", - "id": id, - "result": result, - }) - .to_string() -} - -// ── RPC commands ─────────────────────────────────────────────── - -#[tokio::test] -async fn test_eth_block_number() { - let server = MockServer::start().await; - let result = server.repl().execute_command("eth.blockNumber").await; - assert!(result.contains("68943"), "expected 68943, got: {result}"); -} - -#[tokio::test] -async fn test_eth_chain_id() { - let server = MockServer::start().await; - let result = server.repl().execute_command("eth.chainId").await; - assert!(result.contains("1"), "expected 1, got: {result}"); -} - -#[tokio::test] -async fn test_eth_get_balance() { - let server = MockServer::start().await; - let result = server - .repl() - .execute_command("eth.getBalance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") - .await; - assert!( - result.contains("1000000000000000000"), - "expected 1 ETH in wei, got: {result}" - ); -} - -#[tokio::test] -async fn test_eth_get_balance_with_block() { - let server = MockServer::start().await; - let result = server - .repl() - .execute_command("eth.getBalance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045 latest") - .await; - assert!( - result.contains("1000000000000000000"), - "expected 1 ETH in wei, got: {result}" - ); -} - -#[tokio::test] -async fn test_eth_gas_price() { - let server = MockServer::start().await; - let result = server.repl().execute_command("eth.gasPrice").await; - assert!( - result.contains("1000000000"), - "expected 1 gwei in wei, got: {result}" - ); -} - -#[tokio::test] -async fn test_web3_client_version() { - let server = MockServer::start().await; - let result = server.repl().execute_command("web3.clientVersion").await; - assert!( - result.contains("ethrex"), - "expected 'ethrex', got: {result}" - ); -} - -#[tokio::test] -async fn test_net_version() { - let server = MockServer::start().await; - let result = server.repl().execute_command("net.version").await; - assert!(result.contains("1"), "expected '1', got: {result}"); -} - -#[tokio::test] -async fn test_net_peer_count() { - let server = MockServer::start().await; - let result = server.repl().execute_command("net.peerCount").await; - assert!(result.contains("25"), "expected 25, got: {result}"); -} - -// ── Error handling ───────────────────────────────────────────── - -#[tokio::test] -async fn test_unknown_command() { - let server = MockServer::start().await; - let result = server.repl().execute_command("foo.bar").await; - assert!( - result.contains("unknown command"), - "expected 'unknown command', got: {result}" - ); -} - -#[tokio::test] -async fn test_missing_required_arg() { - let server = MockServer::start().await; - let result = server.repl().execute_command("eth.getBalance").await; - assert!( - result.contains("Error") && result.contains("requires at least"), - "expected error about missing args, got: {result}" - ); -} - -#[tokio::test] -async fn test_invalid_address() { - let server = MockServer::start().await; - let result = server - .repl() - .execute_command("eth.getBalance 0xinvalid") - .await; - assert!( - result.contains("Error"), - "expected error for invalid address, got: {result}" - ); -} - -// ── Syntax variants ──────────────────────────────────────────── - -#[tokio::test] -async fn test_parenthesized_syntax() { - let server = MockServer::start().await; - let result = server - .repl() - .execute_command( - r#"eth.getBalance("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest")"#, - ) - .await; - assert!( - result.contains("1000000000000000000"), - "expected 1 ETH in wei via parens, got: {result}" - ); -} - -#[tokio::test] -async fn test_block_number_decimal_conversion() { - let server = MockServer::start().await; - let result = server - .repl() - .execute_command("eth.getBlockByNumber 100 false") - .await; - assert!( - result.contains("null"), - "expected null response, got: {result}" - ); -} - -// ── Utility commands ─────────────────────────────────────────── - -#[tokio::test] -async fn test_utility_to_wei() { - let server = MockServer::start().await; - let result = server.repl().execute_command("toWei 1 ether").await; - assert!( - result.contains("1000000000000000000"), - "expected 1e18, got: {result}" - ); -} - -#[tokio::test] -async fn test_utility_from_wei() { - let server = MockServer::start().await; - let result = server - .repl() - .execute_command("fromWei 1000000000000000000 ether") - .await; - assert!(result.contains("1"), "expected 1, got: {result}"); -} - -#[tokio::test] -async fn test_utility_to_hex() { - let server = MockServer::start().await; - let result = server.repl().execute_command("toHex 255").await; - assert!(result.contains("0xff"), "expected 0xff, got: {result}"); -} - -#[tokio::test] -async fn test_utility_from_hex() { - let server = MockServer::start().await; - let result = server.repl().execute_command("fromHex 0xff").await; - assert!(result.contains("255"), "expected 255, got: {result}"); -} - -#[tokio::test] -async fn test_utility_is_address() { - let server = MockServer::start().await; - let result = server - .repl() - .execute_command("isAddress 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") - .await; - assert!(result.contains("true"), "expected true, got: {result}"); -} - -// ── Built-in commands in non-interactive mode ────────────────── - -#[tokio::test] -async fn test_builtin_in_non_interactive() { - let server = MockServer::start().await; - let result = server.repl().execute_command(".help").await; - assert!( - result.contains("not available"), - "expected 'not available', got: {result}" - ); -} - -// ── Edge cases ───────────────────────────────────────────────── - -#[tokio::test] -async fn test_empty_input() { - let server = MockServer::start().await; - let result = server.repl().execute_command("").await; - assert!(result.is_empty(), "expected empty string, got: {result}"); -} - -#[tokio::test] -async fn test_whitespace_input() { - let server = MockServer::start().await; - let result = server.repl().execute_command(" ").await; - assert!(result.is_empty(), "expected empty string, got: {result}"); -} - -// ── Sequential commands on same Repl ─────────────────────────── - -#[tokio::test] -async fn test_multiple_sequential_commands() { - let server = MockServer::start().await; - let repl = server.repl(); - - let r1 = repl.execute_command("eth.blockNumber").await; - assert!(r1.contains("68943"), "first command failed: {r1}"); - - let r2 = repl.execute_command("eth.chainId").await; - assert!(r2.contains("1"), "second command failed: {r2}"); - - let r3 = repl - .execute_command("eth.getBalance 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") - .await; - assert!( - r3.contains("1000000000000000000"), - "third command failed: {r3}" - ); - - let r4 = repl.execute_command("toWei 1 ether").await; - assert!( - r4.contains("1000000000000000000"), - "fourth command failed: {r4}" - ); -} diff --git a/tooling/sync/.env.example b/tooling/sync/.env.example deleted file mode 100644 index 9e0c7939da1..00000000000 --- a/tooling/sync/.env.example +++ /dev/null @@ -1,12 +0,0 @@ -# Multisync Auto-Update Configuration -# Copy this file to .env and fill in the values - -# Branch to track (defaults to current branch if not set) -# MULTISYNC_BRANCH=main - -# Build profile: 'release-with-debug-assertions' enables validation -# MULTISYNC_BUILD_PROFILE=release-with-debug-assertions - -# Slack webhooks for notifications (optional) -# SLACK_WEBHOOK_URL_SUCCESS=https://hooks.slack.com/services/... -# SLACK_WEBHOOK_URL_FAILED=https://hooks.slack.com/services/... diff --git a/tooling/sync/Makefile b/tooling/sync/Makefile deleted file mode 100644 index 0343373b30d..00000000000 --- a/tooling/sync/Makefile +++ /dev/null @@ -1,344 +0,0 @@ -.PHONY = help create_datadir gen_jwt sync start_geth_holesky flamegraph-main flamegraph \ -flamegraph-branch flamegraph-inner flamegraph-mainnet flamegraph-sepolia flamegraph-holesky \ -flamegraph-hoodi start-lighthouse start-ethrex backup-db start-mainnet-metrics-docker \ -start-sepolia-metrics-docker start-holesky-metrics-docker start-hoodi-metrics-docker \ -start-metrics-docker tail-syncing-logs tail-metrics-logs copy_flamegraph import-with-metrics \ -multisync-up multisync-down multisync-clean multisync-logs multisync-status \ -multisync-restart multisync-monitor multisync-run multisync-loop multisync-loop-auto - -ETHREX_DIR ?= "../.." - -# Frame pointers for profiling (default on for sync/profiling targets) -FRAME_POINTERS ?= 1 - -ifeq ($(FRAME_POINTERS),1) -PROFILING_CFG := --config .cargo/profiling.toml -endif - -EVM ?= levm -NODE_NAME ?= ethrex -ENGINE_PORT ?= 8551 -LIGHTHOUSE_PORT ?= 9099 -LIGHTHOUSE_DISCOVERY_PORT ?= 9999 -CURRENT_DATETIME = $(shell date +'%y.%m.%d-%H.%M.%S') -BATCH_SIZE ?= 1024 -OS = $(shell uname) -ifeq ($(OS), Darwin) - DATA_PATH = $(HOME)/Library -else - DATA_PATH = $(HOME)/.local/share -endif - -# Set checkpoint sync url based on network -NETWORK ?= mainnet# default network if not set -ifeq ($(NETWORK), mainnet) -CHECKPOINT_SYNC_URL ?= https://beaconstate.info -endif -ifeq ($(NETWORK), sepolia) -CHECKPOINT_SYNC_URL ?= https://checkpoint-sync.sepolia.ethpandaops.io -endif -ifeq ($(NETWORK), holesky) -CHECKPOINT_SYNC_URL ?= https://checkpoint-sync.holesky.ethpandaops.io -endif -ifeq ($(NETWORK), hoodi) -CHECKPOINT_SYNC_URL ?= https://hoodi.beaconstate.ethstaker.cc -endif - -# Set bootnodes flag only if we have bootnodes -ifndef BOOTNODES -BOOTNODES_FLAG ?= -else -BOOTNODES_FLAG ?= --bootnodes ${BOOTNODES} -endif - -default: help - -help: ## Display help for the makefile. - @grep -E '^[%a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' - -create_datadir: ## Create the data folder for the network. If NETWORK environment variable is not provided, mainnet will be used as default. - mkdir -p $(DATA_PATH)/$(NETWORK)_data - -gen-jwt: create_datadir ## Create the jwt for a given network. NETWORK environment variable required. - openssl rand -hex 32 | tr -d "\n" | tee $(DATA_PATH)/$(NETWORK)_data/jwt.hex - -sync: create_datadir ## Run the sync for a given network. SYNC_BLOCK_NUM environment variable required (for block to start from). If NETWORK environment variable is not provided, mainnet will be used as default. EVM can also be set to select the evm to use. -ifndef SYNC_BLOCK_NUM - $(error "Sync block number not set") -endif -# samply record --unstable-presymbolicate --save-only -- - mkdir -p logs - make start-ethrex >> ./logs/ethrex-sync-$(NETWORK)-$(EVM).log - - -flamegraph-main: ## Run flamegraph on main branch. SYNC_BLOCK_NUM environment variable required (for block to start from). If NETWORK environment variable is not provided, mainnet will be used as default. EVM can also be set to select the evm to use. Execution logs are output to log file. -ifndef SYNC_BLOCK_NUM - $(error "Sync block number not set) -endif - cd $(ETHREX_DIR) && git checkout main - mkdir -p logs - make flamegraph-inner >> logs/ethrex-$(NETWORK)-$(EVM)-flamegraph-$(CURRENT_DATETIME)-main-block-$(SYNC_BLOCK_NUM)-$(LOGNAME).log - -flamegraph: ## Run flamegraph on the currently checked out branch. SYNC_BLOCK_NUM environment variable required (for block to start from). If NETWORK environment variable is not provided, mainnet will be used as default. EVM can also be set to select the evm to use. Execution logs are output to log file. -ifndef SYNC_BLOCK_NUM - $(error "Sync block number not set) -endif - mkdir -p logs - make flamegraph-inner >> logs/ethrex-$(NETWORK)-$(EVM)-flamegraph-$(CURRENT_DATETIME)-main-block-$(SYNC_BLOCK_NUM)-$(LOGNAME).log - -flamegraph-branch: ## Run flamegraph on custom branch. SYNC_BLOCK_NUM environment variable required (for block to start from). If NETWORK environment variable is not provided, mainnet will be used as default. EVM can also be set to select the evm to use. Execution logs are output to log file. -ifndef SYNC_BLOCK_NUM - $(error "Sync block number not set") -endif -ifndef BRANCH - $(error "Branch not specified") -endif - cd $(ETHREX_DIR) && git checkout $(BRANCH) - mkdir -p logs - make flamegraph-inner >> logs/ethrex-$(NETWORK)-$(EVM)-flamegraph-$(CURRENT_DATETIME)-$(BRANCH)-block-$(SYNC_BLOCK_NUM)-$(LOGNAME).log - - -flamegraph-inner: # Inner target used for flamegraph-related targets. Runs flamegraph on the network given by NETWORK. - cd $(ETHREX_DIR) && CARGO_PROFILE_RELEASE_DEBUG=true RUST_LOG=3 cargo flamegraph --features "rocksdb sync-test" --bin ethrex -- \ - --http.port 8545 \ - --authrpc.port 8551 \ - --p2p.port 30303\ - --discovery.port 30303 \ - --network $(NETWORK) \ - --datadir $(DATA_PATH)/${NETWORK}_data/ethrex/$(EVM) \ - --authrpc.jwtsecret $(DATA_PATH)/${NETWORK}_data/jwt.hex \ - $(BOOTNODES_FLAG) \ - -flamegraph-%: ## Run flamegraph on either mainnet, sepolia, holesky or hoodi network. For example, to run the flamgraph on hoodi, use `flamegraph-hoodi` - $(MAKE) flamegraph-inner NETWORK=$* - -backup-db: ## Back-up the store db. EVM and NETWORK environment variables need to be provided to select which DB to back up. If NETWORK environment variable is not provided, mainnet will be used as default. - mkdir -p $(DATA_PATH)/ethrex_db_backups/$(NETWORK)/$(EVM)/db_backup_$(CURRENT_DATETIME) -ifeq ($(OS), Darwin) - rsync -ah --progress $(DATA_PATH)/$(NETWORK)_data/ethrex/$(EVM)/mdbx.* $(DATA_PATH)/ethrex_db_backups/$(NETWORK)/$(EVM)/db_backup_$(CURRENT_DATETIME) - rsync -ah --progress ./logs/ethrex-sync-$(NETWORK)-$(EVM).log $(DATA_PATH)/ethrex_db_backups/$(NETWORK)/$(EVM)/db_backup_$(CURRENT_DATETIME)/ethrex-sync-$(NETWORK)-$(EVM).log -else - rsync -ah --info=progress2 $(DATA_PATH)/$(NETWORK)_data/ethrex/$(EVM)/mdbx.* $(DATA_PATH)/ethrex_db_backups/$(NETWORK)/$(EVM)/db_backup_$(CURRENT_DATETIME) - rsync -ah --info=progress2 ./logs/ethrex-sync-$(NETWORK)-$(EVM).log $(DATA_PATH)/ethrex_db_backups/$(NETWORK)/$(EVM)/db_backup_$(CURRENT_DATETIME)/ethrex-sync-$(NETWORK)-$(EVM).log -endif - -import-with-metrics: ## Start L1 docker compose, lighthouse in background, and ethrex for holesky -ifndef RLP_FILE - $(error "RLP file with blocks not provided") -endif - @echo "Starting L1 docker compose with metrics..." - cd $(ETHREX_DIR)/metrics && docker compose -f docker-compose-metrics.yaml -f docker-compose-metrics-l1.overrides.yaml up -d - @echo "Starting ethrex..." - cd $(ETHREX_DIR) && cargo run $(PROFILING_CFG) --release --features "metrics" --bin ethrex -- \ - --network $(NETWORK) \ - --metrics \ - --metrics.port 3701 \ - import $(RLP_FILE) \ - --removedb - -tail-syncing-logs: ## Tail the syncing logs for a given log file. Environment variable LOGNAME with the name of the file needs to be provided. -ifndef LOGNAME - $(error "Log file not provided") -endif - tail -n 100 -f ./logs/$(LOGNAME) | grep -e "SYNCING" - -tail-metrics-logs: ## Tail the metrics logs for a given log file. Environment variable LOGNAME with the name of the file needs to be provided. -ifndef LOGNAME - $(error "Log file not provided") -endif - tail -n 2000 -f ./logs/$(LOGNAME) | grep -A4 -e "METRICS" - -copy-flamegraph: ## Copy flamegraph from ethrex folder to flamegraphs folder. A name for the file can be provided with GRAPHNAME variable. -ifeq ($(OS), Darwin) - rsync -ah --progress $(ETHREX_DIR)/flamegraph.svg flamegraphs/flamegraph-$(GRAPHNAME).svg -else - rsync -ah --info=progress2 $(ETHREX_DIR)/flamegraph.svg flamegraphs/flamegraph-$(GRAPHNAME).svg -endif - -start-%-metrics-docker: ## Start L1 docker compose, lighthouse in background, and ethrex for either mainnet, sepolia, holesky or hoodi network. For example, to run on hoodi, use `start-hoodi-metrics-docker` - $(MAKE) start-metrics-docker NETWORK=$* - -start-metrics-docker: ## Start L1 docker compose, lighthouse in background, and ethrex for the network given by NETWORK. - @echo "Starting L1 docker compose with metrics..." - cd $(ETHREX_DIR)/metrics && docker compose -f docker-compose-metrics.yaml -f docker-compose-metrics-l1.overrides.yaml up -d - @echo "Starting lighthouse in background..." - cd $(ETHREX_DIR)/tooling/sync && nohup make start-lighthouse > /dev/null 2>&1 & - @echo "Starting ethrex..." - cd $(ETHREX_DIR)/tooling/sync && make start-ethrex - -start-lighthouse: ## Start lighthouse for the network given by NETWORK. - lighthouse bn \ - --network $(NETWORK) \ - --execution-endpoint http://localhost:${ENGINE_PORT} \ - --execution-jwt $(DATA_PATH)/${NETWORK}_data/jwt.hex \ - --checkpoint-sync-url $(CHECKPOINT_SYNC_URL) \ - --http \ - --http-address 0.0.0.0 \ - --http-allow-origin "*" \ - --metrics \ - --metrics-address 0.0.0.0 \ - --metrics-port 5054 \ - --datadir $(DATA_PATH)/${NETWORK}_data/lighthouse_${NODE_NAME}_$(EVM) \ - --port $(LIGHTHOUSE_PORT) --discovery-port $(LIGHTHOUSE_DISCOVERY_PORT) - -start-ethrex: ## Start ethrex for the network given by NETWORK. - @echo $(RUSTFLAGS) - cd $(ETHREX_DIR) && RUST_LOG=3 cargo run $(PROFILING_CFG) --release --features "rocksdb sync-test metrics" --bin ethrex -- \ - --http.addr 0.0.0.0 \ - --http.port 8545 \ - --authrpc.port 8551 \ - --p2p.port 30303\ - --discovery.port 30303 \ - --metrics \ - --metrics.port 3701 \ - --network $(NETWORK) \ - --datadir $(DATA_PATH)/${NETWORK}_data/ethrex/$(EVM) \ - --authrpc.jwtsecret $(DATA_PATH)/${NETWORK}_data/jwt.hex \ - $(BOOTNODES_FLAG) \ - -SERVER_SYNC_BRANCH ?= main -SERVER_SYNC_NETWORK ?= hoodi - -ifeq ($(SERVER_SYNC_NETWORK),hoodi) -CHECKPOINT_URL = https://hoodi-checkpoint-sync.stakely.io/ -else ifeq ($(SERVER_SYNC_NETWORK),sepolia) -CHECKPOINT_URL = https://checkpoint-sync.sepolia.ethpandaops.io -else ifeq ($(SERVER_SYNC_NETWORK),mainnet) -CHECKPOINT_URL = https://mainnet-checkpoint-sync.attestant.io -else -$(error Unknown network $(SERVER_SYNC_NETWORK)) -endif - -LOGS_FILE ?= output.log - -# Use make server-sync SERVER_SYNC_BRANCH=branch_name SERVER_SYNC_NETWORK=network_name LOGS_FILE=logs_file_name HEALING=1 MEMORY=1 FULL_SYNC=1 -# SERVER_SYNC_BRANCH is the branch to checkout before syncing, SERVER_SYNC_NETWORK is the network to sync, LOGS_FILE is the file to output logs to, HEALING enables healing mode, MEMORY uses memory datadir, FULL_SYNC switches to full-sync mode. -# If you want to run metrics locally (grafana/prometheus/ethereum-metrics-exporter in docker-compose), make sure to run the following first: -# cd ../../metrics && docker compose -f docker-compose-metrics.yaml -f docker-compose-metrics-l1.overrides.yaml up -d -server-sync: - git fetch --all - - git checkout $(SERVER_SYNC_BRANCH) - - git pull - - tmux kill-session -t sync || true - - sleep 5 - - tmux new-session -d -s sync -n htop "htop" - - tmux new-window -t sync:1 -n lighthouse "lighthouse bn --network $(SERVER_SYNC_NETWORK) --execution-endpoint http://localhost:8551 --execution-jwt ~/secrets/jwt.hex --http --checkpoint-sync-url $(CHECKPOINT_URL) --http-address 0.0.0.0 --purge-db-force " - - sleep 0.2 - - tmux new-window -t sync:2 -n ethrex "cd ../.. && ulimit -n 1000000 && rm -rf ~/.local/share/ethrex && RUST_LOG=info,ethrex_p2p::sync=debug $(if $(DEBUG_ASSERT),RUSTFLAGS='-C debug-assertions=yes') $(if $(HEALING),SKIP_START_SNAP_SYNC=1) cargo run $(PROFILING_CFG) --release --bin ethrex --features rocksdb -- --http.addr 0.0.0.0 --metrics --metrics.port 3701 --network $(SERVER_SYNC_NETWORK) $(if $(MEMORY),--datadir memory) --authrpc.jwtsecret ~/secrets/jwt.hex $(if $(or $(FULL_SYNC),$(HEALING)),--syncmode full) 2>&1 | tee $(LOGS_FILE)" - -# ============================================================================== -# Docker Compose Multi-Network Snapsync -# ============================================================================== - -MULTISYNC_COMPOSE = docker compose -f docker-compose.multisync.yaml -MULTISYNC_NETWORKS ?= hoodi,sepolia,mainnet -comma := , -MULTISYNC_NETWORK_LIST := $(subst $(comma), ,$(MULTISYNC_NETWORKS)) -MULTISYNC_SERVICES := $(foreach n,$(MULTISYNC_NETWORK_LIST),setup-jwt-$(n) ethrex-$(n) consensus-$(n)) - -# Auto-update configuration (for multisync-loop-auto) -# Build profile: 'release-with-debug-assertions' enables state trie validation -MULTISYNC_BUILD_PROFILE ?= release-with-debug-assertions -# Local image tag for builds -MULTISYNC_LOCAL_IMAGE ?= ethrex-local:multisync -# Branch to track for auto-update mode (defaults to current branch if not set) -MULTISYNC_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) - -multisync-up: ## Start all networks specified in MULTISYNC_NETWORKS via Docker Compose. - $(MULTISYNC_COMPOSE) up -d $(MULTISYNC_SERVICES) - -multisync-down: ## Stop and remove all snapsync containers. - $(MULTISYNC_COMPOSE) down - -multisync-clean: ## Stop, remove containers AND volumes (full reset). - $(MULTISYNC_COMPOSE) down -v - -multisync-logs: ## Tail logs from all networks. - $(MULTISYNC_COMPOSE) logs -f - -multisync-logs-%: ## Tail logs for a specific network (e.g., multisync-logs-hoodi). - $(MULTISYNC_COMPOSE) logs -f ethrex-$* consensus-$* - -multisync-logs-ethrex-%: ## Tail only ethrex logs for a network (e.g., multisync-logs-ethrex-hoodi). - $(MULTISYNC_COMPOSE) logs -f ethrex-$* - -multisync-logs-consensus-%: ## Tail only consensus logs for a network (e.g., multisync-logs-consensus-hoodi). - $(MULTISYNC_COMPOSE) logs -f consensus-$* - -multisync-restart: ## Restart the cycle (clean volumes + start fresh). - $(MULTISYNC_COMPOSE) down -v - $(MULTISYNC_COMPOSE) up -d $(MULTISYNC_SERVICES) - -multisync-monitor: ## Monitor all networks (one-shot, exits on completion). - python3 docker_monitor.py --networks $(MULTISYNC_NETWORKS) --exit-on-success - -multisync-run: ## Full run: start + monitor (one-shot, exits on completion). - $(MULTISYNC_COMPOSE) up -d $(MULTISYNC_SERVICES) - @echo "Waiting 10s for containers to start..." - @sleep 10 - python3 docker_monitor.py --networks $(MULTISYNC_NETWORKS) --exit-on-success - -multisync-loop: ## Continuous loop: sync all networks, restart on success, repeat forever. - $(MULTISYNC_COMPOSE) up -d $(MULTISYNC_SERVICES) - @echo "Waiting 10s for containers to start..." - @sleep 10 - python3 docker_monitor.py --networks $(MULTISYNC_NETWORKS) --compose-file docker-compose.multisync.yaml --compose-dir $(CURDIR) - -multisync-history: ## View the run history log. - @if [ -f multisync_logs/run_history.log ]; then \ - cat multisync_logs/run_history.log; \ - else \ - echo "No run history found. Run 'make multisync-loop' first."; \ - fi - -multisync-list-logs: ## List all saved run logs. - @if [ -d multisync_logs ]; then \ - echo "=== Saved Run Logs ===" && \ - ls -la multisync_logs/ && \ - echo "" && \ - for dir in multisync_logs/run_*/; do \ - if [ -d "$$dir" ]; then \ - echo "$$dir:"; \ - ls "$$dir"; \ - echo ""; \ - fi; \ - done; \ - else \ - echo "No logs directory found."; \ - fi - -# ============================================================================== -# Multisync with Auto-Update and Validation -# ============================================================================== -# The multisync-loop-auto target pulls latest code and rebuilds before each run. -# When using 'release-with-debug-assertions' profile, state trie validation is -# enabled (same as daily snapsync CI checks). The validation verifies: -# - State root: traverses entire account trie, validates all node hashes -# - Storage roots: validates each account's storage trie (parallelized) -# - Bytecodes: verifies code exists for all accounts with code -# -# If validation fails, the node exits with code 1 and logs the error. -# ============================================================================== - -multisync-loop-auto: ## Continuous loop with auto-update: pull latest, build, and validate on each run. - @echo "Starting multisync loop with auto-update enabled..." - @echo "Branch: $(MULTISYNC_BRANCH)" - @echo "Build profile: $(MULTISYNC_BUILD_PROFILE)" - @echo "Image tag: $(MULTISYNC_LOCAL_IMAGE)" - python3 docker_monitor.py \ - --networks $(MULTISYNC_NETWORKS) \ - --compose-file docker-compose.multisync.yaml \ - --compose-dir $(CURDIR) \ - --auto-update \ - --branch "$(MULTISYNC_BRANCH)" \ - --build-profile "$(MULTISYNC_BUILD_PROFILE)" \ - --image-tag "$(MULTISYNC_LOCAL_IMAGE)" \ - --ethrex-dir "$(ETHREX_DIR)" diff --git a/tooling/sync/README.md b/tooling/sync/README.md deleted file mode 100644 index ce738fc5bfb..00000000000 --- a/tooling/sync/README.md +++ /dev/null @@ -1,233 +0,0 @@ -# Quick starter guide to sync tooling - -The targets provided by the makefile aim towards making starting a sync or running a benchmark on Ethrex much simpler. This readme will provide a quick explanation to get you started. - -## Environment variables - -The commands use a number of environment variables, which can be easily passed alongside the `make` command to provide some settings to the target being run. Many of the commands *will not run* if requisite environment variables aren't set. These variables are: - -- `NETWORK`: network on which to sync (at the moment, only mainnet, sepolia, holesky and hoodi are supported as options). If this variable is not set `mainnet` will be used by default. - -- `EVM`: the EVM which will be used. `levm` is the default, but it can be set to `revm` as well. - -- `LOGNAME`: used in the flamegraph commands to append a custom naming to the default name scheme, and in the tailing commands to select the log file to tail. - -- `SYNC_BLOCK_NUM`: block number on which to start the sync. Required by both the `sync` and `flamegraph` commands. All the commands which use this variable require it to be set by the user. - -- `EXECUTE_BATCH_SIZE`: the amount of blocks to execute in batch during full sync. Optional. - -- `BRANCH`: required by the `flamegraph-branch` command. Branch on which to run. - -- `GRAPHNAME`: used by the `copy-flamegraph` command to provide a custom name to the flamegraph being copied. - -## Logs - -All logs are output to the `logs` folder in `tooling/sync`. The sync logs follow the naming convention `ethrex-sync-NETWORK-EVM.log` (replacing NETWORK and EVM with the network and evm being used), whereas all the flamegraph logs follow the naming convention `ethrex-NETWORK-EVM-flamegraph-CURRENT_DATETIME-BRANCH-block-BLOCK_NUM-LOGNAME.log`, with CURRENT_DATETIME being the date and time the run was started in in the format YY.MM.DD-HH.MM.SS, BRANCH being the ethrex repository branch the run was done on, and SYNC_BLOCK_NUM being the block the sync was started on. - -## Database location - -The databases are stored in the `~/.local/share/` folder in Linux, and `~/Library/Application Support` in Mac. For each network, a NETWORK_data folder is created. Inside this folder is the jwt our command creates, and an `ethrex` folder; which will contain one EVM folder for each evm ethrex was ran with on the network that corresponds to the current path (so, for example, if a sync was run with levm on hoodi, a `~/.local/share/hoodi_data/ethrex/levm` folder will be present. Then, if another sync in hoodi is run with revm, a `~/.local/share/hoodi_data/ethrex/revm` will be created). - -## Running a sync - -Lighthouse must be running for the sync to work. Aditionally, a jwt has to be provided too. The SYNC_BLOCK_NUM also has to be one a batch ended on for that network and evm. *The sync will not work if not started from a block number like such*, so it's important to check the numebr carefully. - -## Running flamegraphs - -You will first need to install flamegraph by running: - -```=bash -cargo install flamegraph -``` - -It's advisable to only run flamegraphs on blocks that have already been synced, so that the overhead of retrieving the headers and bodies from the network doesn't distort the measurements. The generated flamegraphs are stored by default in the ethrex root folder. You can run the flamegraph using the provided commands. The run has to be stopped manually interrupting it with `ctrl + c`. Afterwards, a script starts that creates a flamegraph from the gathered data. Once this script finishes, the flamegraph should be ready. - -## Commands - -- `make gen_jwt` generates the jwt to use to connect to the network. `NETWORK` must be provided. - -- `make sync` can be used to start a sync. `NETWORK` and `SYNC_BLOCK_NUM` must be provided, `EVM` can be optionally provided too. - -- `make flamegraph-main` and `make flamegraph-branch` can be used to run benchmarks on the main branch of the repo or a custom branch, respectively; generating both a flamegraph and logs of the run. `NETWORK` and `SYNC_BLOCK_NUM` must be provided, `EVM` can be optionally provided too. `BRANCH` must be provided for `flamegraph-branch` as well. `make flamegraph` can also be used as a branch agnostic option. - -- `make start-lighthouse` can be used to start lighthouse. `NETWORK` must be provided or else mainnet will be used as default. - -- `make backup-db` can be used to create a backup of the database. `NETWORK` must be provided, and `EVM` should be provided too. Backups are stored in `~/.local/share/ethrex_db_backups` in Linux and `~/Library/Application Support/ethrex_db_backups` folder in MacOS. The logs up to that point are also backed up in the same folder. - -- `make tail-syncing-logs` can be used to easily tail the syncing information in any given log. `LOGNAME` must be provided to indicate the log file to tail. - -- `make tail-metrics-logs` can be used to easily tail the metrics information in any given log (how long batches are taking to process). `LOGNAME` must be provided to indicate the log file to tail. - -- `make copy-flamegraph` can be used to quickly copy the flamegraph generated by the flamegraph commands from the `ethrex` repo folder to the `tooling/sync/flamegraphs` folder so it isn't overwritten by future flamegraph runs. `GRAPHNAME` can be provided to give the file a custom name. - -- `make import-with-metrics` can be used to import blocks from an RLP file with metrics enabled, specially useful for a block processing profile. The path to the rlp file can be passed with the `RLP_FILE` environment variable, while the network can be provided with the `NETWORK` variable. - -## Multi-Network Parallel Snapsync - -This feature allows running multiple Ethrex nodes in parallel (hoodi, sepolia, mainnet) via Docker Compose, with automated monitoring, Slack notifications, and a history log of runs. - -### Overview - -The parallel snapsync system: -- Spawns multiple networks simultaneously via Docker Compose -- Monitors snapsync progress with configurable timeout (default 8 hours) -- Verifies block processing after sync completion (default 22 minutes) -- Sends Slack notifications on success/failure -- Maintains a history log of all runs -- On success: restarts containers and begins a new sync cycle -- On failure: keeps containers running for debugging - -### Auto-Update Mode with State Trie Validation - -The `multisync-loop-auto` target provides continuous integration testing by: -1. **Pulling latest code** from a configured branch before each run -2. **Building Docker image** with configurable Cargo profile -3. **Running state trie validation** when using `release-with-debug-assertions` profile -4. **Looping continuously** on success, stopping on failure for inspection - -State trie validation (enabled with `release-with-debug-assertions` profile) verifies: -- **State root**: Traverses entire account trie, validates all node hashes -- **Storage roots**: Validates each account's storage trie (parallelized) -- **Bytecodes**: Verifies code exists for all accounts with code - -This mirrors the daily snapsync CI checks but runs continuously on your own infrastructure. - -**Quick Start:** - -```bash -# Run with validation on current branch -make multisync-loop-auto - -# Run on specific branch -make multisync-loop-auto MULTISYNC_BRANCH=main - -# Run without validation (faster builds) -make multisync-loop-auto MULTISYNC_BUILD_PROFILE=release -``` - -**Configuration (in `.env` or as make variables):** - -| Variable | Default | Description | -|----------|---------|-------------| -| `MULTISYNC_BRANCH` | current branch | Git branch to track | -| `MULTISYNC_BUILD_PROFILE` | `release-with-debug-assertions` | Cargo build profile | -| `MULTISYNC_LOCAL_IMAGE` | `ethrex-local:multisync` | Docker image tag | -| `MULTISYNC_NETWORKS` | `hoodi,sepolia,mainnet` | Networks to sync | - -**Run count persistence:** The run count is persisted across restarts by reading from the history log. If run #5 fails and you restart, the next run will be #6. - -### Requirements - -- Docker and Docker Compose -- Python 3 with the `requests` library (`pip install requests`) -- (Optional) Slack webhook URLs for notifications - -### Quick Start - -```bash -# Start a continuous monitoring loop (recommended for servers) -make multisync-loop - -# Or run a single sync cycle -make multisync-run -``` - -### Docker Compose Setup - -The `docker-compose.multisync.yaml` file defines services for each network with isolated volumes. Each network uses Lighthouse as the consensus client with checkpoint sync. - -Host port mapping: -- **hoodi**: `localhost:8545` -- **sepolia**: `localhost:8546` -- **mainnet**: `localhost:8547` -- **hoodi-2**: `localhost:8548` (for additional testing) - -### Environment Variables - -Create a `.env` file in `tooling/sync/` with: - -```bash -# Slack notifications (optional) -SLACK_WEBHOOK_URL_SUCCESS=https://hooks.slack.com/services/... -SLACK_WEBHOOK_URL_FAILED=https://hooks.slack.com/services/... - -# Monitoring timeouts (optional - values shown are defaults) -SYNC_TIMEOUT=480 # Sync timeout in minutes (default: 8 hours) -BLOCK_PROCESSING_DURATION=1320 # Block processing verification in seconds (default: 22 minutes) -BLOCK_STALL_TIMEOUT=600 # Fail if no new block for this many seconds (default: 10 minutes) -NODE_UNRESPONSIVE_TIMEOUT=300 # Fail if node unresponsive for this many seconds (default: 5 minutes) -CHECK_INTERVAL=10 # How often to check node status in seconds -STATUS_PRINT_INTERVAL=30 # How often to print status in seconds -``` - -The `MULTISYNC_NETWORKS` variable controls which networks to sync (default: `hoodi,sepolia,mainnet`): - -```bash -# Sync only hoodi and sepolia -make multisync-loop MULTISYNC_NETWORKS=hoodi,sepolia -``` - -### Monitoring Behavior - -The `docker_monitor.py` script manages the sync lifecycle: - -1. **Waiting**: Node container starting up -2. **Syncing**: Snapsync in progress -3. **Block Processing**: Sync complete, verifying block processing -4. **Success**: Network synced and processing blocks -5. **Failed**: Timeout, stall, or error detected - -The monitor checks for: -- Sync timeout (default 8 hours, configurable via `SYNC_TIMEOUT`) -- Block processing stall (default 10 minutes without new blocks, configurable via `BLOCK_STALL_TIMEOUT`) -- Node unresponsiveness (default 5 minutes, configurable via `NODE_UNRESPONSIVE_TIMEOUT`) - -### Logs and History - -Logs are saved to `tooling/sync/multisync_logs/`: - -``` -multisync_logs/ -├── run_history.log # Append-only history of all runs -└── run_YYYYMMDD_HHMMSS/ # Per-run folder - ├── summary.txt # Run summary - ├── ethrex-hoodi.log # Ethrex logs per network - ├── consensus-hoodi.log # Lighthouse logs per network - └── ... -``` - -### Commands - -**Starting and Stopping:** - -- `make multisync-up` starts all networks via Docker Compose. -- `make multisync-down` stops and removes containers (preserves volumes). -- `make multisync-clean` stops containers and removes volumes (full reset). -- `make multisync-restart` restarts the cycle (clean volumes + start fresh). - -**Monitoring:** - -- `make multisync-loop` runs continuous sync cycles (recommended for servers). On success, restarts and syncs again. On failure, stops for debugging. -- `make multisync-run` runs a single sync cycle and exits on completion. -- `make multisync-monitor` monitors already-running containers (one-shot). - -**Logs:** - -- `make multisync-logs` tails logs from all networks. -- `make multisync-logs-hoodi` tails logs for a specific network. -- `make multisync-logs-ethrex-hoodi` tails only ethrex logs for a network. -- `make multisync-logs-consensus-hoodi` tails only consensus logs for a network. -- `make multisync-history` views the run history log. -- `make multisync-list-logs` lists all saved run logs. - -### Slack Notifications - -When configured, notifications are sent: -- On **success**: All networks synced and processing blocks -- On **failure**: Any network failed (timeout, stall, or error) - -Notifications include: -- Run ID and count -- Host, branch, and commit info -- Per-network status with sync time and blocks processed -- Link to the commit on GitHub diff --git a/tooling/sync/docker-compose.multisync.yaml b/tooling/sync/docker-compose.multisync.yaml deleted file mode 100644 index 200f91326a7..00000000000 --- a/tooling/sync/docker-compose.multisync.yaml +++ /dev/null @@ -1,240 +0,0 @@ -# Multi-network parallel snapsync -# -# For full documentation, see: tooling/sync/README.md (section "Multi-Network Parallel Snapsync") -# -# This file defines 4 networks: hoodi, sepolia, mainnet, and hoodi-2. -# By default, the Makefile starts only hoodi, sepolia, and mainnet. -# -# Usage via Makefile (recommended): -# make multisync-loop # Start default networks and monitor continuously -# make multisync-run # Single sync cycle -# make multisync-loop MULTISYNC_NETWORKS=hoodi,sepolia # Custom network subset -# -# Direct docker compose usage (starts ALL 4 networks): -# docker compose -f docker-compose.multisync.yaml up -d -# docker compose -f docker-compose.multisync.yaml logs -f -# docker compose -f docker-compose.multisync.yaml down -v -# -# To start a subset directly with docker compose: -# docker compose -f docker-compose.multisync.yaml up -d ethrex-hoodi consensus-hoodi setup-jwt-hoodi -# -# Each network runs in isolation with its own volumes. -# Host ports are mapped for external RPC access: -# hoodi: localhost:8545 -# sepolia: localhost:8546 -# mainnet: localhost:8547 -# hoodi-2: localhost:8548 - -x-ethrex-common: ðrex-common - image: "${ETHREX_IMAGE:-ghcr.io/lambdaclass/ethrex:main}" - pull_policy: "${ETHREX_PULL_POLICY:-always}" - ulimits: - nofile: 1000000 - restart: unless-stopped - -x-consensus-common: &consensus-common - image: sigp/lighthouse:v8.0.1 - restart: unless-stopped - -services: - # ============================================================================= - # HOODI - # ============================================================================= - setup-jwt-hoodi: - image: alpine - volumes: - - secrets-hoodi:/secrets - command: sh -c 'apk add openssl && openssl rand -hex 32 | tr -d "\n" | tee /secrets/jwt.hex' - - consensus-hoodi: - <<: *consensus-common - container_name: consensus-hoodi - volumes: - - secrets-hoodi:/secrets - - consensus-hoodi:/root/.lighthouse - command: > - lighthouse bn - --network hoodi - --http --http-address 0.0.0.0 - --execution-endpoint http://ethrex-hoodi:8551 - --execution-jwt /secrets/jwt.hex - --checkpoint-sync-url https://hoodi-checkpoint-sync.stakely.io/ - --checkpoint-sync-url-timeout 600 - --purge-db - depends_on: - setup-jwt-hoodi: - condition: service_completed_successfully - - ethrex-hoodi: - <<: *ethrex-common - container_name: ethrex-hoodi - ports: - - "8545:8545" # RPC - volumes: - - secrets-hoodi:/secrets - - ethrex-hoodi:/data - command: > - --http.addr 0.0.0.0 - --network hoodi - --authrpc.addr 0.0.0.0 - --authrpc.jwtsecret /secrets/jwt.hex - --syncmode snap - --datadir /data - depends_on: - setup-jwt-hoodi: - condition: service_completed_successfully - - # ============================================================================= - # SEPOLIA - # ============================================================================= - setup-jwt-sepolia: - image: alpine - volumes: - - secrets-sepolia:/secrets - command: sh -c 'apk add openssl && openssl rand -hex 32 | tr -d "\n" | tee /secrets/jwt.hex' - - consensus-sepolia: - <<: *consensus-common - container_name: consensus-sepolia - volumes: - - secrets-sepolia:/secrets - - consensus-sepolia:/root/.lighthouse - command: > - lighthouse bn - --network sepolia - --http --http-address 0.0.0.0 - --execution-endpoint http://ethrex-sepolia:8551 - --execution-jwt /secrets/jwt.hex - --checkpoint-sync-url https://checkpoint-sync.sepolia.ethpandaops.io - --checkpoint-sync-url-timeout 600 - --purge-db - depends_on: - setup-jwt-sepolia: - condition: service_completed_successfully - - ethrex-sepolia: - <<: *ethrex-common - container_name: ethrex-sepolia - ports: - - "8546:8545" # RPC on different host port - volumes: - - secrets-sepolia:/secrets - - ethrex-sepolia:/data - command: > - --http.addr 0.0.0.0 - --network sepolia - --authrpc.addr 0.0.0.0 - --authrpc.jwtsecret /secrets/jwt.hex - --syncmode snap - --datadir /data - depends_on: - setup-jwt-sepolia: - condition: service_completed_successfully - - # ============================================================================= - # MAINNET - # ============================================================================= - setup-jwt-mainnet: - image: alpine - volumes: - - secrets-mainnet:/secrets - command: sh -c 'apk add openssl && openssl rand -hex 32 | tr -d "\n" | tee /secrets/jwt.hex' - - consensus-mainnet: - <<: *consensus-common - container_name: consensus-mainnet - volumes: - - secrets-mainnet:/secrets - - consensus-mainnet:/root/.lighthouse - command: > - lighthouse bn - --network mainnet - --http --http-address 0.0.0.0 - --execution-endpoint http://ethrex-mainnet:8551 - --execution-jwt /secrets/jwt.hex - --checkpoint-sync-url https://mainnet-checkpoint-sync.attestant.io - --checkpoint-sync-url-timeout 600 - --purge-db - depends_on: - setup-jwt-mainnet: - condition: service_completed_successfully - - ethrex-mainnet: - <<: *ethrex-common - container_name: ethrex-mainnet - ports: - - "8547:8545" # RPC on different host port - volumes: - - secrets-mainnet:/secrets - - ethrex-mainnet:/data - command: > - --http.addr 0.0.0.0 - --network mainnet - --authrpc.addr 0.0.0.0 - --authrpc.jwtsecret /secrets/jwt.hex - --syncmode snap - --datadir /data - depends_on: - setup-jwt-mainnet: - condition: service_completed_successfully - - # ============================================================================= - # HOODI-2 - # ============================================================================= - setup-jwt-hoodi-2: - image: alpine - volumes: - - secrets-hoodi-2:/secrets - command: sh -c 'apk add openssl && openssl rand -hex 32 | tr -d "\n" | tee /secrets/jwt.hex' - - consensus-hoodi-2: - <<: *consensus-common - container_name: consensus-hoodi-2 - volumes: - - secrets-hoodi-2:/secrets - - consensus-hoodi-2:/root/.lighthouse - command: > - lighthouse bn - --network hoodi - --http --http-address 0.0.0.0 - --execution-endpoint http://ethrex-hoodi-2:8551 - --execution-jwt /secrets/jwt.hex - --checkpoint-sync-url https://hoodi-checkpoint-sync.stakely.io/ - --checkpoint-sync-url-timeout 600 - --purge-db - depends_on: - setup-jwt-hoodi-2: - condition: service_completed_successfully - - ethrex-hoodi-2: - <<: *ethrex-common - container_name: ethrex-hoodi-2 - ports: - - "8548:8545" # RPC on different host port - volumes: - - secrets-hoodi-2:/secrets - - ethrex-hoodi-2:/data - command: > - --http.addr 0.0.0.0 - --network hoodi - --authrpc.addr 0.0.0.0 - --authrpc.jwtsecret /secrets/jwt.hex - --syncmode snap - --datadir /data - depends_on: - setup-jwt-hoodi-2: - condition: service_completed_successfully - -volumes: - secrets-hoodi: - secrets-sepolia: - secrets-mainnet: - consensus-hoodi: - consensus-sepolia: - consensus-mainnet: - ethrex-hoodi: - ethrex-sepolia: - ethrex-mainnet: - secrets-hoodi-2: - consensus-hoodi-2: - ethrex-hoodi-2: diff --git a/tooling/sync/docker-compose.yml b/tooling/sync/docker-compose.yml deleted file mode 100644 index 674b7d08dde..00000000000 --- a/tooling/sync/docker-compose.yml +++ /dev/null @@ -1,55 +0,0 @@ -services: - setup_jwt: - image: alpine - volumes: - - secrets:/secrets - command: sh -c 'apk add openssl && openssl rand -hex 32 | tr -d "\n" | tee /secrets/jwt.hex' - - lighthouse: - container_name: lighthouse - image: sigp/lighthouse - ports: - - "9000:9000/tcp" - - "9000:9000/udp" - - "9001:9001/udp" - - 127.0.0.1:5052:5052 - volumes: - - secrets:/secrets - command: > - lighthouse - --network hoodi - beacon - --http - --http-address 0.0.0.0 - --execution-endpoint http://ethrex:8551 - --execution-jwt /secrets/jwt.hex - --checkpoint-sync-url https://hoodi-checkpoint-sync.attestant.io - --checkpoint-sync-url-timeout 600 - depends_on: - setup_jwt: - condition: service_completed_successfully - - ethrex: - container_name: ethrex - image: "ethrex:unstable" - build: ../../ - ports: - - 127.0.0.1:8545:8545 - - "30303:30303/tcp" - - "30303:30303/udp" - volumes: - - secrets:/secrets - environment: - - RUST_LOG=ethrex_p2p::rlpx::eth::blocks=off,ethrex_p2p::sync=debug,ethrex_p2p::network=info,spawned_concurrency::tasks::gen_server=off - command: > - --http.addr 0.0.0.0 - --authrpc.addr 0.0.0.0 - --network hoodi - --authrpc.jwtsecret /secrets/jwt.hex - --syncmode snap - depends_on: - setup_jwt: - condition: service_completed_successfully - -volumes: - secrets: diff --git a/tooling/sync/docker_monitor.py b/tooling/sync/docker_monitor.py deleted file mode 100644 index 0ab1c4254c0..00000000000 --- a/tooling/sync/docker_monitor.py +++ /dev/null @@ -1,829 +0,0 @@ -#!/usr/bin/env python3 -"""Monitor Docker Compose snapsync instances for sync completion.""" - -import argparse -import os -import re -import socket -import subprocess -import sys -import time -from dataclasses import dataclass -from datetime import datetime -from pathlib import Path -from typing import Any, Optional - -import requests - -# Load .env file if it exists -if os.path.exists('.env'): - with open('.env') as f: - for line in f: - line = line.strip() - if line and not line.startswith('#'): - key, _, value = line.partition('=') - os.environ[key.strip()] = value.strip() - -CHECK_INTERVAL = int(os.environ.get("CHECK_INTERVAL", 10)) -SYNC_TIMEOUT = int(os.environ.get("SYNC_TIMEOUT", 8 * 60)) # default 8 hours (in minutes) -BLOCK_PROCESSING_DURATION = int(os.environ.get("BLOCK_PROCESSING_DURATION", 22 * 60)) # default 22 minutes (in seconds) -BLOCK_STALL_TIMEOUT = int(os.environ.get("BLOCK_STALL_TIMEOUT", 10 * 60)) # default 10 minutes (in seconds) -NODE_UNRESPONSIVE_TIMEOUT = int(os.environ.get("NODE_UNRESPONSIVE_TIMEOUT", 5 * 60)) # default 5 minutes (in seconds) -STATUS_PRINT_INTERVAL = int(os.environ.get("STATUS_PRINT_INTERVAL", 30)) - -# Network to port mapping (fixed in docker-compose.multisync.yaml) -NETWORK_PORTS = { - "hoodi": 8545, - "sepolia": 8546, - "mainnet": 8547, - "hoodi-2": 8548, -} - -# Logging configuration -LOGS_DIR = Path("./multisync_logs") -RUN_LOG_FILE = LOGS_DIR / "run_history.log" # Append-only text log - -STATUS_EMOJI = { - "waiting": "⏳", "syncing": "🔄", "synced": "✅", - "block_processing": "📦", "success": "🎉", "failed": "❌" -} - -# Phase completion patterns for parsing sync logs -PHASE_COMPLETION_PATTERNS = { - "Block Headers": r"✓ BLOCK HEADERS complete: ([\d,]+) headers in (\d+:\d{2}:\d{2})", - "Account Ranges": r"✓ ACCOUNT RANGES complete: ([\d,]+) accounts in (\d+:\d{2}:\d{2})", - "Account Insertion": r"✓ ACCOUNT INSERTION complete: ([\d,]+) accounts inserted in (\d+:\d{2}:\d{2})", - "Storage Ranges": r"✓ STORAGE RANGES complete: ([\d,]+) storage slots in (\d+:\d{2}:\d{2})", - "Storage Insertion": r"✓ STORAGE INSERTION complete: ([\d,]+) storage slots inserted in (\d+:\d{2}:\d{2})", - "State Healing": r"✓ STATE HEALING complete: ([\d,]+) state paths healed in (\d+:\d{2}:\d{2})", - "Storage Healing": r"✓ STORAGE HEALING complete: ([\d,]+) storage accounts healed in (\d+:\d{2}:\d{2})", - "Bytecodes": r"✓ BYTECODES complete: ([\d,]+) bytecodes in (\d+:\d{2}:\d{2})", -} - - -@dataclass -class Instance: - name: str - port: int - container: str = "" - status: str = "waiting" - start_time: float = 0 - sync_time: float = 0 - last_block: int = 0 - last_block_time: float = 0 # When we last saw a new block - block_check_start: float = 0 - initial_block: int = 0 # Block when entering block_processing - error: str = "" - first_failure_time: float = 0 - validation_status: str = "" # Current validation progress (if any) - - @property - def rpc_url(self) -> str: - return f"http://localhost:{self.port}" - - -def fmt_time(secs: float) -> str: - secs = int(abs(secs)) - h, m, s = secs // 3600, (secs % 3600) // 60, secs % 60 - return " ".join(f"{v}{u}" for v, u in [(h, "h"), (m, "m"), (s, "s")] if v or (not h and not m)) - - -def git_commit() -> str: - try: - return subprocess.check_output(["git", "rev-parse", "--short", "HEAD"], stderr=subprocess.DEVNULL).decode().strip() - except Exception: - return "unknown" - - -def git_branch() -> str: - try: - return subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"], stderr=subprocess.DEVNULL).decode().strip() - except Exception: - return "unknown" - - -def git_pull_latest(branch: str, ethrex_dir: str) -> tuple[bool, str]: - """Fetch and pull latest changes from the specified branch. - - Returns (success, new_commit_hash). - """ - try: - print(f"📥 Pulling latest from branch '{branch}'...") - # Fetch all remotes - subprocess.run(["git", "fetch", "--all"], cwd=ethrex_dir, check=True, capture_output=True) - # Checkout the branch - subprocess.run(["git", "checkout", branch], cwd=ethrex_dir, check=True, capture_output=True) - # Pull latest from origin explicitly - subprocess.run(["git", "pull", "origin", branch], cwd=ethrex_dir, check=True, capture_output=True) - # Get new commit hash - new_commit = subprocess.check_output( - ["git", "rev-parse", "--short", "HEAD"], - cwd=ethrex_dir, - stderr=subprocess.DEVNULL - ).decode().strip() - print(f"✅ Updated to commit {new_commit}") - return True, new_commit - except subprocess.CalledProcessError as e: - error_details = "" - if e.stderr: - error_details = e.stderr.decode(errors='replace').strip() - print(f"❌ Failed to pull latest: {e}") - if error_details: - print(f" {error_details}") - return False, "" - - -def build_docker_image(profile: str, image_tag: str, ethrex_dir: str) -> bool: - """Build the Docker image with the specified profile. - - Args: - profile: Cargo build profile (e.g., 'release-with-debug-assertions') - image_tag: Docker image tag (e.g., 'ethrex-local:validate') - ethrex_dir: Path to ethrex repository root - """ - print(f"🔨 Building Docker image with profile '{profile}'...") - print(f" Image tag: {image_tag}") - try: - subprocess.run( - [ - "docker", "build", - "--build-arg", f"PROFILE={profile}", - "-t", image_tag, - "-f", f"{ethrex_dir}/Dockerfile", - ethrex_dir - ], - check=True - ) - print("✅ Docker image built successfully") - return True - except subprocess.CalledProcessError as e: - print(f"❌ Failed to build Docker image: {e}") - return False - - -def container_start_time(name: str) -> Optional[float]: - try: - out = subprocess.check_output(["docker", "inspect", "-f", "{{.State.StartedAt}}", name], stderr=subprocess.DEVNULL).decode().strip() - if '.' in out: - base, frac = out.rsplit('.', 1) - out = f"{base}.{frac.rstrip('Z')[:6]}" - return datetime.fromisoformat(out.replace('Z', '+00:00')).timestamp() - except Exception: - return None - - -def container_exit_info(name: str) -> tuple[Optional[bool], Optional[int]]: - """Check if container has exited and get exit code. - - Returns: - (is_running, exit_code) - is_running is True if running, False if exited, None on error. - exit_code is the exit code if exited, None otherwise. - """ - try: - # Get container state - status = subprocess.check_output( - ["docker", "inspect", "-f", "{{.State.Status}}", name], - stderr=subprocess.DEVNULL - ).decode().strip() - - if status == "running": - return True, None - elif status == "exited": - exit_code = subprocess.check_output( - ["docker", "inspect", "-f", "{{.State.ExitCode}}", name], - stderr=subprocess.DEVNULL - ).decode().strip() - return False, int(exit_code) - else: - return None, None - except Exception: - return None, None - - -# Validation error patterns to look for in container logs -VALIDATION_ERROR_PATTERNS = [ - "We have failed the validation of the state tree", - "validate_storage_root", - "Missing code hash", - "Node count mismatch", - "TrieError::Verify", -] - -# Validation progress patterns - indicate validation is running -VALIDATION_PROGRESS_PATTERNS = [ - ("Starting validate_state_root", "validating state root"), - ("Starting validate_storage_root", "validating storage roots"), - ("Starting validate_bytecodes", "validating bytecodes"), - ("Finished validate_storage_root", "storage validation complete"), - ("Succesfully validated tree", "state validation complete"), -] - - -def check_validation_failure(container: str) -> Optional[str]: - """Check container logs for validation failure messages. - - Returns a validation error message if found, None otherwise. - """ - try: - # Get last 100 lines of logs (validation errors should be near the end) - logs = subprocess.check_output( - ["docker", "logs", "--tail", "100", container], - stderr=subprocess.STDOUT, - timeout=10 - ).decode(errors='replace') - - for pattern in VALIDATION_ERROR_PATTERNS: - if pattern in logs: - return f"State trie validation failed: found '{pattern}' in logs" - return None - except Exception: - return None - - -def check_validation_progress(container: str) -> Optional[str]: - """Check container logs for validation progress. - - Returns the latest validation status if validation is in progress, None otherwise. - """ - try: - # Get last 200 lines of logs to catch validation messages - logs = subprocess.check_output( - ["docker", "logs", "--tail", "200", container], - stderr=subprocess.STDOUT, - timeout=10 - ).decode(errors='replace') - - # Find the most recent validation progress message - latest_status = None - latest_pos = -1 - for pattern, status in VALIDATION_PROGRESS_PATTERNS: - pos = logs.rfind(pattern) - if pos > latest_pos: - latest_pos = pos - latest_status = status - - return latest_status - except Exception: - return None - - -def rpc_call(url: str, method: str) -> Optional[Any]: - try: - return requests.post(url, json={"jsonrpc": "2.0", "method": method, "params": [], "id": 1}, timeout=5).json().get("result") - except Exception: - return None - - -def parse_phase_timings(run_id: str, container: str) -> list[tuple[str, str, str]]: - """Parse phase completion times from saved container logs. - - Returns list of (phase_name, count, duration) tuples. - """ - log_file = LOGS_DIR / f"run_{run_id}" / f"{container}.log" - if not log_file.exists(): - return [] - - try: - logs = log_file.read_text() - except Exception: - return [] - - phases = [] - for phase_name, pattern in PHASE_COMPLETION_PATTERNS.items(): - match = re.search(pattern, logs) - if match: - count = match.group(1) - duration = match.group(2) - phases.append((phase_name, count, duration)) - return phases - - -def slack_notify(run_id: str, run_count: int, instances: list, hostname: str, branch: str, commit: str, build_profile: str = ""): - """Send a single summary Slack message for the run.""" - all_success = all(i.status == "success" for i in instances) - url = os.environ.get("SLACK_WEBHOOK_URL_SUCCESS" if all_success else "SLACK_WEBHOOK_URL_FAILED") - if not url: - return - status_icon = "✅" if all_success else "❌" - header = f"{status_icon} Run #{run_count} (ID: {run_id})" - run_start = datetime.strptime(run_id, "%Y%m%d_%H%M%S") - elapsed_secs = (datetime.now() - run_start).total_seconds() - elapsed_str = fmt_time(elapsed_secs) - - # Validation is enabled when using debug-assertions profile - validation_enabled = "debug-assertions" in build_profile - validation_str = "enabled ✓" if validation_enabled else "disabled" - - # Check for validation failures - validation_failures = [i for i in instances if i.error and "validation" in i.error.lower()] - - summary = ( - f"*Host:* `{hostname}`\n" - f"*Branch:* `{branch}`\n" - f"*Commit:* \n" - f"*Elapsed:* `{elapsed_str}`\n" - f"*Validation:* `{validation_str}`\n" - f"*Logs:* `tooling/sync/multisync_logs/run_{run_id}`\n" - f"*Result:* {'SUCCESS' if all_success else 'FAILED'}" - ) - blocks = [ - {"type": "header", "text": {"type": "plain_text", "text": header}}, - {"type": "section", "text": {"type": "mrkdwn", "text": summary}}, - {"type": "divider"} - ] - - # Add validation failure warning if any - if validation_failures: - blocks.append({ - "type": "section", - "text": {"type": "mrkdwn", "text": "⚠️ *State trie validation failed!* Check logs for details."} - }) - - for i in instances: - icon = "✅" if i.status == "success" else "❌" - line = f"{icon} *{i.name}*: `{i.status}`" - if i.sync_time: - line += f" (sync: {fmt_time(i.sync_time)})" - # Show validation status if it was tracked - if i.validation_status: - line += f" [validation: {i.validation_status}]" - if i.initial_block: - line += f" post-sync block: {i.initial_block}" - if i.initial_block and i.last_block > i.initial_block: - blocks_processed = i.last_block - i.initial_block - line += f" (processed +{blocks_processed} blocks in {BLOCK_PROCESSING_DURATION//60}m)" - if i.error: - line += f"\n Error: {i.error}" - blocks.append({"type": "section", "text": {"type": "mrkdwn", "text": line}}) - - # Add phase breakdown for each instance - for i in instances: - phases = parse_phase_timings(run_id, i.container) - if phases: - phase_lines = [f"📊 *Phase Breakdown — {i.name}*", "```"] - max_name_len = max(len(name) for name, _, _ in phases) - for name, count, duration in phases: - phase_lines.append(f"{name:<{max_name_len}} {duration} ({count})") - phase_lines.append("```") - blocks.append({ - "type": "section", - "text": {"type": "mrkdwn", "text": "\n".join(phase_lines)} - }) - - try: - requests.post(url, json={"blocks": blocks}, timeout=10) - except Exception: - # Slack notification failures are non-critical; ignore them so they - # do not interfere with the main monitoring workflow. - pass - - -def ensure_logs_dir(): - """Ensure the logs directory exists.""" - LOGS_DIR.mkdir(parents=True, exist_ok=True) - - -def save_container_logs(container: str, run_id: str, suffix: str = ""): - """Save container logs to a file.""" - log_file = LOGS_DIR / f"run_{run_id}" / f"{container}{suffix}.log" - log_file.parent.mkdir(parents=True, exist_ok=True) - try: - logs = subprocess.check_output( - ["docker", "logs", container], - stderr=subprocess.STDOUT, - timeout=60 - ).decode(errors='replace') - log_file.write_text(logs) - print(f" 📄 Saved logs: {log_file}") - return True - except subprocess.CalledProcessError as e: - print(f" ⚠️ Failed to get logs for {container}: {e}") - return False - except subprocess.TimeoutExpired: - print(f" ⚠️ Timeout getting logs for {container}") - return False - except Exception as e: - print(f" ⚠️ Error saving logs for {container}: {e}") - return False - - -def save_all_logs(instances: list[Instance], run_id: str, compose_file: str): - """Save logs for all containers (ethrex + consensus).""" - print(f"\n📁 Saving logs for run {run_id}...") - - for inst in instances: - # Save ethrex logs - save_container_logs(inst.container, run_id) - # Save consensus logs (convention: consensus-{network}) - consensus_container = inst.container.replace("ethrex-", "consensus-") - save_container_logs(consensus_container, run_id) - - print(f"📁 Logs saved to {LOGS_DIR}/run_{run_id}/\n") - - -def log_run_result(run_id: str, run_count: int, instances: list[Instance], hostname: str, branch: str, commit: str, build_profile: str = ""): - """Append run result to the persistent log file.""" - ensure_logs_dir() - all_success = all(i.status == "success" for i in instances) - status_icon = "✅" if all_success else "❌" - run_start = datetime.strptime(run_id, "%Y%m%d_%H%M%S") - elapsed_secs = (datetime.now() - run_start).total_seconds() - elapsed_str = fmt_time(elapsed_secs) - - # Validation is enabled when using debug-assertions profile - validation_enabled = "debug-assertions" in build_profile - validation_str = "enabled" if validation_enabled else "disabled" - validation_failures = [i for i in instances if i.error and "validation" in i.error.lower()] - - # Build log entry as plain text - lines = [ - f"\n{'='*60}", - f"{status_icon} Run #{run_count} (ID: {run_id})", - f"{'='*60}", - f"Host: {hostname}", - f"Branch: {branch}", - f"Commit: {commit}", - f"Elapsed: {elapsed_str}", - f"Validation: {validation_str}", - f"Result: {'SUCCESS' if all_success else 'FAILED'}", - "", - ] - - if validation_failures: - lines.append("⚠️ VALIDATION FAILED - State trie validation errors detected!") - lines.append("") - - for inst in instances: - icon = "✅" if inst.status == "success" else "❌" - line = f" {icon} {inst.name}: {inst.status}" - if inst.sync_time: - line += f" (sync: {fmt_time(inst.sync_time)})" - if inst.validation_status: - line += f" [validation: {inst.validation_status}]" - if inst.initial_block: - line += f" post-sync block: {inst.initial_block}" - if inst.initial_block and inst.last_block > inst.initial_block: - blocks_processed = inst.last_block - inst.initial_block - line += f" (processed +{blocks_processed} blocks in {BLOCK_PROCESSING_DURATION//60}m)" - if inst.error: - line += f"\n Error: {inst.error}" - lines.append(line) - - # Add phase breakdown - phases = parse_phase_timings(run_id, inst.container) - if phases: - lines.append(f" Phase Breakdown:") - max_name_len = max(len(name) for name, _, _ in phases) - for name, count, duration in phases: - lines.append(f" {name:<{max_name_len}} {duration} ({count})") - - lines.append("") - # Append to log file - with open(RUN_LOG_FILE, "a") as f: - f.write("\n".join(lines) + "\n") - print(f"📝 Run logged to {RUN_LOG_FILE}") - # Also write summary to the run folder - summary_file = LOGS_DIR / f"run_{run_id}" / "summary.txt" - summary_file.parent.mkdir(parents=True, exist_ok=True) - summary_file.write_text("\n".join(lines)) - - -def generate_run_id() -> str: - """Generate a unique run ID based on timestamp.""" - return datetime.now().strftime("%Y%m%d_%H%M%S") - - -def get_next_run_count() -> int: - """Get the next run count by parsing the run history log. - - This provides persistence across restarts - if run #3 failed and we restart, - the next run will be #4 instead of starting from #1 again. - """ - if not RUN_LOG_FILE.exists(): - return 1 - - try: - content = RUN_LOG_FILE.read_text() - # Find all "Run #N" patterns and get the highest number - matches = re.findall(r'Run #(\d+)', content) - if matches: - max_run = max(int(m) for m in matches) - return max_run + 1 - return 1 - except Exception: - return 1 - - -def restart_containers(compose_file: str, compose_dir: str, networks: list[str] = None, image_tag: str = ""): - """Stop and restart docker compose containers, clearing volumes. - - Args: - compose_file: Docker compose file name - compose_dir: Directory containing docker compose file - networks: Optional list of network names (for selective restart) - image_tag: Optional image tag override (sets ETHREX_IMAGE env var) - """ - print("\n🔄 Restarting containers...\n", flush=True) - try: - subprocess.run(["docker", "compose", "-f", compose_file, "down", "-v"], cwd=compose_dir, check=True) - time.sleep(5) - - env = os.environ.copy() - if image_tag: - env["ETHREX_IMAGE"] = image_tag - env["ETHREX_PULL_POLICY"] = "never" - - # Build service list if networks specified - cmd = ["docker", "compose", "-f", compose_file, "up", "-d"] - if networks: - for n in networks: - cmd.extend([f"setup-jwt-{n}", f"ethrex-{n}", f"consensus-{n}"]) - - subprocess.run(cmd, cwd=compose_dir, check=True, env=env) - print("✅ Containers restarted successfully\n", flush=True) - return True - except subprocess.CalledProcessError as e: - print(f"❌ Failed to restart containers: {e}\n", flush=True) - return False - - -def reset_instance(inst: Instance): - """Reset instance state for a new sync cycle.""" - inst.status = "waiting" - inst.start_time = 0 - inst.sync_time = 0 - inst.last_block = 0 - inst.last_block_time = 0 - inst.block_check_start = 0 - inst.initial_block = 0 - inst.error = "" - inst.first_failure_time = 0 - inst.validation_status = "" - - -def print_status(instances: list[Instance]): - print("\033[2J\033[H", end="") - print(f"{'='*60}\nStatus at {time.strftime('%H:%M:%S')}\n{'='*60}") - - for i in instances: - elapsed = time.time() - i.start_time if i.start_time else 0 - # Build status-specific extra info - if i.status == "syncing": - validation_info = f" [{i.validation_status}]" if i.validation_status else "" - extra = f" ({fmt_time(elapsed)} elapsed){validation_info}" - elif i.status == "waiting": - extra = " (waiting for node...)" - elif i.status == "synced": - extra = f" (synced in {fmt_time(i.sync_time)})" - elif i.status == "block_processing": - extra = f" (block {i.last_block}, +{i.last_block - i.initial_block} blocks, {fmt_time(BLOCK_PROCESSING_DURATION - (time.time() - i.block_check_start))} left)" - elif i.status == "success": - extra = f" ✓ synced in {fmt_time(i.sync_time)}, processed +{i.last_block - i.initial_block} blocks" - elif i.status == "failed": - extra = f" - {i.error}" - else: - extra = "" - print(f" {STATUS_EMOJI.get(i.status, '?')} {i.name} (:{i.port}): {i.status}{extra}") - - print(flush=True) - - -def update_instance(inst: Instance, timeout_min: int) -> bool: - if inst.status in ("success", "failed"): - return False - - now = time.time() - block = rpc_call(inst.rpc_url, "eth_blockNumber") - block = int(block, 16) if block else None - - if block is None: - if inst.status != "waiting": - if inst.first_failure_time == 0: - inst.first_failure_time = now - elif (now - inst.first_failure_time) >= NODE_UNRESPONSIVE_TIMEOUT: - first_fail_str = datetime.fromtimestamp(inst.first_failure_time).strftime("%H:%M:%S") - - # Check if container exited (possibly due to validation failure) - is_running, exit_code = container_exit_info(inst.container) - if is_running is False and exit_code is not None: - # Container exited - check for validation failure - validation_error = check_validation_failure(inst.container) - if validation_error: - inst.status, inst.error = "failed", validation_error - else: - inst.status, inst.error = "failed", f"Container exited with code {exit_code} (first failure at {first_fail_str})" - else: - inst.status, inst.error = "failed", f"Node stopped responding (first failure at {first_fail_str}, down for {fmt_time(now - inst.first_failure_time)})" - return True - return False - - inst.first_failure_time = 0 - - if inst.status == "waiting": - inst.status, inst.start_time = "syncing", inst.start_time or now - return True - - if inst.status == "syncing": - if (now - inst.start_time) > timeout_min * 60: - inst.status, inst.error = "failed", f"Sync timeout after {fmt_time(timeout_min * 60)}" - return True - # Check for validation progress (shows when validation is running) - validation_progress = check_validation_progress(inst.container) - if validation_progress and validation_progress != inst.validation_status: - inst.validation_status = validation_progress - return True # Status changed, refresh display - if rpc_call(inst.rpc_url, "eth_syncing") is False: - inst.status, inst.sync_time = "synced", now - inst.start_time - inst.block_check_start, inst.last_block = now, block - inst.initial_block, inst.last_block_time = block, now - return True - - if inst.status == "synced": - inst.status = "block_processing" - inst.block_check_start, inst.last_block, inst.initial_block, inst.last_block_time = now, block, block, now - return True - - if inst.status == "block_processing": - # Check for stalled node (no new blocks for too long) - if (now - inst.last_block_time) > BLOCK_STALL_TIMEOUT: - inst.status, inst.error = "failed", f"Block processing stalled at {inst.last_block} for {fmt_time(BLOCK_STALL_TIMEOUT)}" - return True - # Update last block time if we see progress - if block and block > inst.last_block: - inst.last_block, inst.last_block_time = block, now - # Success after duration, but only if we made progress - if (now - inst.block_check_start) > BLOCK_PROCESSING_DURATION: - if inst.last_block > inst.initial_block: - inst.status = "success" - # If validation was running, mark it as complete - if inst.validation_status: - inst.validation_status = "complete" - return True - else: - inst.status, inst.error = "failed", "No block progress during monitoring" - return True - - return False - - -def main(): - p = argparse.ArgumentParser(description="Monitor Docker snapsync instances") - p.add_argument("--networks", default="hoodi,sepolia,mainnet") - p.add_argument("--timeout", type=int, default=SYNC_TIMEOUT) - p.add_argument("--no-slack", action="store_true") - p.add_argument("--exit-on-success", action="store_true") - p.add_argument("--compose-file", default="docker-compose.multisync.yaml", help="Docker compose file name") - p.add_argument("--compose-dir", default=".", help="Directory containing docker compose file") - # Auto-update and build options - p.add_argument("--auto-update", action="store_true", - help="Pull latest code and rebuild Docker image before each run") - p.add_argument("--branch", default=os.environ.get("MULTISYNC_BRANCH", ""), - help="Git branch to track (default: from MULTISYNC_BRANCH env or current branch)") - p.add_argument("--build-profile", default=os.environ.get("MULTISYNC_BUILD_PROFILE", "release-with-debug-assertions"), - help="Cargo build profile for Docker image") - p.add_argument("--image-tag", default=os.environ.get("MULTISYNC_LOCAL_IMAGE", "ethrex-local:multisync"), - help="Docker image tag to build") - p.add_argument("--ethrex-dir", default=os.environ.get("ETHREX_DIR", "../.."), - help="Path to ethrex repository root") - args = p.parse_args() - - # Resolve ethrex directory to absolute path - ethrex_dir = os.path.abspath(args.ethrex_dir) - - names = [n.strip() for n in args.networks.split(",")] - ports = [] - for n in names: - if n not in NETWORK_PORTS: - sys.exit(f"Error: unknown network '{n}', known networks: {list(NETWORK_PORTS.keys())}") - ports.append(NETWORK_PORTS[n]) - containers = [f"ethrex-{n}" for n in names] - - instances = [Instance(n, p, c) for n, p, c in zip(names, ports, containers)] - - # Detect state of already-running containers - for inst in instances: - if t := container_start_time(inst.container): - inst.start_time = t - # Check if already synced - syncing = rpc_call(inst.rpc_url, "eth_syncing") - if syncing is False: - # Already synced - go straight to block_processing - block = rpc_call(inst.rpc_url, "eth_blockNumber") - block = int(block, 16) if block else 0 - inst.status = "block_processing" - inst.sync_time = time.time() - t - inst.block_check_start = time.time() - inst.initial_block = block - inst.last_block = block - inst.last_block_time = time.time() - elif syncing is not None: - # Still syncing - inst.status = "syncing" - # else: node not responding yet, stay in "waiting" - - hostname = socket.gethostname() - branch = args.branch if args.branch else git_branch() - commit = git_commit() - - # Ensure logs directory exists first (needed for run count) - ensure_logs_dir() - - # Get run count from existing logs (persists across restarts) - run_count = get_next_run_count() - run_id = generate_run_id() - - print(f"📁 Logs will be saved to {LOGS_DIR.absolute()}") - print(f"📝 Run history: {RUN_LOG_FILE.absolute()}") - if args.auto_update: - print(f"🔄 Auto-update enabled: tracking branch '{branch}'") - print(f" Build profile: {args.build_profile}") - print(f" Image tag: {args.image_tag}") - print() - - try: - while True: - # Auto-update: pull latest and rebuild before each run - if args.auto_update: - print(f"\n{'='*60}") - print(f"🔄 Auto-update: Preparing run #{run_count}") - print(f"{'='*60}") - success, new_commit = git_pull_latest(branch, ethrex_dir) - if not success: - print("❌ Failed to pull latest code, aborting") - sys.exit(1) - commit = new_commit # Update commit for logging - if not build_docker_image(args.build_profile, args.image_tag, ethrex_dir): - print("❌ Failed to build Docker image, aborting") - sys.exit(1) - - # Start/restart containers with the new image - if not restart_containers(args.compose_file, args.compose_dir, names, args.image_tag): - print("❌ Failed to start containers", file=sys.stderr) - sys.exit(1) - # Reset instances since we restarted - for inst in instances: - reset_instance(inst) - time.sleep(30) # Wait for containers to start - print(f"{'='*60}\n") - - print(f"🔍 Run #{run_count} (ID: {run_id}): Monitoring {len(instances)} instances (timeout: {args.timeout}m)", flush=True) - last_print = 0 - while True: - changed = any(update_instance(i, args.timeout) for i in instances) - if changed or (time.time() - last_print) > STATUS_PRINT_INTERVAL: - print_status(instances) - last_print = time.time() - if all(i.status in ("success", "failed") for i in instances): - print_status(instances) - break - time.sleep(CHECK_INTERVAL) - # Log the run result and save container logs BEFORE any restart - save_all_logs(instances, run_id, args.compose_file) - log_run_result(run_id, run_count, instances, hostname, branch, commit, args.build_profile) - # Send a single Slack summary notification for the run - if not args.no_slack: - slack_notify(run_id, run_count, instances, hostname, branch, commit, args.build_profile) - # Check results - if all(i.status == "success" for i in instances): - print(f"🎉 Run #{run_count}: All instances synced successfully!") - if args.exit_on_success: - sys.exit(0) - # Prepare for another run - run_count += 1 - run_id = generate_run_id() # New run ID for the new cycle - - # If auto-update is enabled, the loop will pull/build/restart - # Otherwise, just restart containers now - if not args.auto_update: - image_tag = args.image_tag if args.image_tag != "ethrex-local:multisync" else "" - if restart_containers(args.compose_file, args.compose_dir, names, image_tag): - for inst in instances: - reset_instance(inst) - time.sleep(30) # Wait for containers to start - else: - print("❌ Failed to restart containers", file=sys.stderr) - sys.exit(1) - else: - # Reset instances - containers will be restarted after pull/build - for inst in instances: - reset_instance(inst) - else: - # On failure: containers are NOT stopped, you can inspect the DB - print("\n" + "="*60) - print("⚠️ FAILURE - Containers are still running for inspection") - print("="*60) - print("\nYou can:") - print(" - Inspect the database in the running containers") - print(" - Check logs: docker logs ") - print(f" - View saved logs: {LOGS_DIR}/run_{run_id}/") - print(f" - View run history: {RUN_LOG_FILE}") - print("\nTo restart manually: make multisync-restart") - sys.exit(1) - except KeyboardInterrupt: - print("\n⚠️ Interrupted") - print_status(instances) - sys.exit(130) - - -if __name__ == "__main__": - main() diff --git a/tooling/sync/server_runner.py b/tooling/sync/server_runner.py deleted file mode 100644 index 466cc761402..00000000000 --- a/tooling/sync/server_runner.py +++ /dev/null @@ -1,298 +0,0 @@ -import subprocess -import sys -import argparse -import requests -import time -import os -import json -import socket - - -RPC_URL = "http://localhost:8545" -CHECK_INTERVAL = 5 # seconds - -def format_elapsed_time(seconds): - if not isinstance(seconds, (int, float)): - raise ValueError("Input must be a number") - - seconds = abs(seconds) - - hours = int(seconds // 3600) - remaining = seconds % 3600 - minutes = int(remaining // 60) - secs = int(remaining % 60) - - parts = [] - if hours > 0: - parts.append(f"{hours}h") - if minutes > 0: - parts.append(f"{minutes}m") - if secs > 0 or (hours == 0 and minutes == 0): # Include seconds if time is 0 - parts.append(f"{secs}s") - - return " ".join(parts) - -def get_git_commit(): - try: - cmd = ["git", "rev-parse", "--short", "HEAD"] - return subprocess.check_output(cmd).decode().strip() - except subprocess.CalledProcessError: - return None - -def parse_args(): - parser = argparse.ArgumentParser( - description="Run a Makefile with optional variables." - ) - parser.add_argument("--full-sync", action="store_true", help="Whether full-sync is activated") - parser.add_argument( - "--healing", action="store_true", help="Whether healing is activated" - ) - parser.add_argument( - "--memory", action="store_true", help="Whether memory is activated" - ) - parser.add_argument( - "--network", type=str, default="hoodi", help="Network variable (default: hoodi)" - ) - parser.add_argument( - "--branch", - type=str, - default="main", - help="Branch variable (default: main)", - ) - parser.add_argument( - "--logs_file", - type=str, - default="output", - help="Logs file name (default: output)", - ) - parser.add_argument( - "--timeout", type=int, default=60, help="Timeout in minutes (default: 60)" - ) - parser.add_argument( - "--no-monitor", - action="store_true", - help="Whether we should restart after success/failure", - ) - parser.add_argument( - "--block_wait_time", - type=int, - default=120, - help="Time to wait until new block in seconds (default: 60)", - ) - parser.add_argument( - "--debug-assert", action="store_true", help="Whether it should be compiled with debug assertions" - ) - - return parser.parse_args() - - -def send_slack_message_failed(header: str, hostname: str, maybe_timeout_in_minutes, log_file: str, branch: str): - try: - commit = get_git_commit() - webhook_url = os.environ["SLACK_WEBHOOK_URL_FAILED"] - - maybe_timeout = "" if maybe_timeout_in_minutes == None else f"\n*Timeout:* {format_elapsed_time(maybe_timeout_in_minutes * 60)}" - - message = { - "blocks": [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": f"{header}" - } - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": f"*Server:* `{hostname}`{maybe_timeout}\n*Logs in:* `{log_file}`\n*Branch:* `{branch}`\n*Commit:* `{commit if commit else 'N/A'}`" - } - } - ] - } - response = requests.post( - webhook_url, - data=json.dumps(message), - headers={"Content-Type": "application/json"}, - ) - - if response.status_code != 200: - print(f"Error sending Slack message") - - except Exception as e: - print(f"Error sending Slack message: {e}", file=sys.stderr) - return - - -def send_slack_message_success(hostname: str, elapsed, network: str, log_file: str, branch: str, debug_assert: bool): - try: - commit = get_git_commit() - webhook_url = os.environ["SLACK_WEBHOOK_URL_SUCCESS"] - - message = { - "blocks": [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": f":white_check_mark: Node snap-synced {capitalize_network(network)} and advanced for 30 minutes" - } - }, - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": f'*Server:* `{hostname}`\n*Synced in:* {format_elapsed_time(elapsed)}\n*Logs in:* `{log_file}`\n*Branch:* `{branch}`\n*Commit:* `{commit if commit else "N/A"}`\n*Validation:* `{"true" if debug_assert else "false"}`' - } - } - ] - } - - response = requests.post( - webhook_url, - data=json.dumps(message), - headers={"Content-Type": "application/json"}, - ) - - if response.status_code != 200: - print(f"Error sending Slack message") - - except Exception as e: - print(f"Error sending Slack message: {e}", file=sys.stderr) - return - - -def capitalize_network(word): - if not word: - return word - return word[0].upper() + word[1:] - - -def get_variables(args): - variables = {} - - # Only include FULL_SYNC if flag is set - if args.full_sync: - variables["FULL_SYNC"] = "1" - if args.healing: - variables["HEALING"] = "1" - if args.memory: - variables["MEMORY"] = "1" - if args.debug_assert: - variables["DEBUG_ASSERT"] = "1" - variables["SERVER_SYNC_NETWORK"] = args.network - variables["SERVER_SYNC_BRANCH"] = args.branch - - return variables - - -def block_production_loop( - hostname, args, logs_file, elapsed, start_time, block_production_payload, debug_assert -): - current_block_number = 0 - block_start_time = time.time() - while True: - block_elapsed = time.time() - block_start_time - if block_elapsed > 30 * 60: # 30 minutes - print("✅ Node is fully synced!") - send_slack_message_success(hostname, elapsed, args.network, f"{logs_file}_{start_time}.log", args.branch, debug_assert) - with open("sync_logs.txt", "a") as f: - f.write(f"LOGS_FILE={logs_file}_{start_time}.log SYNCED\n") - return True - try: - response = requests.post(RPC_URL, json=block_production_payload).json() - result = response.get("result") - if int(result, 0) > current_block_number: - current_block_number = int(result, 0) - else: - print(f"⚠️ Node did not generated a new block. Stopping.") - send_slack_message_failed(f"⚠️ Node stopped generating new blocks after snap syncing {capitalize_network(args.network)}", hostname, None, f"{logs_file}_{start_time}.log", args.branch) - with open("sync_logs.txt", "a") as f: - f.write(f"LOGS_FILE={logs_file}_{start_time}.log FAILED\n") - return False - except Exception as e: - print(f"⚠️ Node did stopped. Stopping.") - print("Error:", e) - send_slack_message_failed(f"⚠️ Node stopped running after snap syncing {capitalize_network(args.network)}", hostname, None, f"{logs_file}_{start_time}.log", args.branch) - with open("sync_logs.txt", "a") as f: - f.write(f"LOGS_FILE={logs_file}_{start_time}.log FAILED\n") - return False - time.sleep(args.block_wait_time) - - -def verification_loop( - logs_file, args, hostname, payload, block_production_payload, start_time, debug_assert -): - while True: - try: - elapsed = time.time() - start_time - if elapsed > args.timeout * 60: - print(f"⚠️ Node did not sync within {args.timeout} minutes. Stopping.") - send_slack_message_failed(f"⚠️ Node failed to sync {capitalize_network(args.network)} within timeout", hostname, args.timeout, f"{logs_file}_{start_time}.log", args.branch) - with open("sync_logs.txt", "a") as f: - f.write(f"LOGS_FILE={logs_file}_{start_time}.log FAILED\n") - return False - response = requests.post(RPC_URL, json=payload).json() - result = response.get("result") - if result is False: - success = block_production_loop( - hostname, - args, - logs_file, - elapsed, - start_time, - block_production_payload, - debug_assert, - ) - return success - time.sleep(CHECK_INTERVAL) - except Exception as e: - pass - - -def execution_loop( - command, logs_file, args, hostname, payload, block_production_payload, debug_assert -): - while True: - start_time = time.time() - subprocess.run( - command + [f"LOGS_FILE={logs_file}_{start_time}.log"], check=True - ) - if args.no_monitor: - print("No monitor flag set, exiting.") - break - success = verification_loop( - logs_file, args, hostname, payload, block_production_payload, start_time, debug_assert - ) - if not success: - break - -def main(): - args = parse_args() - hostname = socket.gethostname() - variables = get_variables(args) - - logs_file = args.logs_file - command = ["make", "server-sync"] - - for key, value in variables.items(): - command.append(f"{key}={value}") - - payload = {"jsonrpc": "2.0", "method": "eth_syncing", "params": [], "id": 1} - block_production_payload = { - "jsonrpc": "2.0", - "method": "eth_blockNumber", - "params": [], - "id": 1, - } - try: - execution_loop( - command, logs_file, args, hostname, payload, block_production_payload, args.debug_assert - ) - except subprocess.CalledProcessError as e: - print(f"An error occurred while running the make command: {e}", file=sys.stderr) - sys.exit(1) - -if __name__ == "__main__": - main()