diff --git a/.github/workflows/eest-r2-stateless-inputs.yml b/.github/workflows/eest-r2-stateless-inputs.yml new file mode 100644 index 00000000..4104c397 --- /dev/null +++ b/.github/workflows/eest-r2-stateless-inputs.yml @@ -0,0 +1,94 @@ +name: EEST R2 Stateless Inputs + +on: + workflow_dispatch: + inputs: + catalog_url: + description: Public R2 catalog root, index.html URL, or manifest.json URL + required: false + default: https://pub-5345007fbd06486bbb7cbbe9f3112c45.r2.dev/devnets/glamsterdam-devnet-5 + type: string + eest_ref: + description: ethereum/execution-specs ref to validate with + required: false + default: tests-zkevm@v0.4.1 + type: string + batch_count: + description: Number of latest complete batches to validate + required: false + default: "10" + type: string + schedule: + - cron: "23 */2 * * *" + +permissions: + contents: read + +concurrency: + group: eest-r2-stateless-inputs-${{ github.workflow }} + cancel-in-progress: true + +jobs: + validate: + name: Validate Latest R2 Batch + runs-on: ubuntu-latest + env: + CATALOG_URL: ${{ github.event.inputs.catalog_url || 'https://pub-5345007fbd06486bbb7cbbe9f3112c45.r2.dev/devnets/glamsterdam-devnet-5' }} + EEST_REF: ${{ github.event.inputs.eest_ref || 'tests-zkevm@v0.4.1' }} + BATCH_COUNT: ${{ github.event.inputs.batch_count || '10' }} + SUMMARY_DIR: workload/target/eest-r2-stateless-inputs + steps: + - name: Checkout workload + uses: actions/checkout@v4 + with: + path: workload + + - name: Checkout EEST + uses: actions/checkout@v4 + with: + repository: ethereum/execution-specs + ref: ${{ env.EEST_REF }} + path: execution-specs + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install uv + run: python -m pip install --upgrade pip uv + + - name: Resolve EEST commit + id: eest + run: echo "commit=$(git -C execution-specs rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + - name: Validate R2 stateless inputs with EEST + run: | + mkdir -p "$SUMMARY_DIR" + uv run --project execution-specs --with zstandard \ + python workload/scripts/validate-r2-stateless-inputs-with-eest.py \ + --catalog-url "$CATALOG_URL" \ + --batch-count "$BATCH_COUNT" \ + --summary-json "$SUMMARY_DIR/summary.json" \ + --summary-md "$SUMMARY_DIR/summary.md" \ + --eest-ref "$EEST_REF" \ + --eest-commit "${{ steps.eest.outputs.commit }}" + + - name: Append summary + if: always() + run: | + if [ -f "$SUMMARY_DIR/summary.md" ]; then + cat "$SUMMARY_DIR/summary.md" >> "$GITHUB_STEP_SUMMARY" + else + echo "No validation summary was produced." >> "$GITHUB_STEP_SUMMARY" + fi + + - name: Upload summary + if: always() + uses: actions/upload-artifact@v4 + with: + name: eest-r2-stateless-inputs-summary + path: | + ${{ env.SUMMARY_DIR }}/summary.json + ${{ env.SUMMARY_DIR }}/summary.md + if-no-files-found: warn diff --git a/.gitignore b/.gitignore index 9c9fb58d..5ab5c617 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ zisk-profiles # private envrc file .envrc + +/.nethermind-src/ +/nethermind-guest/ +/nethermind-guest-perf/ diff --git a/Cargo.lock b/Cargo.lock index a76b6a47..8956f68d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,7 +14,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common", + "crypto-common 0.1.7", "generic-array", ] @@ -26,7 +26,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -73,27 +73,27 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e0378e959aa6a885897522080a990e80eb317f1e9a222a604492ea50e13096" +checksum = "3b5cc30538e90795a57647bef8d8864aad6e8d86190617009b4ef8d8b647b49a" dependencies = [ "alloy-primitives", "alloy-rlp", "num_enum", + "phf 0.14.0", "serde", - "strum 0.27.2", ] [[package]] name = "alloy-consensus" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8c24c95e90c1608c2d91cff1b451d796474168d3310ccc8b7cd12502ca8169" +checksum = "83447eeb17816e172f1dfc0db1f9dc0b7c5d069bd1f7cecbecceb382bf931015" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "alloy-trie", "alloy-tx-macros", "arbitrary", @@ -104,7 +104,7 @@ dependencies = [ "either", "k256", "once_cell", - "rand 0.8.6", + "rand 0.8.7", "secp256k1 0.30.0", "serde", "serde_json", @@ -114,24 +114,24 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d211ad0ef468a70a7a829e49683ff59ad25f02b4ab3764344c4c2663329a52c" +checksum = "5406343e306856dc2be762700e98a16904de45dee14a07f233e742ce68daff2f" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "arbitrary", "serde", ] [[package]] name = "alloy-dyn-abi" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2db5c583aaef0255aa63a4fe827f826090142528bba48d1bf4119b62780cad" +checksum = "a475bb02d9cef2dbb99065c1664ab3fe1f9352e21d6d5ed3f02cdbfc06ed1abc" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -141,7 +141,7 @@ dependencies = [ "itoa", "serde", "serde_json", - "winnow 0.7.15", + "winnow 1.0.3", ] [[package]] @@ -154,7 +154,7 @@ dependencies = [ "alloy-rlp", "arbitrary", "crc", - "rand 0.8.6", + "rand 0.8.7", "serde", "thiserror 2.0.18", ] @@ -169,7 +169,7 @@ dependencies = [ "alloy-rlp", "arbitrary", "borsh", - "rand 0.8.6", + "rand 0.8.7", "serde", ] @@ -184,7 +184,7 @@ dependencies = [ "arbitrary", "borsh", "k256", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_with", "thiserror 2.0.18", @@ -192,9 +192,9 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "407510740da514b694fecb44d8b3cebdc60d448f70cc5d24743e8ba273448a6e" +checksum = "6b827a6d7784fe3eb3489d40699407a4cdcce74271421a01bdffe60cf573bb16" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -202,6 +202,7 @@ dependencies = [ "borsh", "once_cell", "serde", + "thiserror 2.0.18", ] [[package]] @@ -229,9 +230,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae69eaa5096b47ffe97e6a5d6bde7e7fa2dec106af22a9315621d11039c3de3c" +checksum = "0dca4c89ace90684b4b77366d00631ed498c9af962079af2a5dbc593a0618a77" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -239,7 +240,7 @@ dependencies = [ "alloy-eip7928", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "arbitrary", "auto_impl", "borsh", @@ -253,12 +254,12 @@ dependencies = [ [[package]] name = "alloy-evm" -version = "0.33.2" +version = "0.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc4b83cb672156663e6094d098beb509965b7fe684bb3d6e44bb9ca2e9ae714" +checksum = "7f092eb6af80456e4ab41c9722e777d791335bc9c00b11bc3db7bf29a432dfd0" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-hardforks", "alloy-primitives", "alloy-rpc-types-engine", @@ -273,13 +274,13 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39789db0b3f3bbef0e6549c87bc6842b73886ebabee1405b6941685b1cc34083" +checksum = "ab0e0fe9e6d1120ad7bb9254c3fc2b9bc80a8df42a033fb626be6559c13d5153" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "alloy-trie", "borsh", "serde", @@ -302,9 +303,9 @@ dependencies = [ [[package]] name = "alloy-json-abi" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" +checksum = "7c36c9d7f9021601b04bfef14a4b64849f6d73116a4e91e071d7fbfe10247901" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -314,9 +315,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662b525af73e86b2167dae923261c8edf440ba7e1426b30a8b993177bc214c02" +checksum = "c4691c60de5d628533752cd07e102d17c47874c06c04c91af33960fd94c484f4" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -329,19 +330,19 @@ dependencies = [ [[package]] name = "alloy-network" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c657c2d9751d3c7d94990554b231e5372c3c2e4bad842806280b6151a0d6a05d" +checksum = "a7db7b095b0b1db1d18ce7e91dcd2e82007f2d52bfb8125e6b64633a74a06bc3" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-json-rpc", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-any", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "alloy-signer", "alloy-sol-types", "async-trait", @@ -355,22 +356,22 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e7c4bb0ebbd6d7406d2808968f43c0d5186c69c5e58cedcbee7380f4cd1fcf" +checksum = "cd28d9bfd11729037d194f2b1d43db8642eb3f342032691f4ca96bb745479c3c" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "serde", ] [[package]] name = "alloy-primitives" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" +checksum = "4885c1409b6936c4898e646ef58baf6ec54edaf6d8179f79df805a7b85b7cf3e" dependencies = [ "alloy-rlp", "arbitrary", @@ -379,28 +380,29 @@ dependencies = [ "const-hex", "derive_more 2.1.1", "foldhash 0.2.0", - "getrandom 0.4.2", - "hashbrown 0.16.1", + "getrandom 0.4.3", + "hashbrown 0.17.1", "indexmap 2.14.0", "itoa", "k256", "keccak-asm", "paste", "proptest", - "proptest-derive", - "rand 0.9.4", + "proptest-derive 0.8.0", + "rand 0.9.5", "rapidhash", "ruint", "rustc-hash", + "secp256k1 0.31.1", "serde", - "sha3", + "sha3 0.11.0", ] [[package]] name = "alloy-rlp" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" +checksum = "24671b1f62edcf0f9b62994c7bf72cd621a04a4b99f5020ece1a647b40e2f103" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -409,20 +411,20 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36834a5c0a2fa56e171bf256c34d70fca07d0c0031583edea1c4946b7889c9e" +checksum = "9d4311c03125e8a18296504560b9de3d75ecbd0dcda7f71e6cf2a196d57e6fba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "alloy-rpc-client" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5ee7b51752c68fb95f21705e402700750e692b1d21ccc294ac48fadc8655d53" +checksum = "755447dc13a04c6fa6db8dedb5d32ab5edfa4dd7aa34487ff192a3d873e0e8cf" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -430,7 +432,7 @@ dependencies = [ "alloy-transport-http", "futures", "pin-project", - "reqwest 0.13.2", + "reqwest 0.13.4", "serde", "serde_json", "tokio", @@ -443,22 +445,22 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fa76988f54105ad4398828e8aaf1a39b3f07f91fb79091529056689514ee8c2" +checksum = "052c031d1f7c5611997056bbcb8814e5cbf20f7efeee8c3de690555172038cf2" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "serde", ] [[package]] name = "alloy-rpc-types-admin" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670b3a8e0d1b32e9886b7a419b8efe6754ea00b9fdd4c0ea3c7411b6c30431f4" +checksum = "ef669b370940e7945a3a384cc4024038cd69ee658b71270d59c20b78dd8d20d4" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -468,38 +470,38 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d276bea4e92e4991269d31b9abd3e722eed2565b82036478a4416adb8dd4992" +checksum = "2ff111a54268dc0bbd3b17f98571a7e27cc661dc081ad2999d91888647eb2e11" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "serde", ] [[package]] name = "alloy-rpc-types-any" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f1a9a3bda9be7f6515316eb792710532411878bbfc88934973f4b371376b00d" +checksum = "0a6561ed4759c974d9c144500a59e3fb8c1d87327a12900d5ce455c0cae6dcb6" dependencies = [ "alloy-consensus-any", "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "serde", "serde_json", ] [[package]] name = "alloy-rpc-types-beacon" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf5d68ddca890854fb78291cbde06115473ded00b2337d0f815e92c0c1f8003" +checksum = "9a62f6ce2d95f59ed310bd90d5fd1566a29d1ec45cc219abbc5dcc807d31f136" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rpc-types-engine", "derive_more 2.1.1", @@ -511,9 +513,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea21739e232c221779741eba7e7b9bc19ad8ff777b72736647ae519f5c9f6f33" +checksum = "b9e9c1f9ac81c56b2d96901201db7eb95c4e9fc3c21237a4690f8c652aa62089" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -524,35 +526,35 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f05338cfb4ee5508ff76f01c88142cab8a4579db74b7d9432936c26e4f11374" +checksum = "7eba59e1c069f168a01982f42a57797736923b76aa854194df4930be17867e1c" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "derive_more 2.1.1", - "jsonwebtoken 10.3.0", - "rand 0.8.6", + "jsonwebtoken", + "rand 0.8.7", "serde", "strum 0.27.2", ] [[package]] name = "alloy-rpc-types-eth" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda4ece0050154ab278241aeffade58916b04f38254832e8cb6e4671c6e72ed2" +checksum = "175a2a5b6017d7f61b5e4b800d21215fe8e94fe729d00828e13bb6d93dcf3492" dependencies = [ "alloy-consensus", "alloy-consensus-any", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "alloy-sol-types", "arbitrary", "itertools 0.14.0", @@ -564,28 +566,28 @@ dependencies = [ [[package]] name = "alloy-rpc-types-mev" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0df223478aec91d8fb0f8643234764042fa432e6111aca126774802b2618a3a5" +checksum = "ed1004c1d68bfaee001712f83356f88031ab74a727b8560fb7fc738d1281ebe5" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "serde", "serde_json", ] [[package]] name = "alloy-rpc-types-trace" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5905ac3663b0859d67b82d912acce20887d20682a0cadde79c8a763b133a515" +checksum = "514b4b1ce3354f65067b4fc7eb75358e0f2ec8be3340c96dea65d6894f9ca435" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "serde", "serde_json", "thiserror 2.0.18", @@ -593,13 +595,13 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fbf71892d4df9cae8d35dc96f15d522384bb93806205465e2c8c012b7f0a34" +checksum = "76e34a42ebb4a71ab0bfdebc6d2f3c7bf809f01edf154d08fed159d10d1ef1d4" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "serde", ] @@ -616,9 +618,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beaa5c581a67e2743d95b4849eb9cfeb90866429cdaa6d8f6b75eb988b2d0cd9" +checksum = "c1e97b3e0b9f816b25083045dcfa69431bd059a078e828e4d82d296d1949b96c" dependencies = [ "alloy-primitives", "arbitrary", @@ -628,9 +630,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5da9ae50f9b48d7b4e2e5cde87175257be7e5e56909a7794720597c1d9806f6" +checksum = "6913d06ccc0d9c6ab67e2f82e2fbe292706da1f91442f30cded2d04b56bf0d58" dependencies = [ "alloy-primitives", "async-trait", @@ -643,9 +645,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "2.0.1" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b794002d57fd2f71b4c87298a41ca24dfc0f2cf6630d95106a477e451747ba" +checksum = "e48366d2c42b8d95ef951101bafa28486590f21b7a1e68b6b2d069746557bbe3" dependencies = [ "alloy-consensus", "alloy-network", @@ -653,29 +655,29 @@ dependencies = [ "alloy-signer", "async-trait", "k256", - "rand 0.8.6", + "rand 0.8.7", "thiserror 2.0.18", ] [[package]] name = "alloy-sol-macro" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" +checksum = "840128ed2b2971d6d4668a553fe403a82683d3acc646c73e75887e7157408033" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" +checksum = "63ec265e5d65d725175f6ca7711c970824c90ef9c0d1f1973711d4150ee612dd" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -684,16 +686,16 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "sha3", - "syn 2.0.117", + "sha3 0.11.0", + "syn 2.0.118", "syn-solidity", ] [[package]] name = "alloy-sol-macro-input" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" +checksum = "89bf01077f18650876cfa682eb1f949967b5cde03f1a51c955c469d2c9b4aa67" dependencies = [ "const-hex", "dunce", @@ -701,25 +703,25 @@ dependencies = [ "macro-string", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" +checksum = "857b470ecdd2ed38beaf82ad1a38c516a8ff75266750f38b9eeed001d575241b" dependencies = [ "serde", - "winnow 0.7.15", + "winnow 1.0.3", ] [[package]] name = "alloy-sol-types" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" +checksum = "384cf252de0db2dec52821eac037a7f57e2aa33fe5b900ce6fe39973402341f1" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -729,9 +731,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19dec9bfb59647254afdecbb5ddcddd7ba02edcd48ffa40510bddfbed0be1634" +checksum = "999adfe5c91035c6bf4c4210e0eb8d0caed79d76fbf5e1b70d78721f6097ac04" dependencies = [ "alloy-json-rpc", "auto_impl", @@ -752,14 +754,14 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2035f3c4d6bee20624da2dcf765d469b292398e48d766ffade61b0fcf8b4d45d" +checksum = "0e79a2c1793afc61eed9ca0963da370a988b27d2bbfa67c78013e262d47424c4" dependencies = [ "alloy-json-rpc", "alloy-transport", "itertools 0.14.0", - "reqwest 0.13.2", + "reqwest 0.13.4", "serde_json", "tower", "tracing", @@ -779,7 +781,7 @@ dependencies = [ "derive_more 2.1.1", "nybbles", "proptest", - "proptest-derive", + "proptest-derive 0.7.0", "serde", "smallvec", "thiserror 2.0.18", @@ -788,14 +790,14 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3520337f3d3d063a7fe20f47aaa62d695e3dc0372b34f601560dee24e76988b9" +checksum = "406bc1183f6843e0aba09f7b3365e828b597213d60793ba5cb41befc863e3a78" dependencies = [ "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -859,9 +861,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "aquamarine" @@ -874,7 +876,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -995,6 +997,23 @@ dependencies = [ "zeroize", ] +[[package]] +name = "ark-ff" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7a806ac6c8307b929df4645776290a50ee2aac754ad09d8bdf73391309e43af" +dependencies = [ + "ark-ff-asm 0.6.0", + "ark-ff-macros 0.6.0", + "ark-serialize 0.6.0", + "ark-std 0.6.0", + "digest 0.10.7", + "educe", + "num-bigint", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ff-asm" version = "0.3.0" @@ -1022,7 +1041,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", +] + +[[package]] +name = "ark-ff-asm" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1479009684adc073dff49a1025d3a7065b317a9ead25aaaca38cdc70058ba8a2" +dependencies = [ + "quote", + "syn 2.0.118", ] [[package]] @@ -1060,7 +1089,20 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", +] + +[[package]] +name = "ark-ff-macros" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0691ed21ef00ef89c1e9bda832eba493dda3ec2f8d892fb25b705f73f06bb8" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.118", ] [[package]] @@ -1134,13 +1176,26 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ - "ark-serialize-derive", + "ark-serialize-derive 0.5.0", "ark-std 0.5.0", "arrayvec", "digest 0.10.7", "num-bigint", ] +[[package]] +name = "ark-serialize" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74dd304fd536fb95d0a328e72be759209cc496a9da094c5bc56e5fea4f9e86b" +dependencies = [ + "ark-serialize-derive 0.6.0", + "ark-std 0.6.0", + "digest 0.10.7", + "num-bigint", + "serde_with", +] + [[package]] name = "ark-serialize-derive" version = "0.5.0" @@ -1149,7 +1204,18 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f153690697a2b91e5e1251ff98411ee5371500a111a0fd317a70e588eb300f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", ] [[package]] @@ -1159,7 +1225,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -1169,7 +1235,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -1179,7 +1245,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand 0.8.6", + "rand 0.8.7", +] + +[[package]] +name = "ark-std" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "367c9c827ed431bff6868b7aa926e05b16eb46603cc8b6e768e4a5553fa1d155" +dependencies = [ + "num-traits", + "rand 0.8.7", ] [[package]] @@ -1190,9 +1266,9 @@ checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" dependencies = [ "serde", ] @@ -1211,7 +1287,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1238,20 +1314,20 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.16.3" +version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" +checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" dependencies = [ "aws-lc-sys", "untrusted 0.7.1", @@ -1260,14 +1336,15 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.40.0" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" +checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" dependencies = [ "cc", "cmake", "dunce", "fs_extra", + "pkg-config", ] [[package]] @@ -1277,7 +1354,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core", - "base64", "bytes", "form_urlencoded", "futures-util", @@ -1296,10 +1372,8 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sha1", "sync_wrapper", "tokio", - "tokio-tungstenite", "tower", "tower-layer", "tower-service", @@ -1325,29 +1399,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum-extra" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9963ff19f40c6102c76756ef0a46004c0d58957d87259fc9208ff8441c12ab96" -dependencies = [ - "axum", - "axum-core", - "bytes", - "futures-util", - "headers", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "serde_core", - "tower-layer", - "tower-service", - "tracing", -] - [[package]] name = "base-x" version = "0.2.11" @@ -1370,6 +1421,12 @@ dependencies = [ "match-lookup", ] +[[package]] +name = "base45" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240e56f4d3c453c36faacb695c535a4d5f8c7d23dac175014f32eb0a71012a03" + [[package]] name = "base64" version = "0.22.1" @@ -1393,23 +1450,22 @@ dependencies = [ "ere-dockerized", "ere-util-tokio", "flate2", - "guest", "hex", - "integration-tests", "rayon", "reqwest 0.12.28", "serde", "serde_json", - "sha2", + "stateless-validator-common", "stateless-validator-ethrex", - "stateless-validator-reth", "strum 0.26.3", "tar", "tempfile", + "tokio", "toml 0.8.23", "tracing", "walkdir", "witness-generator", + "z6m_stateless_validator", "zkevm-metrics", ] @@ -1438,7 +1494,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.11.1", + "bitflags", "cexpr", "clang-sys", "itertools 0.13.0", @@ -1446,8 +1502,8 @@ dependencies = [ "quote", "regex", "rustc-hash", - "shlex", - "syn 2.0.117", + "shlex 1.3.0", + "syn 2.0.118", ] [[package]] @@ -1466,32 +1522,47 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] -name = "bitcoin-io" -version = "0.1.4" +name = "bitcoin-consensus-encoding" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" +checksum = "b2d6094e2a1ba3c93b5a596fe5a10d1a10c3c6e06785cde89f693a044c01aa40" +dependencies = [ + "bitcoin-internals", +] [[package]] -name = "bitcoin_hashes" -version = "0.14.1" +name = "bitcoin-internals" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" +checksum = "a30a22d1f112dde8e16be7b45c63645dc165cef254f835b3e1e9553e485cfa64" dependencies = [ - "bitcoin-io", - "hex-conservative", + "hex-conservative 0.3.2", ] [[package]] -name = "bitflags" -version = "1.3.2" +name = "bitcoin-io" +version = "0.1.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78" +dependencies = [ + "bitcoin-consensus-encoding", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.14.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" +dependencies = [ + "bitcoin-io", + "hex-conservative 0.2.2", +] [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" dependencies = [ "serde_core", ] @@ -1504,9 +1575,9 @@ checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" [[package]] name = "bitvec" -version = "1.0.1" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" dependencies = [ "funty", "radium", @@ -1525,34 +1596,21 @@ dependencies = [ ] [[package]] -name = "block-padding" -version = "0.3.3" +name = "block-buffer" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" dependencies = [ - "generic-array", + "hybrid-array", ] [[package]] -name = "block2" -version = "0.6.2" +name = "block-padding" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" -dependencies = [ - "objc2", -] - -[[package]] -name = "bls12_381" -version = "0.8.0" -source = "git+https://github.com/lambdaclass/bls12_381?branch=expose-affine-constructors#78cad0378b17fc3157b83f514be192bf46edf9a1" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" dependencies = [ - "digest 0.10.7", - "ff", - "group", - "pairing", - "rand_core 0.6.4", - "subtle", + "generic-array", ] [[package]] @@ -1569,9 +1627,9 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" +checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" dependencies = [ "borsh-derive", "bytes", @@ -1580,22 +1638,22 @@ dependencies = [ [[package]] name = "borsh-derive" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" +checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "boyer-moore-magiclen" -version = "0.2.22" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7441b4796eb8a7107d4cd99d829810be75f5573e1081c37faa0e8094169ea0d6" +checksum = "43f0fdabfbc8017645223fd529c6077df2c491277e44e88ed8afd5afe54e715a" dependencies = [ "debug-helper", ] @@ -1611,9 +1669,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "byte-slice-cast" @@ -1641,14 +1699,14 @@ checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "d6aedf8ae72766347502cf3cb4f41cf5e9cc37d28bee90f1fdaaae15f9cf9424" [[package]] name = "byteorder" @@ -1658,9 +1716,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" dependencies = [ "serde", ] @@ -1677,9 +1735,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "2.1.7" +version = "2.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6648ed1e4ea8e8a1a4a2c78e1cda29a3fd500bc622899c340d8525ea9a76b24a" +checksum = "38d04308254695569fdb9bfe3bacc1c91837a670d0806605eb82d63748fbd3a6" dependencies = [ "arbitrary", "blst", @@ -1693,9 +1751,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0" dependencies = [ "serde_core", ] @@ -1749,14 +1807,14 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.60" +version = "1.2.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex", + "shlex 2.0.1", ] [[package]] @@ -1786,11 +1844,22 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chrono" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -1806,7 +1875,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common", + "crypto-common 0.1.7", "inout", ] @@ -1852,7 +1921,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -1906,12 +1975,12 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.18.1" +version = "1.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" +checksum = "33e2a781ebdf4467d1428dc4593067825fb646f6871475098d8577421af73558" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "proptest", "serde_core", ] @@ -2002,6 +2071,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc" version = "3.4.0" @@ -2013,9 +2091,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" @@ -2047,18 +2125,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -2066,27 +2144,27 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crunchy" @@ -2118,23 +2196,21 @@ dependencies = [ ] [[package]] -name = "ctr" -version = "0.9.2" +name = "crypto-common" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ - "cipher", + "hybrid-array", ] [[package]] -name = "ctrlc" -version = "3.5.2" +name = "ctr" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" dependencies = [ - "dispatch2", - "nix", - "windows-sys 0.61.2", + "cipher", ] [[package]] @@ -2144,7 +2220,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "digest 0.10.7", "fiat-crypto", @@ -2161,7 +2237,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2195,7 +2271,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2209,7 +2285,7 @@ dependencies = [ "quote", "serde", "strsim", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2220,7 +2296,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2231,14 +2307,14 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "dashmap" -version = "6.1.0" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" dependencies = [ "arbitrary", "cfg-if", @@ -2252,15 +2328,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "data-encoding-macro" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" +checksum = "3259c913752a86488b501ed8680446a5ed2d5aeac6e596cb23ba3800768ea32c" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -2268,19 +2344,19 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" +checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "debug-helper" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e" +checksum = "80a4af69c60438a1a82af89d362f4729fd38db7b73f305a237636fad31ceb2bf" [[package]] name = "delay_map" @@ -2309,7 +2385,6 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] @@ -2332,7 +2407,7 @@ checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2343,7 +2418,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2364,7 +2439,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2374,7 +2449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2404,7 +2479,7 @@ dependencies = [ "convert_case 0.6.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "unicode-xid", ] @@ -2418,7 +2493,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.117", + "syn 2.0.118", "unicode-xid", ] @@ -2437,12 +2512,22 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", - "crypto-common", + "crypto-common 0.1.7", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", +] + [[package]] name = "dirs-next" version = "2.0.0" @@ -2487,7 +2572,7 @@ dependencies = [ "more-asserts", "multiaddr", "parking_lot", - "rand 0.8.6", + "rand 0.8.7", "smallvec", "socket2", "tokio", @@ -2496,33 +2581,21 @@ dependencies = [ "zeroize", ] -[[package]] -name = "dispatch2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" -dependencies = [ - "bitflags 2.11.1", - "block2", - "libc", - "objc2", -] - [[package]] name = "displaydoc" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "downloader" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere-guests?tag=v0.11.0#91735556bb1bdd6a16c3bcecee46ef14078846d0" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere-guests?tag=v0.13.0#cffc3f1a299e0e5f097305775d6ba1141b5f9546" dependencies = [ "anyhow", "reqwest 0.12.28", @@ -2592,7 +2665,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -2601,7 +2674,7 @@ version = "0.1.0" source = "git+https://github.com/paradigmxyz/stateless?rev=e5480e71a8031641ff471cad9dc482a7a14b32d5#e5480e71a8031641ff471cad9dc482a7a14b32d5" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-genesis", "alloy-primitives", "alloy-rlp", @@ -2633,9 +2706,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" dependencies = [ "serde", ] @@ -2682,10 +2755,10 @@ dependencies = [ "hex", "k256", "log", - "rand 0.8.6", + "rand 0.8.7", "secp256k1 0.30.0", "serde", - "sha3", + "sha3 0.10.9", "zeroize", ] @@ -2698,36 +2771,27 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "enum-ordinalize" -version = "4.3.2" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a1091a7bb1f8f2c4b28f1fe2cef4980ca2d410a3d727d67ecc3178c9b0800f0" +checksum = "07f808d588c10e464ea6f7d3eaed500049eff30aaac103460f61828c2d65b3eb" dependencies = [ "enum-ordinalize-derive", ] [[package]] name = "enum-ordinalize-derive" -version = "4.3.2" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" +checksum = "42e528e2d34ba8a67a1a650b86beae8ef69fc5fdb638016f386b973226590432" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", -] - -[[package]] -name = "envy" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" -dependencies = [ - "serde", + "syn 2.0.118", ] [[package]] @@ -2749,8 +2813,8 @@ dependencies = [ [[package]] name = "ere-catalog" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "ere-util-build", "serde", @@ -2759,8 +2823,8 @@ dependencies = [ [[package]] name = "ere-cluster-client-zisk" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "bincode 2.0.1", "ere-compiler-core", @@ -2774,25 +2838,26 @@ dependencies = [ "tokio", "tonic", "tonic-prost", + "tracing", ] [[package]] name = "ere-codec" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" [[package]] name = "ere-compiler-core" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "serde", ] [[package]] name = "ere-dockerized" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "anyhow", "ere-catalog", @@ -2822,13 +2887,13 @@ dependencies = [ [[package]] name = "ere-platform-core" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" [[package]] name = "ere-prover-core" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "anyhow", "auto_impl", @@ -2844,8 +2909,8 @@ dependencies = [ [[package]] name = "ere-server-api" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "prost", "serde", @@ -2854,8 +2919,8 @@ dependencies = [ [[package]] name = "ere-server-client" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "bincode 2.0.1", "ere-prover-core", @@ -2866,24 +2931,24 @@ dependencies = [ [[package]] name = "ere-util-build" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "cargo_metadata 0.19.2", ] [[package]] name = "ere-util-tokio" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "tokio", ] [[package]] name = "ere-verifier-core" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "auto_impl", "ere-codec", @@ -2892,8 +2957,8 @@ dependencies = [ [[package]] name = "ere-verifier-zisk" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere?tag=v0.11.0#e1d345c5e4e2b41d1334421310443306ea44f65f" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere?tag=v0.13.0#fcbf0dc8a2e1ea2af43cf0158a92dfbd33871fe0" dependencies = [ "bincode 2.0.1", "bytemuck", @@ -2943,32 +3008,10 @@ dependencies = [ "uint 0.10.0", ] -[[package]] -name = "ethrex-blockchain" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" -dependencies = [ - "bytes", - "crossbeam", - "ethrex-common", - "ethrex-crypto", - "ethrex-metrics", - "ethrex-rlp", - "ethrex-storage", - "ethrex-trie", - "ethrex-vm", - "rayon", - "rustc-hash", - "thiserror 2.0.18", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "ethrex-common" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" +version = "19.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v19.0.0#68b2f4f523a7c34675acfafac0d52c895e03db29" dependencies = [ "bytes", "crc32fast", @@ -2977,15 +3020,15 @@ dependencies = [ "ethrex-rlp", "ethrex-trie", "hex", - "hex-literal", + "hex-literal 0.4.1", "hex-simd", "indexmap 2.14.0", "lazy_static", "libc", - "libssz 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", - "libssz-derive 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", - "libssz-merkle 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", - "libssz-types 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", + "libssz", + "libssz-derive", + "libssz-merkle", + "libssz-types", "lru", "once_cell", "rkyv", @@ -2999,23 +3042,18 @@ dependencies = [ [[package]] name = "ethrex-crypto" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" +version = "19.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v19.0.0#68b2f4f523a7c34675acfafac0d52c895e03db29" dependencies = [ "ark-bn254", "ark-ec", "ark-ff 0.5.0", - "bls12_381", - "c-kzg", "ethereum-types", - "ff", - "hex-literal", + "hex-literal 0.4.1", "k256", - "malachite", "num-bigint", "p256", "ripemd", - "secp256k1 0.30.0", "sha2", "thiserror 2.0.18", "tiny-keccak", @@ -3023,8 +3061,8 @@ dependencies = [ [[package]] name = "ethrex-guest-program" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" +version = "19.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v19.0.0#68b2f4f523a7c34675acfafac0d52c895e03db29" dependencies = [ "bytes", "ethereum-types", @@ -3034,10 +3072,10 @@ dependencies = [ "ethrex-rlp", "ethrex-vm", "hex", - "libssz 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", - "libssz-derive 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", - "libssz-merkle 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", - "libssz-types 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", + "libssz", + "libssz-derive", + "libssz-merkle", + "libssz-types", "rkyv", "serde", "serde_with", @@ -3046,8 +3084,8 @@ dependencies = [ [[package]] name = "ethrex-l2-common" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" +version = "19.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v19.0.0#68b2f4f523a7c34675acfafac0d52c895e03db29" dependencies = [ "bytes", "ethereum-types", @@ -3064,8 +3102,8 @@ dependencies = [ [[package]] name = "ethrex-levm" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" +version = "19.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v19.0.0#68b2f4f523a7c34675acfafac0d52c895e03db29" dependencies = [ "bytes", "derive_more 1.0.0", @@ -3079,139 +3117,20 @@ dependencies = [ "thiserror 2.0.18", ] -[[package]] -name = "ethrex-metrics" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" -dependencies = [ - "axum", - "ethrex-common", - "prometheus", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", - "tracing-subscriber 0.3.23", -] - -[[package]] -name = "ethrex-p2p" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" -dependencies = [ - "aes", - "aes-gcm", - "bytes", - "concat-kdf", - "crossbeam", - "ctr", - "ethereum-types", - "ethrex-blockchain", - "ethrex-common", - "ethrex-crypto", - "ethrex-rlp", - "ethrex-storage", - "ethrex-trie", - "futures", - "hex", - "hkdf", - "hmac", - "indexmap 2.14.0", - "lazy_static", - "lru", - "prometheus", - "rand 0.8.6", - "rayon", - "rustc-hash", - "secp256k1 0.30.0", - "serde", - "sha2", - "snap", - "spawned-concurrency", - "spawned-rt", - "thiserror 2.0.18", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", -] - [[package]] name = "ethrex-rlp" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" +version = "19.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v19.0.0#68b2f4f523a7c34675acfafac0d52c895e03db29" dependencies = [ "bytes", "ethereum-types", "thiserror 2.0.18", ] -[[package]] -name = "ethrex-rpc" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" -dependencies = [ - "axum", - "axum-extra", - "bytes", - "envy", - "ethereum-types", - "ethrex-blockchain", - "ethrex-common", - "ethrex-crypto", - "ethrex-metrics", - "ethrex-p2p", - "ethrex-rlp", - "ethrex-storage", - "ethrex-trie", - "ethrex-vm", - "hex", - "hex-literal", - "jsonwebtoken 9.3.1", - "rand 0.8.6", - "reqwest 0.12.28", - "secp256k1 0.30.0", - "serde", - "serde_json", - "sha2", - "spawned-concurrency", - "spawned-rt", - "thiserror 2.0.18", - "tokio", - "tokio-util", - "tower-http", - "tracing", - "tracing-subscriber 0.3.23", - "uuid", -] - -[[package]] -name = "ethrex-storage" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" -dependencies = [ - "anyhow", - "bytes", - "ethrex-common", - "ethrex-crypto", - "ethrex-rlp", - "ethrex-trie", - "fastbloom", - "lru", - "rayon", - "rustc-hash", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tracing", -] - [[package]] name = "ethrex-trie" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" +version = "19.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v19.0.0#68b2f4f523a7c34675acfafac0d52c895e03db29" dependencies = [ "anyhow", "bytes", @@ -3229,8 +3148,8 @@ dependencies = [ [[package]] name = "ethrex-vm" -version = "12.0.0" -source = "git+https://github.com/lambdaclass/ethrex.git?rev=b112f94748a222e78b642406155674df9e180ee4#b112f94748a222e78b642406155674df9e180ee4" +version = "19.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v19.0.0#68b2f4f523a7c34675acfafac0d52c895e03db29" dependencies = [ "bytes", "derive_more 1.0.0", @@ -3255,18 +3174,6 @@ dependencies = [ "once_cell", ] -[[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.4", - "siphasher", -] - [[package]] name = "fastrand" version = "2.4.1" @@ -3301,7 +3208,6 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ - "bitvec", "rand_core 0.6.4", "subtle", ] @@ -3314,8 +3220,8 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "fields" -version = "0.18.0" -source = "git+https://github.com/0xPolygonHermez/pil2-proofman.git?tag=v0.18.0#42ad4d02da3d3b6238ac126ead82a8814f3fd426" +version = "1.0.0-alpha" +source = "git+https://github.com/0xPolygonHermez/pil2-proofman.git?tag=v1.0.0-alpha#4f2a34ac686c788ad66a1a8ea0a17a016319e65b" dependencies = [ "cfg-if", "num-bigint", @@ -3325,13 +3231,12 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" dependencies = [ "cfg-if", "libc", - "libredox", ] [[package]] @@ -3342,9 +3247,9 @@ checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixed-cache" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41c7aa69c00ebccf06c3fa7ffe2a6cf26a58b5fe4deabfe646285ff48136a8f" +checksum = "2fe63500644ef0269fe6b744e7e5dc5c20b5eebf3d881bc2be53f194636f6583" dependencies = [ "equivalent", "rapidhash", @@ -3358,7 +3263,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.6", + "rand 0.8.7", "rustc-hex", "static_assertions", ] @@ -3381,7 +3286,7 @@ checksum = "6dc7a9cb3326bafb80642c5ce99b39a2c0702d4bfa8ee8a3e773791a6cbe2407" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3513,7 +3418,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -3530,9 +3435,9 @@ checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-timer" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968" dependencies = [ "gloo-timers", "send_wrapper", @@ -3592,24 +3497,23 @@ 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" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", - "wasip2", - "wasip3", + "rand_core 0.10.1", + "wasm-bindgen", ] [[package]] @@ -3628,7 +3532,7 @@ version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" dependencies = [ - "bitflags 2.11.1", + "bitflags", "libc", "libgit2-sys", "log", @@ -3664,9 +3568,9 @@ dependencies = [ [[package]] name = "gloo-timers" -version = "0.2.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +checksum = "482ce8a491a501da4cd806bd190275363d674f2845005c6ddbd5d3e1dd54495d" dependencies = [ "futures-channel", "futures-core", @@ -3698,21 +3602,11 @@ dependencies = [ "subtle", ] -[[package]] -name = "guest" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere-guests?tag=v0.11.0#91735556bb1bdd6a16c3bcecee46ef14078846d0" -dependencies = [ - "ere-codec", - "ere-platform-core", - "sha2", -] - [[package]] name = "h2" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ "atomic-waker", "bytes", @@ -3770,47 +3664,26 @@ dependencies = [ "allocator-api2", "equivalent", "foldhash 0.2.0", - "serde", - "serde_core", ] [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" - -[[package]] -name = "hashlink" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ - "hashbrown 0.16.1", -] - -[[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", + "foldhash 0.2.0", + "serde", + "serde_core", ] [[package]] -name = "headers-core" -version = "0.3.0" +name = "hashlink" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" dependencies = [ - "http", + "hashbrown 0.16.1", ] [[package]] @@ -3840,12 +3713,27 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "hex-conservative" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830e599c2904b08f0834ee6337d8fe8f0ed4a63b5d9e7a7f49c0ffa06d08d360" +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-literal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" + [[package]] name = "hex-simd" version = "0.8.0" @@ -3872,7 +3760,7 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.4", + "rand 0.9.5", "ring", "serde", "thiserror 2.0.18", @@ -3895,7 +3783,7 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.4", + "rand 0.9.5", "resolv-conf", "serde", "smallvec", @@ -3924,9 +3812,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -3969,9 +3857,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" [[package]] name = "humantime-serde" @@ -3983,11 +3871,20 @@ dependencies = [ "serde", ] +[[package]] +name = "hybrid-array" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" -version = "1.9.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" dependencies = [ "atomic-waker", "bytes", @@ -4020,6 +3917,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", + "webpki-roots", ] [[package]] @@ -4088,7 +3986,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.61.2", + "windows-core 0.62.2", ] [[package]] @@ -4182,12 +4080,6 @@ dependencies = [ "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" @@ -4207,9 +4099,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -4294,7 +4186,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4341,27 +4233,27 @@ checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] [[package]] name = "inotify" -version = "0.11.1" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" +checksum = "153be1941a183ec9ccd095ddbe17a8b8d435ef6c76e9e02451b933c3999af2c8" dependencies = [ - "bitflags 2.11.1", + "bitflags", "inotify-sys", "libc", ] [[package]] name = "inotify-sys" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" dependencies = [ "libc", ] @@ -4376,27 +4268,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "integration-tests" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere-guests?tag=v0.11.0#91735556bb1bdd6a16c3bcecee46ef14078846d0" -dependencies = [ - "alloy-primitives", - "ere-dockerized", - "flate2", - "guest", - "rayon", - "serde", - "serde_json", - "sha2", - "stateless", - "stateless-validator-common", - "tar", - "tempfile", - "tracing", - "tracing-subscriber 0.3.23", -] - [[package]] name = "ipconfig" version = "0.3.4" @@ -4416,16 +4287,6 @@ 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_polyfill" version = "1.70.2" @@ -4481,6 +4342,36 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version 0.4.1", + "simd_cesu8", + "syn 2.0.118", +] + [[package]] name = "jni-sys" version = "0.3.1" @@ -4506,28 +4397,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.95" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] @@ -4590,7 +4480,7 @@ dependencies = [ "jsonrpsee-types", "parking_lot", "pin-project", - "rand 0.9.4", + "rand 0.9.5", "rustc-hash", "serde", "serde_json", @@ -4635,7 +4525,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -4705,24 +4595,9 @@ dependencies = [ [[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 = "jsonwebtoken" -version = "10.3.0" +version = "10.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0529410abe238729a60b108898784df8984c87f6054c9c4fcacc47e4803c1ce1" +checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" dependencies = [ "aws-lc-rs", "base64", @@ -4733,6 +4608,7 @@ dependencies = [ "serde_json", "signature", "simple_asn1", + "zeroize", ] [[package]] @@ -4756,14 +4632,24 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", +] + +[[package]] +name = "keccak" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", ] [[package]] name = "keccak-asm" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa468878266ad91431012b3e5ef1bf9b170eab22883503a318d46857afa4579a" +checksum = "dd5dc2c0d691cbf7595cde551ced329cca99c2387c2cbc97754c5d0cd045d3ee" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -4786,9 +4672,9 @@ checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" [[package]] name = "kqueue" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +checksum = "273c0752728918e0ac4976f2b275b6fefb9ecd400585dec929419f3844cd87b5" dependencies = [ "kqueue-sys", "libc", @@ -4796,11 +4682,11 @@ dependencies = [ [[package]] name = "kqueue-sys" -version = "1.0.4" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" dependencies = [ - "bitflags 1.3.2", + "bitflags", "libc", ] @@ -4811,11 +4697,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58b1a1c1102a5a7fbbda117b79fb3a01e033459c738a3c1642269603484fd1c1" dependencies = [ "lambdaworks-math", - "rand 0.8.6", + "rand 0.8.7", "rand_chacha 0.3.1", "serde", "sha2", - "sha3", + "sha3 0.10.9", ] [[package]] @@ -4827,7 +4713,7 @@ dependencies = [ "getrandom 0.2.17", "num-bigint", "num-traits", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_json", ] @@ -4838,12 +4724,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[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.186" @@ -4852,9 +4732,9 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libgit2-sys" -version = "0.18.3+1.9.2" +version = "0.18.5+1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487" +checksum = "005d6ae6eac1912906073e069f7db60b1fa98e052a68227824afe3e3a1c59ca2" dependencies = [ "cc", "libc", @@ -4880,9 +4760,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libp2p-identity" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c7892c221730ba55f7196e98b0b8ba5e04b4155651736036628e9f73ed6fc3" +checksum = "9525f3831544f7ae497bde79adf114ef127b0fbbb97edbbf692a80408636421c" dependencies = [ "asn1_der", "bs58", @@ -4890,7 +4770,7 @@ dependencies = [ "hkdf", "k256", "multihash", - "quick-protobuf", + "prost", "sha2", "thiserror 2.0.18", "tracing", @@ -4899,14 +4779,11 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.16" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ - "bitflags 2.11.1", "libc", - "plain", - "redox_syscall 0.7.4", ] [[package]] @@ -4926,87 +4803,50 @@ dependencies = [ [[package]] name = "libssz" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54193e6560611847aceb81ae53c75aa7031b50304bead95ec935f97af79378f2" -dependencies = [ - "smallvec", -] - -[[package]] -name = "libssz" -version = "0.2.1" -source = "git+https://github.com/lambdaclass/libssz?rev=7262a4f#7262a4f17f71fb9108166ba260659bcdd64feb4c" -dependencies = [ - "smallvec", -] - -[[package]] -name = "libssz-derive" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb6393ec2e9b660bbcb0d9d74aac7b3c351c7b4440dcc24e9d344e62cf4bf10" +checksum = "d498c0482bba87d2647ea4601ea76cf2b498065e3958798a88f49274f3ced5e9" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "smallvec", ] [[package]] name = "libssz-derive" -version = "0.2.1" -source = "git+https://github.com/lambdaclass/libssz?rev=7262a4f#7262a4f17f71fb9108166ba260659bcdd64feb4c" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ddfb5c969c28a4a54043e630f80c723352637bd1020f256ee3ac7a8814922b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "libssz-merkle" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37acd26ea2fbecfd8af8121780a385284cfb6d6f4c2f77648a9798b5d95a87d8" -dependencies = [ - "libssz 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2", -] - -[[package]] -name = "libssz-merkle" -version = "0.2.1" -source = "git+https://github.com/lambdaclass/libssz?rev=7262a4f#7262a4f17f71fb9108166ba260659bcdd64feb4c" +checksum = "63c6d6d5ce5d79bba66bc98c99869eedffedf7f14f0aa0915f1a62802650bdf6" dependencies = [ - "libssz 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", + "libssz", "sha2", ] [[package]] name = "libssz-types" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc4f9486929bd568731a599e9b4914aa44a0b4ecbd38c702151fe97210c323" -dependencies = [ - "libssz 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libssz-merkle 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec", -] - -[[package]] -name = "libssz-types" -version = "0.2.1" -source = "git+https://github.com/lambdaclass/libssz?rev=7262a4f#7262a4f17f71fb9108166ba260659bcdd64feb4c" +checksum = "747273ab2d923e82ed147091fe0fb3e602dd2012c872cdad5efe69e27c3b4099" dependencies = [ - "libssz 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", - "libssz-merkle 0.2.1 (git+https://github.com/lambdaclass/libssz?rev=7262a4f)", + "libssz", + "libssz-merkle", "smallvec", ] [[package]] name = "libz-sys" -version = "1.1.28" +version = "1.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" +checksum = "85bc9657773828b90eeb625adff10eeac83cc21bbfd8e23a03eaa8a33c9e28d9" dependencies = [ "cc", "libc", @@ -5030,12 +4870,6 @@ dependencies = [ "serde_core", ] -[[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" @@ -5060,9 +4894,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" [[package]] name = "lru" @@ -5091,9 +4925,9 @@ dependencies = [ [[package]] name = "lz4_flex" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98c23545df7ecf1b16c303910a69b079e8e251d60f7dd2cc9b4177f2afaf1746" +checksum = "90071f8077f8e40adfc4b7fe9cd495ce316263f19e75c2211eeff3fdf475a3d9" [[package]] name = "mach2" @@ -5106,13 +4940,13 @@ dependencies = [ [[package]] name = "macro-string" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +checksum = "59a9dbbfc75d2688ed057456ce8a3ee3f48d12eec09229f560f3643b9f275653" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5169,7 +5003,7 @@ checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5189,27 +5023,27 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap2" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +checksum = "d1219ed1b7f229ee7104d281dd01d6802fe28bb6e95d292942c4daacdeb798c0" dependencies = [ "libc", ] [[package]] name = "metrics" -version = "0.24.3" +version = "0.24.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8" +checksum = "89550ee9f79e88fef3119de263694973a8adb26c21d75322164fb8c493039fe2" dependencies = [ - "ahash", "portable-atomic", + "rapidhash", ] [[package]] @@ -5220,7 +5054,7 @@ checksum = "161ab904c2c62e7bda0f7562bf22f96440ca35ff79e66c800cbac298f2f4f5ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5247,9 +5081,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "log", @@ -5275,7 +5109,7 @@ checksum = "59b43b4fd69e3437618106f7754f34021b831a514f9e1a98ae863cabcd8d8dad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5322,23 +5156,23 @@ dependencies = [ [[package]] name = "multibase" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" +checksum = "7e0e4a371cbf1dfd666b658ba137763edb23c45beb43cfe369b5593cd6b437b6" dependencies = [ "base-x", "base256emoji", + "base45", "data-encoding", "data-encoding-macro", ] [[package]] name = "multihash" -version = "0.19.4" +version = "0.19.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ace881e3f514092ce9efbcb8f413d0ad9763860b828981c2de51ddc666936c" +checksum = "577c63b00ad74d57e8c9aa870b5fccebf2fd64a308a5aee9f1bb88e4aea19447" dependencies = [ - "no_std_io2", "unsigned-varint", ] @@ -5359,7 +5193,7 @@ checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5379,27 +5213,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "nix" -version = "0.31.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" -dependencies = [ - "bitflags 2.11.1", - "cfg-if", - "cfg_aliases", - "libc", -] - -[[package]] -name = "no_std_io2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3564ce7035b1e4778d8cb6cacebb5d766b5e8fe5a75b9e441e33fb61a872c6" -dependencies = [ - "memchr", -] - [[package]] name = "nom" version = "7.1.3" @@ -5416,7 +5229,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.11.1", + "bitflags", "fsevent-sys", "inotify", "kqueue", @@ -5434,7 +5247,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.11.1", + "bitflags", ] [[package]] @@ -5471,9 +5284,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", @@ -5490,9 +5303,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-integer" @@ -5505,11 +5318,10 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -5564,7 +5376,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5591,30 +5403,15 @@ dependencies = [ "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-core-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.1", + "bitflags", ] -[[package]] -name = "objc2-encode" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" - [[package]] name = "objc2-io-kit" version = "0.3.2" @@ -5649,15 +5446,14 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.78" +version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ - "bitflags 2.11.1", + "bitflags", "cfg-if", "foreign-types", "libc", - "once_cell", "openssl-macros", "openssl-sys", ] @@ -5670,7 +5466,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5681,9 +5477,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.114" +version = "0.9.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" dependencies = [ "cc", "libc", @@ -5767,7 +5563,7 @@ dependencies = [ "futures-util", "opentelemetry", "percent-encoding", - "rand 0.9.4", + "rand 0.9.5", "thiserror 2.0.18", ] @@ -5799,15 +5595,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "pairing" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" -dependencies = [ - "group", -] - [[package]] name = "parity-scale-codec" version = "3.7.5" @@ -5834,7 +5621,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5855,7 +5642,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", "windows-link 0.2.1", ] @@ -5884,9 +5671,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.6" +version = "2.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +checksum = "47627dd7305c6a2d6c8c6bcd24c5a4c17dbbf425f4f9c5313e724b38fc9782e9" dependencies = [ "memchr", "ucd-trie", @@ -5899,10 +5686,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ "phf_macros", - "phf_shared", + "phf_shared 0.13.1", "serde", ] +[[package]] +name = "phf" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010378780309880b08997fae13be7834dba947d36393bd372f2b1556deb2a2f6" +dependencies = [ + "phf_shared 0.14.0", +] + [[package]] name = "phf_generator" version = "0.13.1" @@ -5910,7 +5706,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ "fastrand", - "phf_shared", + "phf_shared 0.13.1", ] [[package]] @@ -5920,10 +5716,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ "phf_generator", - "phf_shared", + "phf_shared 0.13.1", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5935,24 +5731,33 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fd9027e2d9319be6349febd1db4e8d02aa544921200c9b777720ac34a3aa89" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -5983,12 +5788,6 @@ version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - [[package]] name = "plain_hasher" version = "0.2.3" @@ -6005,7 +5804,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "opaque-debug", "universal-hash", ] @@ -6040,16 +5839,6 @@ 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 = "primeorder" version = "0.13.6" @@ -6089,7 +5878,7 @@ 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", + "toml_edit 0.25.12+spec-1.1.0", ] [[package]] @@ -6111,7 +5900,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6123,49 +5912,10 @@ 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.1", - "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.1", - "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", - "procfs", - "protobuf", - "thiserror 2.0.18", -] - [[package]] name = "proofman-util" -version = "0.18.0" -source = "git+https://github.com/0xPolygonHermez/pil2-proofman.git?tag=v0.18.0#42ad4d02da3d3b6238ac126ead82a8814f3fd426" +version = "1.0.0-alpha" +source = "git+https://github.com/0xPolygonHermez/pil2-proofman.git?tag=v1.0.0-alpha#4f2a34ac686c788ad66a1a8ea0a17a016319e65b" dependencies = [ "bincode 2.0.1", "colored", @@ -6175,8 +5925,8 @@ dependencies = [ [[package]] name = "proofman-verifier" -version = "0.18.0" -source = "git+https://github.com/0xPolygonHermez/pil2-proofman.git?tag=v0.18.0#42ad4d02da3d3b6238ac126ead82a8814f3fd426" +version = "1.0.0-alpha" +source = "git+https://github.com/0xPolygonHermez/pil2-proofman.git?tag=v1.0.0-alpha#4f2a34ac686c788ad66a1a8ea0a17a016319e65b" dependencies = [ "bincode 2.0.1", "fields", @@ -6193,9 +5943,9 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.11.1", + "bitflags", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -6222,14 +5972,25 @@ checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", +] + +[[package]] +name = "proptest-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c57924a81864dddafba92e1bf92f9bf82f97096c44489548a60e888e1547549b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", ] [[package]] name = "prost" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" dependencies = [ "bytes", "prost-derive", @@ -6237,46 +5998,26 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" dependencies = [ "anyhow", "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "prost-types" -version = "0.14.3" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" +checksum = "f94967dc7688f3054c7fac87473ffae4cc4c3904800e2d9f5b857246d8963b0a" dependencies = [ "prost", ] -[[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" @@ -6294,7 +6035,7 @@ checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6318,20 +6059,11 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-protobuf" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" -dependencies = [ - "byteorder", -] - [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", "cfg_aliases", @@ -6349,15 +6081,16 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.14" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.4", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash", "rustls", @@ -6371,23 +6104,23 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ "cfg_aliases", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -6412,18 +6145,18 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rancor" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a063ea72381527c2a0561da9c80000ef822bdd7c3241b1cc1b12100e3df081ee" +checksum = "daff8b7b3ccf5f7ba270b3e7a0a4d4c701c5797e38dec27c7e2c3dbb830fed1c" dependencies = [ "ptr_meta", ] [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -6433,15 +6166,26 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", "serde", ] +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -6481,6 +6225,21 @@ dependencies = [ "serde", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "rand_xorshift" version = "0.4.0" @@ -6501,11 +6260,11 @@ dependencies = [ [[package]] name = "rapidhash" -version = "4.4.1" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +checksum = "5da7e78a036ce858e8d55b7e7dc8ba3a88b78350fd2155d3591bbd966b58589e" dependencies = [ - "rand 0.9.4", + "getrandom 0.3.4", "rustversion", ] @@ -6515,7 +6274,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.11.1", + "bitflags", ] [[package]] @@ -6544,16 +6303,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.1", -] - -[[package]] -name = "redox_syscall" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" -dependencies = [ - "bitflags 2.11.1", + "bitflags", ] [[package]] @@ -6584,14 +6334,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "regex" -version = "1.12.3" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2" dependencies = [ "aho-corasick", "memchr", @@ -6601,9 +6351,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db" dependencies = [ "aho-corasick", "memchr", @@ -6612,15 +6362,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rend" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cadadef317c2f20755a64d7fdc48f9e7178ee6b0e1f7fce33fa60f1d68a276e6" +checksum = "663ba70707f96e871406fe10d68128412e619b06d1d47cb91c3a4c6501176240" dependencies = [ "bytecheck", ] @@ -6669,13 +6419,14 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", ] [[package]] name = "reqwest" -version = "0.13.2" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ "base64", "bytes", @@ -6694,7 +6445,7 @@ dependencies = [ "quinn", "rustls", "rustls-pki-types", - "rustls-platform-verifier 0.6.2", + "rustls-platform-verifier 0.7.0", "serde", "serde_json", "sync_wrapper", @@ -6723,7 +6474,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "futures-core", "futures-util", @@ -6750,7 +6501,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-signer", "alloy-signer-local", @@ -6758,7 +6509,7 @@ dependencies = [ "metrics", "parking_lot", "pin-project", - "rand 0.9.4", + "rand 0.9.5", "reth-chainspec", "reth-errors", "reth-ethereum-primitives", @@ -6782,7 +6533,7 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655 dependencies = [ "alloy-chains", "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-evm", "alloy-genesis", "alloy-primitives", @@ -6800,12 +6551,12 @@ name = "reth-cli-util" version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "cfg-if", "eyre", "libc", - "rand 0.8.6", + "rand 0.8.7", "reth-fs-util", "secp256k1 0.30.0", "serde", @@ -6814,12 +6565,12 @@ dependencies = [ [[package]] name = "reth-codecs" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a79b3247ae4fbb1d4d35ce83a11fc596428a4c6ea836c98a75a55340192578a4" +checksum = "4ee9f6a4b8f4992c030c82fb8c2dcc872349452009b4127055f678f7d5a3dea3" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-genesis", "alloy-primitives", "alloy-trie", @@ -6835,13 +6586,13 @@ dependencies = [ [[package]] name = "reth-codecs-derive" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df5dbae40c272b8a1b4fcc08ee2d4e77d3b0ccdb187c1313f412a73ff54ff2a2" +checksum = "07feedb2c0ec9ec76dd1210b7486904f139243dc9b264586994a6246955c1c73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -6879,7 +6630,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -6975,7 +6726,7 @@ name = "reth-db-models" version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "arbitrary", "bytes", @@ -6996,7 +6747,7 @@ dependencies = [ "enr", "itertools 0.14.0", "parking_lot", - "rand 0.8.6", + "rand 0.8.7", "reth-ethereum-forks", "reth-net-banlist", "reth-net-nat", @@ -7023,7 +6774,7 @@ dependencies = [ "futures", "itertools 0.14.0", "metrics", - "rand 0.9.4", + "rand 0.9.5", "reth-chainspec", "reth-ethereum-forks", "reth-metrics", @@ -7075,7 +6826,7 @@ dependencies = [ "futures", "hmac", "pin-project", - "rand 0.8.6", + "rand 0.8.7", "reth-network-peers", "secp256k1 0.30.0", "sha2", @@ -7115,7 +6866,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rpc-types-engine", "auto_impl", @@ -7181,7 +6932,7 @@ dependencies = [ "alloy-chains", "alloy-consensus", "alloy-eip7928", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-hardforks", "alloy-primitives", "alloy-rlp", @@ -7201,7 +6952,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "reth-chainspec", "reth-consensus", @@ -7216,7 +6967,7 @@ name = "reth-ethereum-engine-primitives" version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rpc-types-engine", "reth-engine-primitives", @@ -7246,7 +6997,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rpc-types-eth", "reth-codecs", @@ -7270,7 +7021,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-evm", "alloy-primitives", "auto_impl", @@ -7292,7 +7043,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-evm", "alloy-primitives", "alloy-rpc-types-engine", @@ -7343,7 +7094,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-evm", "alloy-primitives", "alloy-rlp", @@ -7371,7 +7122,7 @@ name = "reth-libmdbx" version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ - "bitflags 2.11.1", + "bitflags", "byteorder", "crossbeam-queue", "dashmap", @@ -7420,7 +7171,7 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655 dependencies = [ "futures-util", "if-addrs", - "reqwest 0.13.2", + "reqwest 0.13.4", "serde_with", "thiserror 2.0.18", "tokio", @@ -7433,7 +7184,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rlp", "aquamarine", @@ -7446,8 +7197,8 @@ dependencies = [ "metrics", "parking_lot", "pin-project", - "rand 0.8.6", - "rand 0.9.4", + "rand 0.8.7", + "rand 0.9.5", "rayon", "reth-chainspec", "reth-consensus", @@ -7514,7 +7265,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "auto_impl", "derive_more 2.1.1", @@ -7606,7 +7357,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rpc-types-engine", "clap", @@ -7616,7 +7367,7 @@ dependencies = [ "futures", "humantime", "ipnet", - "rand 0.9.4", + "rand 0.9.5", "reth-chainspec", "reth-cli-util", "reth-config", @@ -7709,7 +7460,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-engine", @@ -7727,24 +7478,14 @@ dependencies = [ "tokio", ] -[[package]] -name = "reth-payload-validator" -version = "2.1.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" -dependencies = [ - "alloy-consensus", - "alloy-rpc-types-engine", - "reth-primitives-traits", -] - [[package]] name = "reth-primitives-traits" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc759fd87c3f65440e5d3bfa3107fe8a13a61a6807cd485c62c49d63c7bf6717" +checksum = "e83138080a41e35a6aa876410ca67231fb35da7e2ca494315d8b8be15e9ed9c0" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-genesis", "alloy-primitives", "alloy-rlp", @@ -7759,7 +7500,6 @@ dependencies = [ "once_cell", "proptest", "proptest-arbitrary-interop", - "quanta", "rayon", "reth-codecs", "revm-bytecode", @@ -7776,7 +7516,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-genesis", "alloy-primitives", "alloy-rpc-types-engine", @@ -7852,7 +7592,7 @@ name = "reth-rpc-api" version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-genesis", "alloy-json-rpc", "alloy-primitives", @@ -7866,7 +7606,7 @@ dependencies = [ "alloy-rpc-types-mev", "alloy-rpc-types-trace", "alloy-rpc-types-txpool", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "jsonrpsee", "reth-chain-state", "reth-engine-primitives", @@ -7905,7 +7645,7 @@ dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-eip7928", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-evm", "alloy-json-rpc", "alloy-network", @@ -7913,7 +7653,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-types-eth", "alloy-rpc-types-mev", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "async-trait", "auto_impl", "dyn-clone", @@ -7949,7 +7689,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-evm", "alloy-network", "alloy-primitives", @@ -7963,8 +7703,8 @@ dependencies = [ "jsonrpsee-core", "jsonrpsee-types", "metrics", - "rand 0.9.4", - "reqwest 0.13.2", + "rand 0.9.5", + "reqwest 0.13.4", "reth-chain-state", "reth-chainspec", "reth-errors", @@ -7996,7 +7736,7 @@ name = "reth-rpc-server-types" version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rpc-types-engine", "jsonrpsee-core", @@ -8009,9 +7749,9 @@ dependencies = [ [[package]] name = "reth-rpc-traits" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b766da61ec7c46596386b4bc88d9b57d1939d3da2bc9e927567a8a23650e5ce9" +checksum = "706ca5cd7bc99bdf82d89f7f3215242a9f901128ab4c09bb88b0640a0cf40b77" dependencies = [ "alloy-consensus", "alloy-network", @@ -8056,7 +7796,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rpc-types-engine", "auto_impl", @@ -8079,7 +7819,7 @@ name = "reth-storage-errors" version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rlp", "derive_more 2.1.1", @@ -8163,18 +7903,18 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rlp", "aquamarine", "auto_impl", - "bitflags 2.11.1", + "bitflags", "futures-util", "imbl", "metrics", "parking_lot", "pin-project", - "rand 0.9.4", + "rand 0.9.5", "reth-chain-state", "reth-chainspec", "reth-eth-wire-types", @@ -8207,7 +7947,7 @@ version = "2.1.0" source = "git+https://github.com/paradigmxyz/reth?tag=v2.1.0#d58c6e3d0723a28f655e89da83c3738e47dcc364" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-primitives", "alloy-rlp", "alloy-trie", @@ -8236,7 +7976,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde 2.0.1", + "alloy-serde 2.1.1", "alloy-trie", "arbitrary", "arrayvec", @@ -8326,9 +8066,9 @@ dependencies = [ [[package]] name = "reth-zstd-compressors" -version = "0.3.0" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fa54c341d926ec9b0f7fc0c87f831aa4959de699e69caab1a0bfd914326c09" +checksum = "35a1f121117f149412446e92c5442d4a9722e04e7760ac3f62d518f4213634d9" dependencies = [ "zstd", ] @@ -8359,7 +8099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdbb3a3d735efa94c91f2ef6bf20a35f99a77bc78f3e25bd758336901bdf9661" dependencies = [ "bitvec", - "phf", + "phf 0.13.1", "revm-primitives", "serde", ] @@ -8464,9 +8204,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.39.0" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "731b682530a732ef9c189ef831589128e2ce34d4a306c956322ae2dffe009715" +checksum = "5dea6997563b46432f9c1822275cab4c462aed8f4a189dc518478c31317afb99" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -8537,7 +8277,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c32490ed687dba31c3c882beb8c20408bdd30ef96690d8f145b0ee9a87040bfe" dependencies = [ "alloy-eip7928", - "bitflags 2.11.1", + "bitflags", "revm-bytecode", "revm-primitives", "serde", @@ -8578,13 +8318,13 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73389e0c99e664f919275ab5b5b0471391fe9a8de61e1dff9b1eaf56a90f16e3" +checksum = "815cc8a37159a463064825246cadb07961e25cd9885908606f6d08a98d8f8874" dependencies = [ "bytecheck", "bytes", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "indexmap 2.14.0", "munge", "ptr_meta", @@ -8597,13 +8337,13 @@ dependencies = [ [[package]] name = "rkyv_derive" -version = "0.8.16" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d2ed0b54125315fb36bd021e82d314d1c126548f871634b483f46b31d13cac6" +checksum = "c0ed1a78a1b19d184b0daa629dd9a024573173ec7d485b287cb369fb3607cc1c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -8628,9 +8368,9 @@ dependencies = [ [[package]] name = "roaring" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba9ce64a8f45d7fc86358410bb1a82e8c987504c0d4900e9141d69a9f26c885" +checksum = "1dedc5658c6ecb3bdb5ef5f3295bb9253f42dcf3fd1402c03f6b1f7659c3c4a9" dependencies = [ "bytemuck", "byteorder", @@ -8663,15 +8403,16 @@ checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" [[package]] name = "ruint" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0298da754d1395046b0afdc2f20ee76d29a8ae310cd30ffa84ed42acba9cb12a" +checksum = "45caf26f647c19115bf9c453c70ffe4a4a3a6390dceebd942610584f99b8ddce" dependencies = [ "alloy-rlp", "arbitrary", "ark-ff 0.3.0", "ark-ff 0.4.2", "ark-ff 0.5.0", + "ark-ff 0.6.0", "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", @@ -8681,8 +8422,8 @@ dependencies = [ "parity-scale-codec", "primitive-types 0.12.2", "proptest", - "rand 0.8.6", - "rand 0.9.4", + "rand 0.8.7", + "rand 0.9.5", "rlp 0.5.2", "ruint-macro", "serde_core", @@ -8698,11 +8439,11 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" dependencies = [ - "rand 0.8.6", + "rand 0.9.5", ] [[package]] @@ -8729,37 +8470,24 @@ 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.1", - "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.1", + "bitflags", "errno", "libc", - "linux-raw-sys 0.12.1", + "linux-raw-sys", "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.39" +version = "0.23.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" dependencies = [ "aws-lc-rs", "log", @@ -8773,9 +8501,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -8785,9 +8513,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "web-time", "zeroize", @@ -8801,7 +8529,7 @@ checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni", + "jni 0.21.1", "log", "once_cell", "rustls", @@ -8816,13 +8544,13 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni", + "jni 0.22.4", "log", "once_cell", "rustls", @@ -8831,7 +8559,7 @@ dependencies = [ "rustls-webpki", "security-framework", "security-framework-sys", - "webpki-root-certs 1.0.7", + "webpki-root-certs 1.0.8", "windows-sys 0.61.2", ] @@ -8855,9 +8583,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "rusty-fork" @@ -8967,7 +8695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ "bitcoin_hashes", - "rand 0.8.6", + "rand 0.8.7", "secp256k1-sys 0.10.1", "serde", ] @@ -8979,7 +8707,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" dependencies = [ "bitcoin_hashes", - "rand 0.9.4", + "rand 0.9.5", "secp256k1-sys 0.11.0", ] @@ -9007,7 +8735,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.1", + "bitflags", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -9054,9 +8782,9 @@ dependencies = [ [[package]] name = "send_wrapper" -version = "0.4.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" +checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" @@ -9085,14 +8813,14 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "serde_json" -version = "1.0.149" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "indexmap 2.14.0", "itoa", @@ -9145,11 +8873,12 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.18.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64", + "bs58", "chrono", "hex", "indexmap 1.9.3", @@ -9164,14 +8893,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.18.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9186,12 +8915,12 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -9202,7 +8931,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "digest 0.10.7", ] @@ -9213,14 +8942,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" dependencies = [ "digest 0.10.7", - "keccak", + "keccak 0.1.6", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.0", ] [[package]] name = "sha3-asm" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cbb88c189d6352cc8ae96a39d19c7ecad8f7330b29461187f2587fdc2988d5" +checksum = "a6287fd675f713484342a89cbf0a386abef5f15919cfad607e5e1f19e1e15331" dependencies = [ "cc", "cfg-if", @@ -9241,6 +8980,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -9267,6 +9012,16 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version 0.4.1", + "simdutf8", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -9287,9 +9042,9 @@ dependencies = [ [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -9308,9 +9063,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" dependencies = [ "arbitrary", "serde", @@ -9324,9 +9079,9 @@ checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "socket2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", "windows-sys 0.61.2", @@ -9344,49 +9099,10 @@ dependencies = [ "http", "httparse", "log", - "rand 0.8.6", + "rand 0.8.7", "sha1", ] -[[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 = "spki" version = "0.7.3" @@ -9409,7 +9125,7 @@ version = "0.1.0" source = "git+https://github.com/paradigmxyz/stateless?rev=e5480e71a8031641ff471cad9dc482a7a14b32d5#e5480e71a8031641ff471cad9dc482a7a14b32d5" dependencies = [ "alloy-consensus", - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-genesis", "alloy-primitives", "alloy-rlp", @@ -9432,74 +9148,31 @@ dependencies = [ [[package]] name = "stateless-validator-common" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere-guests?tag=v0.11.0#91735556bb1bdd6a16c3bcecee46ef14078846d0" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere-guests?tag=v0.13.0#cffc3f1a299e0e5f097305775d6ba1141b5f9546" dependencies = [ - "alloy-eips 2.0.1", - "alloy-primitives", - "anyhow", - "ere-codec", - "ere-platform-core", - "libssz 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libssz-derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libssz-merkle 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libssz-types 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde", - "serde_with", + "const-hex", + "libssz", + "libssz-derive", + "libssz-merkle", + "libssz-types", "sha2", + "thiserror 2.0.18", ] [[package]] name = "stateless-validator-ethrex" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere-guests?tag=v0.11.0#91735556bb1bdd6a16c3bcecee46ef14078846d0" +version = "0.13.0" +source = "git+https://github.com/eth-act/ere-guests?tag=v0.13.0#cffc3f1a299e0e5f097305775d6ba1141b5f9546" dependencies = [ - "alloy-consensus", - "alloy-eips 2.0.1", - "alloy-genesis", - "alloy-rlp", - "anyhow", + "ere-platform-core", + "ere-util-build", "ethrex-common", "ethrex-crypto", "ethrex-guest-program", - "ethrex-rpc", - "guest", - "libssz 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libssz-merkle 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rkyv", - "stateless", - "stateless-validator-common", - "stateless-validator-reth", -] - -[[package]] -name = "stateless-validator-reth" -version = "0.11.0" -source = "git+https://github.com/eth-act/ere-guests?tag=v0.11.0#91735556bb1bdd6a16c3bcecee46ef14078846d0" -dependencies = [ - "alloy-consensus", - "alloy-eips 2.0.1", - "alloy-genesis", - "alloy-primitives", - "alloy-rlp", - "alloy-rpc-types-engine", - "anyhow", - "bincode 2.0.1", - "ere-prover-core", - "guest", - "once_cell", - "reth-chainspec", - "reth-ethereum-primitives", - "reth-evm-ethereum", - "reth-payload-validator", - "reth-primitives-traits", - "revm", - "serde", - "serde_with", - "sha2", - "stateless", + "hex-literal 1.1.0", "stateless-validator-common", - "tries", + "thiserror 2.0.18", ] [[package]] @@ -9542,7 +9215,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9554,7 +9227,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9582,9 +9255,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -9593,14 +9266,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" +checksum = "ec005042c7d952febc1a3ef5b0f6674e9054aa836877a31c90b20e25b3d31744" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9620,7 +9293,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9672,7 +9345,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.11.1", + "bitflags", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -9701,9 +9374,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.45" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" dependencies = [ "filetime", "libc", @@ -9717,9 +9390,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", - "rustix 1.1.4", + "rustix", "windows-sys 0.61.2", ] @@ -9749,7 +9422,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9760,28 +9433,28 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "thread-priority" -version = "3.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2210811179577da3d54eb69ab0b50490ee40491a25d95b8c6011ba40771cb721" +checksum = "8d2e834949be5111506bb252643498af1514f600d9e1dceedaa42afae155b67f" dependencies = [ - "bitflags 2.11.1", + "bitflags", "cfg-if", "libc", "log", "rustversion", - "windows 0.61.3", + "windows 0.62.2", ] [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] @@ -9797,12 +9470,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", - "itoa", "libc", "num-conv", "num_threads", @@ -9814,15 +9486,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" dependencies = [ "num-conv", "time-core", @@ -9849,9 +9521,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -9864,9 +9536,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.1" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -9887,7 +9559,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -9922,18 +9594,6 @@ dependencies = [ "tokio-util", ] -[[package]] -name = "tokio-tungstenite" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - [[package]] name = "tokio-util" version = "0.7.18" @@ -9944,7 +9604,6 @@ dependencies = [ "futures-core", "futures-io", "futures-sink", - "futures-util", "pin-project-lite", "slab", "tokio", @@ -10020,14 +9679,14 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.11+spec-1.1.0" +version = "0.25.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -10036,7 +9695,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -10053,9 +9712,9 @@ checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", "axum", @@ -10082,9 +9741,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" dependencies = [ "bytes", "prost", @@ -10112,20 +9771,20 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.11.1", + "bitflags", "bytes", "futures-util", "http", "http-body", - "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -10173,7 +9832,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -10336,22 +9995,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tungstenite" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand 0.9.4", - "sha1", - "thiserror 2.0.18", -] - [[package]] name = "twirp" version = "0.10.1" @@ -10383,9 +10026,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.20.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "ucd-trie" @@ -10431,9 +10074,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-xid" @@ -10447,7 +10090,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common", + "crypto-common 0.1.7", "subtle", ] @@ -10502,11 +10145,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.1" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ - "getrandom 0.4.2", + "getrandom 0.4.3", "js-sys", "wasm-bindgen", ] @@ -10578,7 +10221,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -10623,27 +10266,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.118" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -10654,9 +10288,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.68" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ "js-sys", "wasm-bindgen", @@ -10664,9 +10298,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.118" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -10674,48 +10308,26 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.118" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.118" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" 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.5.0" @@ -10729,18 +10341,6 @@ dependencies = [ "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.1", - "hashbrown 0.15.5", - "indexmap 2.14.0", - "semver 1.0.28", -] - [[package]] name = "wasmtimer" version = "0.4.3" @@ -10757,9 +10357,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.95" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -10781,14 +10381,23 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.7", + "webpki-root-certs 1.0.8", ] [[package]] name = "webpki-root-certs" -version = "1.0.7" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webpki-roots" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" dependencies = [ "rustls-pki-types", ] @@ -10939,7 +10548,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -10950,7 +10559,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -11292,118 +10901,31 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" 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" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "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", - "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.1", - "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 = "witness-generator" version = "0.1.0" dependencies = [ - "alloy-eips 2.0.1", + "alloy-eips 2.0.5", "alloy-genesis", "alloy-rpc-types-eth", "async-trait", "ef-tests", "erased-serde", "flate2", + "futures", "http", "jsonrpsee", "rayon", @@ -11436,6 +10958,31 @@ dependencies = [ "witness-generator", ] +[[package]] +name = "witness-generator-spec-cli" +version = "0.1.0" +dependencies = [ + "alloy-consensus", + "alloy-eips 2.0.5", + "alloy-primitives", + "alloy-rlp", + "anyhow", + "clap", + "humantime", + "reqwest 0.12.28", + "serde", + "serde_json", + "sha2", + "stateless-validator-common", + "tar", + "time", + "tokio", + "toml 0.8.23", + "tracing", + "tracing-subscriber 0.3.23", + "zstd", +] + [[package]] name = "writeable" version = "0.6.3" @@ -11458,14 +11005,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" dependencies = [ "libc", - "rustix 1.1.4", + "rustix", ] [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -11480,35 +11027,63 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] +[[package]] +name = "z6m_stateless_validator" +version = "0.1.0" +source = "git+https://github.com/erigontech/zilkworm?tag=v0.1.0-alpha.2-ere#9c38ed4b915ae563a0f63eae20e603df8ec00ffb" +dependencies = [ + "alloy-consensus", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-debug", + "alloy-trie", + "anyhow", + "reqwest 0.12.28", + "serde", + "stateless", + "tokio", + "z6m_unified_rlp", +] + +[[package]] +name = "z6m_unified_rlp" +version = "0.1.0" +source = "git+https://github.com/erigontech/zilkworm?tag=v0.1.0-alpha.2-ere#9c38ed4b915ae563a0f63eae20e603df8ec00ffb" +dependencies = [ + "alloy-primitives", + "alloy-rlp", +] + [[package]] name = "zerocopy" -version = "0.8.48" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.48" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] @@ -11521,28 +11096,28 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" dependencies = [ "zeroize_derive", ] [[package]] name = "zeroize_derive" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -11575,7 +11150,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn 2.0.118", ] [[package]] @@ -11591,8 +11166,8 @@ dependencies = [ [[package]] name = "zisk-verifier" -version = "0.18.0" -source = "git+https://github.com/0xPolygonHermez/zisk.git?tag=v0.18.0#790f9e28a6d1d8575e38dc77f2af4966b8659722" +version = "1.0.0-alpha" +source = "git+https://github.com/0xPolygonHermez/zisk.git?tag=v1.0.0-alpha#4b9f758fabc4955cac20af837019ccc31b803a46" dependencies = [ "proofman-verifier", ] diff --git a/Cargo.toml b/Cargo.toml index 8dabc512..f90612e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "crates/metrics", "crates/witness-generator", "crates/witness-generator-cli", + "crates/witness-generator-spec-cli", "crates/ere-hosts", ] resolver = "2" @@ -100,20 +101,22 @@ zero_sized_map_values = "warn" # local dependencies witness-generator = { path = "crates/witness-generator" } +witness-generator-spec-cli = { path = "crates/witness-generator-spec-cli" } zkevm-metrics = { path = "crates/metrics" } benchmark-runner = { path = "crates/benchmark-runner" } # ere -ere-dockerized = { git = "https://github.com/eth-act/ere", tag = "v0.11.0" } -ere-cluster-client-zisk = { git = "https://github.com/eth-act/ere", tag = "v0.11.0" } -ere-util-tokio = { git = "https://github.com/eth-act/ere", tag = "v0.11.0" } +ere-dockerized = { git = "https://github.com/eth-act/ere", tag = "v0.13.0" } +ere-cluster-client-zisk = { git = "https://github.com/eth-act/ere", tag = "v0.13.0" } +ere-util-tokio = { git = "https://github.com/eth-act/ere", tag = "v0.13.0" } # ere-guests -ere-guests-stateless-validator-reth = { git = "https://github.com/eth-act/ere-guests", tag = "v0.11.0", package = "stateless-validator-reth" } -ere-guests-stateless-validator-ethrex = { git = "https://github.com/eth-act/ere-guests", tag = "v0.11.0", package = "stateless-validator-ethrex" } -ere-guests-guest = { git = "https://github.com/eth-act/ere-guests", tag = "v0.11.0", package = "guest" } -ere-guests-integration-tests = { git = "https://github.com/eth-act/ere-guests", tag = "v0.11.0", package = "integration-tests" } -ere-guests-downloader = { git = "https://github.com/eth-act/ere-guests", tag = "v0.11.0", package = "downloader" } +stateless-validator-common = { git = "https://github.com/eth-act/ere-guests", tag = "v0.13.0", package = "stateless-validator-common" } +ere-guests-stateless-validator-ethrex = { git = "https://github.com/eth-act/ere-guests", tag = "v0.13.0", package = "stateless-validator-ethrex" } +stateless-validator-zilkworm = { git = "https://github.com/erigontech/zilkworm", tag = "v0.1.0-alpha.2-ere", package = "z6m_stateless_validator", features = [ + "download", +] } +ere-guests-downloader = { git = "https://github.com/eth-act/ere-guests", tag = "v0.13.0", package = "downloader" } reth-ethereum-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v2.1.0" } reth-rpc-api = { git = "https://github.com/paradigmxyz/reth", tag = "v2.1.0" } @@ -122,9 +125,12 @@ ef-tests = { git = "https://github.com/paradigmxyz/stateless", rev = "e5480e71a8 stateless = { git = "https://github.com/paradigmxyz/stateless", rev = "e5480e71a8031641ff471cad9dc482a7a14b32d5", default-features = false } # alloy -alloy-eips = { version = "2.0" } -alloy-rpc-types-eth = "2.0" -alloy-genesis = { version = "2.0", default-features = false } +alloy-consensus = { version = "=2.0.5", default-features = false } +alloy-eips = { version = "=2.0.5" } +alloy-primitives = { version = "=1.6.0", default-features = false } +alloy-rpc-types-eth = "=2.0.5" +alloy-genesis = { version = "=2.0.5", default-features = false } +alloy-rlp = { version = "0.3", default-features = false } # misc clap = { version = "4.0", features = ["derive"] } @@ -139,6 +145,7 @@ serde_derive = "*" jsonrpsee = "0.26.0" anyhow = "1.0" async-trait = "0.1.88" +futures = "0.3.32" tokio = { version = "1.45.1" } tokio-util = "0.7.15" tracing = "0.1.41" @@ -147,6 +154,7 @@ erased-serde = "0.4.8" auto_impl = "1.3.0" tempfile = "3.10" humantime = "2.1" +time = { version = "0.3", features = ["formatting"] } reqwest = { version = "0.12", default-features = false, features = [ "rustls-tls-native-roots", "json", @@ -155,3 +163,4 @@ flate2 = "1.1" tar = "0.4" toml = "0.8" hex = "0.4" +zstd = "0.13" diff --git a/README.md b/README.md index 85b2fbe6..03f2d0a5 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ This repository benchmarks Ethereum-related guest programs across multiple zkVMs ## Workspace At a Glance - **`crates/witness-generator-cli`**: fixture-generation CLI for EEST, RPC, and raw-input sources. +- **`crates/witness-generator-spec-cli`**: CLI and library for generating canonical stateless input bytes from CL/EL RPC endpoints. - **`crates/ere-hosts`**: benchmark CLI for execution, proving, and verification jobs. - **`crates/benchmark-runner`**: shared orchestration for guest resolution, execution, proof flow, and verification. - **`crates/metrics`**: serializable result types such as `BenchmarkRun`. @@ -31,6 +32,7 @@ Verify that both CLIs are reachable from the repo root: ```bash cargo run -p witness-generator-cli -- --help +cargo run -p witness-generator-spec-cli -- --help cargo run -p ere-hosts -- --help ``` @@ -54,6 +56,7 @@ cargo run -p ere-hosts --release -- --zkvms sp1 stateless-validator --execution- - [Benchmark input reference](docs/benchmark-execution-inputs.md) - [Benchmark output reference](docs/benchmark-execution-output.md) - [Witness Generator CLI notes](docs/witness-generator-cli.md) +- [Stateless input publication guide](docs/stateless-input-publication.md) The root README is intentionally short. Detailed workflow documentation lives under `docs/`. diff --git a/build-nethermind-guest.sh b/build-nethermind-guest.sh new file mode 100755 index 00000000..3af6b042 --- /dev/null +++ b/build-nethermind-guest.sh @@ -0,0 +1,148 @@ +#!/usr/bin/env bash +# Builds the Nethermind Zisk stateless-validator guest ELF and installs it +# under the directory expected by `ere-hosts --execution-client nethermind`. +# +# Pulls Nethermind from a branch that already carries: +# * Nethermind.Stateless.Executor: optional chain_config_json trailing +# section in InputSerializer + SpecProvider built from it in +# StatelessExecutor (so the guest validates blocks from any chain). +# * tools/StatelessInputGen: --from-fixture / --chain-config-envelope +# command-line modes plus FixtureReader / TransactionReader. +# +# No patches are applied here — anything we need lives in the source branch. +# +# Usage: +# ./build-nethermind-guest.sh +# ./build-nethermind-guest.sh --src /path/to/nethermind --output-dir ./nethermind-guest +# NETHERMIND_REF=some-branch ./build-nethermind-guest.sh +# +# After a successful run, point ere-hosts at the output directory: +# +# NETHERMIND_STATELESS_INPUT_GEN=./nethermind-guest/stateless-input-gen \ +# cargo run -p ere-hosts --release -- --zkvms zisk \ +# --bin-path ./nethermind-guest \ +# stateless-validator --execution-client nethermind +# +# Requirements: git, docker (running, supports linux/amd64), dotnet, make. + +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +NETHERMIND_REPO_URL="${NETHERMIND_REPO_URL:-https://github.com/NethermindEth/nethermind.git}" +NETHERMIND_SRC="${NETHERMIND_SRC:-${REPO_ROOT}/.nethermind-src}" +NETHERMIND_REF="${NETHERMIND_REF:-master}" +OUTPUT_DIR="${OUTPUT_DIR:-${REPO_ROOT}/nethermind-guest}" +OUTPUT_NAME="stateless-validator-nethermind-zisk.elf" + +usage() { + cat <<'EOF' +Usage: build-nethermind-guest.sh [--src DIR] [--output-dir DIR] [--ref GIT_REF] + +Options: + --src DIR Nethermind source checkout (default: ./.nethermind-src). + If the directory does not contain a git checkout, clone + ${NETHERMIND_REPO_URL} into it. + --output-dir DIR Destination for the renamed ELF (default: ./nethermind-guest). + --ref GIT_REF Branch / tag / commit to check out (default: master). + +Environment variables overriding the same options: + NETHERMIND_SRC, OUTPUT_DIR, NETHERMIND_REF, NETHERMIND_REPO_URL +EOF +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --src) NETHERMIND_SRC="$2"; shift 2 ;; + --output-dir) OUTPUT_DIR="$2"; shift 2 ;; + --ref) NETHERMIND_REF="$2"; shift 2 ;; + -h|--help) usage; exit 0 ;; + *) echo "Unknown argument: $1" >&2; usage >&2; exit 1 ;; + esac +done + +log() { + printf '[build-nethermind-guest] %s\n' "$*" +} + +require() { + if ! command -v "$1" >/dev/null 2>&1; then + echo "Missing required tool: $1" >&2 + exit 1 + fi +} + +require git +require docker +require dotnet +require make + +if [[ ! -d "${NETHERMIND_SRC}/.git" ]]; then + log "Cloning ${NETHERMIND_REPO_URL} (branch ${NETHERMIND_REF}) into ${NETHERMIND_SRC}" + git clone --recurse-submodules --branch "${NETHERMIND_REF}" "${NETHERMIND_REPO_URL}" "${NETHERMIND_SRC}" +else + log "Using existing Nethermind checkout at ${NETHERMIND_SRC}" + log "Fetching and checking out ${NETHERMIND_REF}" + git -C "${NETHERMIND_SRC}" fetch --tags --quiet + git -C "${NETHERMIND_SRC}" checkout "${NETHERMIND_REF}" +fi + +GUEST_DIR="${NETHERMIND_SRC}/src/Nethermind/Nethermind.Stateless.ZiskGuest" +if [[ ! -f "${GUEST_DIR}/Makefile" ]]; then + echo "Expected ${GUEST_DIR}/Makefile not found. Wrong branch?" >&2 + exit 1 +fi + +# NOTE: Do NOT strip Nethermind.Analyzers here. On master the analyzer +# ProjectReference in src/Nethermind/Directory.Build.props is already guarded by +# `'$(EnableZkEvm)' != 'true'`, so the guest build (EnableZkEvm=true) never loads +# it — no CS9057. The StatelessInputGen publish below is a normal host build that +# *needs* the analyzer: it ships the HardforkLabelsGenerator source generator that +# emits the implementation of HardforkLabels.BuildAll(); stripping it breaks that +# build with CS8795 (the ZK_EVM stub only applies to the guest). + +log "Running 'make build' in ${GUEST_DIR}" +make -C "${GUEST_DIR}" build + +BUILT_ELF="${GUEST_DIR}/bin/nethermind" +if [[ ! -f "${BUILT_ELF}" ]]; then + echo "make build did not produce ${BUILT_ELF}" >&2 + exit 1 +fi + +mkdir -p "${OUTPUT_DIR}" +cp "${BUILT_ELF}" "${OUTPUT_DIR}/${OUTPUT_NAME}" + +# Record the Nethermind commit the guest was built from. The benchmark runner +# reads this to populate the version field in metrics output paths so different +# builds don't overwrite each other. +NETHERMIND_GIT_SHA=$(git -C "${NETHERMIND_SRC}" rev-parse --short HEAD 2>/dev/null || echo unknown) +printf "%s\n" "${NETHERMIND_GIT_SHA}" > "${OUTPUT_DIR}/${OUTPUT_NAME%.elf}.version" +log "Recorded Nethermind version: ${NETHERMIND_GIT_SHA}" + +# Also build & install StatelessInputGen — the host-side benchmark runner +# delegates Nethermind input serialization to this binary (single source of +# truth, mirrors the C# InputSerializer the guest expects). Published as +# self-contained linux-x64 framework-bundled directory: the launcher script +# in ${OUTPUT_DIR}/${INPUT_GEN_NAME} just shells through to it. +INPUT_GEN_DIR="${NETHERMIND_SRC}/tools/StatelessInputGen" +INPUT_GEN_NAME="stateless-input-gen" +if [[ -d "${INPUT_GEN_DIR}" ]]; then + log "Publishing StatelessInputGen (self-contained linux-x64)" + dotnet publish -c release -r linux-x64 --self-contained true \ + -o "${OUTPUT_DIR}/stateless-input-gen-bin" \ + "${INPUT_GEN_DIR}/StatelessInputGen.csproj" >/dev/null + cat > "${OUTPUT_DIR}/${INPUT_GEN_NAME}" < { println!("cargo:rustc-env=ERE_GUESTS_DOWNLOAD_KIND=tag"); @@ -73,39 +50,45 @@ fn main() { println!("cargo:rustc-env=ERE_GUESTS_DOWNLOAD_VALUE=unknown"); } } -} - -/// Finds the EL version by tracing the ere-guests dependency chain: -/// 1. Locate `guest_pkg_name` (e.g. `stateless-validator-reth`) in the lockfile. -/// 2. Find the first dependency whose name starts with `dep_prefix` (e.g. `reth-`). -/// 3. Look up that dependency's `[[package]]` entry and extract the tag or short -/// commit hash from its `source` field. -fn find_el_version( - packages: &[toml::Value], - guest_pkg_name: &str, - dep_prefix: &str, -) -> Option { - // Step 1: find the guest package and its dependency list. - let guest_pkg = packages - .iter() - .find(|p| p.get("name").and_then(|n| n.as_str()) == Some(guest_pkg_name))?; - - let deps = guest_pkg.get("dependencies").and_then(|d| d.as_array())?; - // Step 2: pick the first dependency that matches the EL prefix. - let el_dep_name = deps - .iter() - .filter_map(|d| d.as_str()) - .find(|name| name.starts_with(dep_prefix))?; + let (zilkworm_tag, zilkworm_sha, zilkworm_repo_api_url) = find_zilkworm_git_ref(packages); + println!("cargo:rustc-env=ZILKWORM_GUEST_TAG={zilkworm_tag}"); + println!("cargo:rustc-env=ZILKWORM_GUEST_SHA={zilkworm_sha}"); + println!("cargo:rustc-env=ZILKWORM_GUEST_REPO_API_URL={zilkworm_repo_api_url}"); + + let zilkworm_version = if !zilkworm_tag.is_empty() { + zilkworm_tag + } else if !zilkworm_sha.is_empty() { + zilkworm_sha[..7.min(zilkworm_sha.len())].to_string() + } else { + "local".to_string() + }; + println!("cargo:rustc-env=ZILKWORM_EL_VERSION={zilkworm_version}"); +} - // Step 3: look up that dependency's source. - let el_pkg = packages +fn find_zilkworm_git_ref(packages: &[toml::Value]) -> (String, String, String) { + let Some(pkg) = packages .iter() - .find(|p| p.get("name").and_then(|n| n.as_str()) == Some(el_dep_name))?; - - let source = el_pkg.get("source").and_then(|s| s.as_str())?; + .find(|p| p.get("name").and_then(|n| n.as_str()) == Some("z6m_stateless_validator")) + else { + return (String::new(), String::new(), String::new()); + }; + let Some(source) = pkg.get("source").and_then(|s| s.as_str()) else { + return (String::new(), String::new(), String::new()); + }; + let tag = extract_tag_from_source(source).unwrap_or_default(); + let sha = extract_hash_from_source(source) + .unwrap_or_default() + .to_string(); + let api_url = extract_repo_api_url_from_source(source).unwrap_or_default(); + (tag, sha, api_url) +} - extract_tag_from_source(source).or_else(|| extract_short_hash_from_source(source)) +fn extract_repo_api_url_from_source(source: &str) -> Option { + let url = source.strip_prefix("git+")?; + let url = url.split(['?', '#']).next()?; + let path = url.strip_prefix("https://github.com/")?; + Some(format!("https://api.github.com/repos/{path}")) } /// Extracts the git tag from a Cargo.lock source string. @@ -115,13 +98,6 @@ fn extract_tag_from_source(source: &str) -> Option { extract_query_param_from_source(source, "tag") } -/// Extracts the short (7-char) commit hash from a Cargo.lock source string. -/// -/// Source format: `git+https://github.com/.../repo?rev=abc123#abc123def456...` -fn extract_short_hash_from_source(source: &str) -> Option { - extract_hash_from_source(source).map(|hash| hash[..7.min(hash.len())].to_string()) -} - fn find_ere_guests_download_source(packages: &[toml::Value]) -> Option { let source = packages.iter().find_map(|package| { let name = package.get("name").and_then(|n| n.as_str())?; diff --git a/crates/benchmark-runner/src/empty_program.rs b/crates/benchmark-runner/src/empty_program.rs deleted file mode 100644 index 475f0747..00000000 --- a/crates/benchmark-runner/src/empty_program.rs +++ /dev/null @@ -1,15 +0,0 @@ -//! Empty program guest program. - -use crate::guest_programs::{GenericGuestFixture, GuestFixture}; -use ere_dockerized::Input; - -/// Generate inputs for the empty program guest program. -pub fn empty_program_input() -> anyhow::Result> { - let fixture = GenericGuestFixture { - name: "empty_program".to_owned(), - input: Input::new(), - expected_public_values: Vec::new(), - metadata: (), - }; - Ok(Box::new(fixture)) -} diff --git a/crates/benchmark-runner/src/guest_programs.rs b/crates/benchmark-runner/src/guest_programs.rs index ff32e735..165638cc 100644 --- a/crates/benchmark-runner/src/guest_programs.rs +++ b/crates/benchmark-runner/src/guest_programs.rs @@ -1,9 +1,7 @@ //! Guest program input generation and metadata types -use ere_dockerized::Input; -use ere_guests_guest::codec::Encode; +use ere_dockerized::{zkVMKind, Input}; use serde::Serialize; -use sha2::{Digest, Sha256}; use std::fmt::Debug; /// Trait for a guest program fixture with associated metadata. @@ -21,6 +19,14 @@ pub trait GuestFixture: Sync + Send { /// Returns the expected public values of guest program fixture. fn expected_public_values(&self) -> anyhow::Result>; + /// Returns the expected public values normalized for the selected zkVM. + fn expected_public_values_for_zkvm(&self, zkvm_kind: zkVMKind) -> anyhow::Result> { + Ok(normalize_expected_public_values( + zkvm_kind, + self.expected_public_values()?, + )) + } + /// Verifies that the provided `public_values` match the expected output. fn verify_public_values(&self, public_values: &[u8]) -> anyhow::Result { let expected_public_values = self.expected_public_values()?; @@ -51,33 +57,6 @@ impl GenericGuestFixture where M: 'static + Send + Sync + Serialize, { - /// Creates a new [`GenericGuestFixture`] from a guest input, output, and metadata. - pub fn new( - name: impl AsRef, - input: ere_guests_guest::GuestInput, - output: ere_guests_guest::GuestOutput, - metadata: M, - ) -> anyhow::Result { - Ok(Self { - name: name.as_ref().to_string(), - input: Input::new().with_stdin( - input - .encode_to_vec() - .map_err(|e| anyhow::anyhow!("Failed to serialize guest input: {}", e))?, - ), - expected_public_values: output - .encode_to_vec() - .map_err(|e| anyhow::anyhow!("Failed to serialize guest output: {}", e))?, - metadata, - }) - } - - /// Consumes the [`GenericGuestFixture`] and constructs a new one with sha256 output. - pub fn output_sha256(mut self) -> Self { - self.expected_public_values = Sha256::digest(self.expected_public_values).to_vec(); - self - } - /// Consumes the [`GenericGuestFixture`] and returns it as a boxed trait object. pub fn into_boxed(self) -> Box { Box::new(self) @@ -113,3 +92,20 @@ pub enum OutputVerifierResult { /// Output does not match the expected result Mismatch(String), } + +fn normalize_expected_public_values( + zkvm_kind: zkVMKind, + mut expected_public_values: Vec, +) -> Vec { + if matches!(zkvm_kind, zkVMKind::Airbender | zkVMKind::OpenVM) + && expected_public_values.len() < 32 + { + expected_public_values.resize(32, 0); + } + + if matches!(zkvm_kind, zkVMKind::Zisk) && expected_public_values.len() < 256 { + expected_public_values.resize(256, 0); + } + + expected_public_values +} diff --git a/crates/benchmark-runner/src/lib.rs b/crates/benchmark-runner/src/lib.rs index 834c69ce..1d6cc434 100644 --- a/crates/benchmark-runner/src/lib.rs +++ b/crates/benchmark-runner/src/lib.rs @@ -4,7 +4,6 @@ pub mod guest_programs; -pub mod empty_program; pub mod stateless_validator; pub mod zisk_profiling; diff --git a/crates/benchmark-runner/src/runner.rs b/crates/benchmark-runner/src/runner.rs index b9041199..a6e96a00 100644 --- a/crates/benchmark-runner/src/runner.rs +++ b/crates/benchmark-runner/src/runner.rs @@ -12,7 +12,9 @@ use ere_util_tokio::block_on; use rayon::iter::{ParallelBridge, ParallelIterator}; use std::fs; use std::path::{Path, PathBuf}; +use std::time::Duration; use std::{any::Any, env, panic}; +use tokio::time::Instant; use tracing::{info, warn}; use zkevm_metrics::{BenchmarkRun, CrashInfo, ExecutionMetrics, HardwareInfo, ProvingMetrics}; @@ -28,6 +30,32 @@ const ERE_GUESTS_DOWNLOAD_KIND: &str = env!("ERE_GUESTS_DOWNLOAD_KIND"); /// Tag or commit SHA matching [`ERE_GUESTS_DOWNLOAD_KIND`]. const ERE_GUESTS_DOWNLOAD_VALUE: &str = env!("ERE_GUESTS_DOWNLOAD_VALUE"); +/// Source used to resolve compiled guest programs. +#[derive(Debug, Clone)] +pub enum GuestProgramSource { + /// Resolve guest programs from the configured ere-guests dependency. + Default, + /// Resolve guest programs from a local directory. + LocalPath(PathBuf), + /// Resolve guest programs from a remote base URL. + ArtifactBaseUrl(String), +} + +impl GuestProgramSource { + /// Returns a stable label for externally supplied guest artifacts. + pub fn version_label(&self) -> Option { + match self { + Self::Default => None, + Self::LocalPath(path) => path + .file_name() + .and_then(|name| name.to_str()) + .filter(|name| !name.is_empty()) + .map(std::string::ToString::to_string), + Self::ArtifactBaseUrl(url) => artifact_base_url_label(url), + } + } +} + /// A zkVM instance bundled with ELF bytes (used for profiling). pub enum ZkVMInstance { /// Dockerized zkVM instance @@ -42,6 +70,9 @@ pub enum ZkVMInstance { ZiskClusterClient { /// gRPC client connected to the remote Zisk cluster. client: ZiskClusterClient, + /// Per-request prove timeout, propagated from the `DockerizedzkVMConfig` + /// prove timeout. Defaults to 3 minutes. + prove_timeout: Duration, /// ELF of Zisk guest with feature `cycle-scope` enabled. /// `Some` only if the guest is a Zisk guest. profiling_elf: Option, @@ -98,8 +129,13 @@ impl ZkVMInstance { ) -> Result<(PublicValues, EncodedProof, ProgramProvingReport)> { match self { Self::Dockerized { zkvm, .. } => zkvm.prove(input), - Self::ZiskClusterClient { client, .. } => { - let (proof, proving_time) = block_on(client.prove(input, None))?; + Self::ZiskClusterClient { + client, + prove_timeout, + .. + } => { + let deadline = Instant::now() + *prove_timeout; + let (proof, proving_time) = block_on(client.prove(input, deadline))?; let (_, public_values) = proof.program_vk_and_public_values()?; let proof = proof.encode_to_vec()?; Ok(( @@ -384,10 +420,17 @@ pub async fn get_el_zkvm_instances( zkvms: &[zkVMKind], resource: ProverResource, zkvm_config: DockerizedzkVMConfig, - bin_path: Option<&Path>, + guest_source: &GuestProgramSource, ) -> Result> { let guest_name_prefix = format!("stateless-validator-{el}"); - get_guest_zkvm_instances(&guest_name_prefix, zkvms, resource, zkvm_config, bin_path).await + get_guest_zkvm_instances( + &guest_name_prefix, + zkvms, + resource, + zkvm_config, + guest_source, + ) + .await } /// Creates the requested guest program zkVMs ere instances. @@ -396,12 +439,12 @@ pub async fn get_guest_zkvm_instances( zkvms: &[zkVMKind], resource: ProverResource, zkvm_config: DockerizedzkVMConfig, - bin_path: Option<&Path>, + guest_source: &GuestProgramSource, ) -> Result> { let mut instances = Vec::new(); for zkvm in zkvms { let guest_name = format!("{}-{}", guest_name_prefix, zkvm.as_str()); - let compiled = load_compiled(&guest_name, bin_path).await?; + let compiled = load_compiled(&guest_name, guest_source).await?; let instance = match &resource { ProverResource::Cpu | ProverResource::Gpu => { let zkvm = DockerizedzkVM::new( @@ -417,11 +460,14 @@ pub async fn get_guest_zkvm_instances( } } ProverResource::Cluster(cfg) if *zkvm == zkVMKind::Zisk => { + const DEFAULT_PROVE_TIMEOUT: Duration = Duration::from_mins(3); + let client = ZiskClusterClient::new(cfg, Elf(compiled.elf.clone())) .await .map_err(|e| anyhow!("Failed to connect to Zisk cluster: {e}"))?; ZkVMInstance::ZiskClusterClient { client, + prove_timeout: zkvm_config.prove_timeout.unwrap_or(DEFAULT_PROVE_TIMEOUT), profiling_elf: compiled.profiling_elf.map(Elf), } } @@ -435,12 +481,16 @@ pub async fn get_guest_zkvm_instances( Ok(instances) } -async fn load_compiled(guest_name: &str, bin_path: Option<&Path>) -> Result { - if let Some(path) = bin_path { +async fn load_compiled( + guest_name: &str, + guest_source: &GuestProgramSource, +) -> Result { + if let GuestProgramSource::LocalPath(path) = guest_source { let elf = fs::read(path.join(format!("{guest_name}.elf"))) .with_context(|| format!("Failed to read ELF from path: {}", path.display()))?; - let program_vk = fs::read(path.join(format!("{guest_name}.vk"))) - .with_context(|| format!("Failed to read program vk from path: {}", path.display()))?; + // .vk is optional: the Zisk backend derives the verifying key from the ELF itself, and + // externally built guests (e.g. Nethermind via build-nethermind-guest.sh) ship no sidecar. + let program_vk = fs::read(path.join(format!("{guest_name}.vk"))).unwrap_or_default(); let profiling_elf = fs::read(path.join(format!("{guest_name}-profiling.elf"))).ok(); return Ok(CompiledGuest { elf, @@ -449,6 +499,34 @@ async fn load_compiled(guest_name: &str, bin_path: Option<&Path>) -> Result Result { } } +async fn load_compiled_from_artifact_base_url( + guest_name: &str, + base_url: &str, +) -> Result { + let client = reqwest::Client::new(); + let elf_url = guest_artifact_url(base_url, &format!("{guest_name}.elf")); + let vk_url = guest_artifact_url(base_url, &format!("{guest_name}.vk")); + let profiling_url = guest_artifact_url(base_url, &format!("{guest_name}-profiling.elf")); + + info!("Downloading guest program from {elf_url}"); + let elf = download_required_artifact(&client, &elf_url).await?; + let program_vk = download_optional_artifact(&client, &vk_url) + .await? + .unwrap_or_default(); + let profiling_elf = download_optional_artifact(&client, &profiling_url).await?; + + Ok(CompiledGuest { + elf, + program_vk, + profiling_elf, + }) +} + +async fn download_required_artifact(client: &reqwest::Client, url: &str) -> Result> { + let response = client + .get(url) + .send() + .await + .with_context(|| format!("Failed to download required guest artifact from {url}"))?; + let status = response.status(); + if !status.is_success() { + bail!("Failed to download required guest artifact from {url}: HTTP {status}"); + } + Ok(response + .bytes() + .await + .with_context(|| format!("Failed to read required guest artifact from {url}"))? + .to_vec()) +} + +async fn download_optional_artifact( + client: &reqwest::Client, + url: &str, +) -> Result>> { + let response = match client.get(url).send().await { + Ok(response) => response, + Err(err) => { + warn!("Skipping optional guest artifact {url}: {err}"); + return Ok(None); + } + }; + let status = response.status(); + if !status.is_success() { + info!("Skipping optional guest artifact {url}: HTTP {status}"); + return Ok(None); + } + Ok(Some( + response + .bytes() + .await + .with_context(|| format!("Failed to read optional guest artifact from {url}"))? + .to_vec(), + )) +} + +fn guest_artifact_url(base_url: &str, filename: &str) -> String { + format!("{}/{}", base_url.trim_end_matches('/'), filename) +} + +fn artifact_base_url_label(base_url: &str) -> Option { + base_url + .split(['?', '#']) + .next() + .unwrap_or(base_url) + .trim_end_matches('/') + .rsplit('/') + .next() + .filter(|label| !label.is_empty()) + .map(std::string::ToString::to_string) +} + /// Dumps the raw input bytes to disk fn dump_input( input: &[u8], @@ -553,8 +712,7 @@ fn public_output_matched( io: &impl GuestFixture, public_values: &[u8], ) -> Result { - let expected_public_values = - normalize_expected_public_values(zkvm_kind, io.expected_public_values()?); + let expected_public_values = io.expected_public_values_for_zkvm(zkvm_kind)?; if expected_public_values == public_values { Ok(true) @@ -569,23 +727,6 @@ fn public_output_matched( } } -fn normalize_expected_public_values( - zkvm_kind: zkVMKind, - mut expected_public_values: Vec, -) -> Vec { - if matches!(zkvm_kind, zkVMKind::Airbender | zkVMKind::OpenVM) - && expected_public_values.len() < 32 - { - expected_public_values.resize(32, 0); - } - - if matches!(zkvm_kind, zkVMKind::Zisk) && expected_public_values.len() < 256 { - expected_public_values.resize(256, 0); - } - - expected_public_values -} - #[cfg(test)] mod tests { use super::*; @@ -673,4 +814,134 @@ mod tests { Ok(()) } + + #[test] + fn public_output_matched_requires_zisk_padding_normalization() -> Result<()> { + let fixture = Fixture::new(vec![0xab]); + let mut zisk_public_values = vec![0xab]; + zisk_public_values.resize(256, 0); + + assert!(!public_output_matched(zkVMKind::Zisk, &fixture, &[0xab])?); + assert!(public_output_matched( + zkVMKind::Zisk, + &fixture, + &zisk_public_values, + )?); + + Ok(()) + } + + #[test] + fn guest_artifact_url_joins_base_and_filename() { + assert_eq!( + guest_artifact_url( + "https://github.com/Consensys/zesu-zkvm/releases/download/bal-devnet-7-2026-06-12/", + "stateless-validator-zesu-zisk.elf", + ), + "https://github.com/Consensys/zesu-zkvm/releases/download/bal-devnet-7-2026-06-12/stateless-validator-zesu-zisk.elf" + ); + } + + #[test] + fn artifact_base_url_label_uses_last_path_segment() { + assert_eq!( + artifact_base_url_label( + "https://github.com/Consensys/zesu-zkvm/releases/download/bal-devnet-7-2026-06-12/" + ) + .as_deref(), + Some("bal-devnet-7-2026-06-12") + ); + } + + #[test] + fn url_artifact_loader_requires_elf_but_not_vk_or_profiling_elf() -> Result<()> { + let server = TestServer::spawn(|path| { + (path == "/stateless-validator-zesu-zisk.elf").then(|| Vec::from("elf-bytes")) + }); + + let compiled = block_on(load_compiled( + "stateless-validator-zesu-zisk", + &GuestProgramSource::ArtifactBaseUrl(server.base_url()), + ))?; + + assert_eq!(compiled.elf, b"elf-bytes"); + assert!(compiled.program_vk.is_empty()); + assert!(compiled.profiling_elf.is_none()); + + Ok(()) + } + + #[test] + fn url_artifact_loader_fails_when_elf_is_missing() { + let server = TestServer::spawn(|_| None); + + let err = block_on(load_compiled( + "stateless-validator-zesu-zisk", + &GuestProgramSource::ArtifactBaseUrl(server.base_url()), + )) + .unwrap_err(); + + assert!(err + .to_string() + .contains("stateless-validator-zesu-zisk.elf")); + } + + struct TestServer { + base_url: String, + } + + impl TestServer { + fn spawn(handler: F) -> Self + where + F: Fn(&str) -> Option> + Send + Sync + 'static, + { + use std::{ + io::{Read, Write}, + net::TcpListener, + sync::Arc, + thread, + }; + + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let base_url = format!("http://{}", listener.local_addr().unwrap()); + let handler = Arc::new(handler); + + thread::spawn(move || { + for stream in listener.incoming().take(3) { + let Ok(mut stream) = stream else { + continue; + }; + let mut request = [0_u8; 1024]; + let Ok(read) = stream.read(&mut request) else { + continue; + }; + let request = String::from_utf8_lossy(&request[..read]); + let path = request + .lines() + .next() + .and_then(|line| line.split_whitespace().nth(1)) + .unwrap_or("/"); + + if let Some(body) = handler(path) { + let response = format!( + "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n", + body.len() + ); + let _ = stream.write_all(response.as_bytes()); + let _ = stream.write_all(&body); + } else { + let _ = stream.write_all( + b"HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n", + ); + } + } + }); + + Self { base_url } + } + + fn base_url(&self) -> String { + self.base_url.clone() + } + } } diff --git a/crates/benchmark-runner/src/stateless_validator.rs b/crates/benchmark-runner/src/stateless_validator.rs index d34972aa..44fa4af6 100644 --- a/crates/benchmark-runner/src/stateless_validator.rs +++ b/crates/benchmark-runner/src/stateless_validator.rs @@ -20,6 +20,12 @@ pub enum ExecutionClient { Reth, /// Ethrex stateless block validation guest program. Ethrex, + /// Zilkworm stateless block validation guest program. + Zilkworm, + /// Zesu stateless block validation guest program. + Zesu, + /// Nethermind stateless block validation guest program (C#, Zisk-only, externally built). + Nethermind, } /// Extra information about the block being benchmarked @@ -29,13 +35,22 @@ pub struct BlockMetadata { pub block_used_gas: u64, } +/// Reth version used to build the Reth artifacts in ere-guests v0.13.0. +const RETH_EL_VERSION: &str = "v2.3.0"; +/// Zesu version republished with the ere-guests v0.13.0 artifacts. +const ZESU_EL_VERSION: &str = "bal-devnet-7-2026-06-24"; + impl ExecutionClient { - /// Returns the version string of the execution client (tag or short commit hash), - /// extracted from the resolved `Cargo.lock` at build time. + /// Returns the version string associated with the selected guest artifact. pub const fn version(&self) -> &'static str { match self { - Self::Reth => env!("RETH_EL_VERSION"), - Self::Ethrex => env!("ETHREX_EL_VERSION"), + Self::Reth => RETH_EL_VERSION, + Self::Ethrex => ere_guests_stateless_validator_ethrex::EL_VERSION, + Self::Zilkworm => env!("ZILKWORM_EL_VERSION"), + Self::Zesu => ZESU_EL_VERSION, + // Externally built (build-nethermind-guest.sh); real version comes from the + // `stateless-validator-nethermind-zisk.version` sidecar, resolved in main.rs. + Self::Nethermind => "unknown", } } } diff --git a/crates/benchmark-runner/src/stateless_validator/fixtures.rs b/crates/benchmark-runner/src/stateless_validator/fixtures.rs index 50dd2bef..0c39b2f9 100644 --- a/crates/benchmark-runner/src/stateless_validator/fixtures.rs +++ b/crates/benchmark-runner/src/stateless_validator/fixtures.rs @@ -48,6 +48,7 @@ pub fn iter_benchmark_fixture_paths(path: &Path) -> impl Iterator, { paths.flat_map(move |path| { - let results: Vec<_> = match load_benchmark_fixtures(&path, &input_root) { + let results: Vec<_> = match load_benchmark_fixtures(&path, &input_root, el) { Ok(fixtures) => fixtures .into_iter() .filter(|fixture| fixture_matches_prefixes(fixture, fixture_prefixes.as_deref())) @@ -182,12 +183,21 @@ fn fixture_matches_prefixes(fixture: &BenchmarkFixture, prefixes: Option<&[Strin }) } -fn load_benchmark_fixtures(path: &Path, input_root: &Path) -> Result> { +fn load_benchmark_fixtures( + path: &Path, + input_root: &Path, + el: ExecutionClient, +) -> Result> { let content = std::fs::read(path)?; let value: serde_json::Value = serde_json::from_slice(&content) .with_context(|| format!("Failed to parse {}", path.display()))?; if value.get("stateless_input").is_some() { + if !matches!(el, ExecutionClient::Zilkworm) { + bail!( + "{el:?} supports only canonical blockchain_tests fixtures with statelessInputBytes/statelessOutputBytes; legacy stateless_input fixtures are supported only for Zilkworm" + ); + } let fixture = serde_json::from_value(value) .with_context(|| format!("Failed to parse legacy fixture {}", path.display()))?; return Ok(vec![BenchmarkFixture::Legacy(Box::new(fixture))]); @@ -243,15 +253,32 @@ fn normalize_fixture_prefix(prefix: &str) -> Result { #[cfg(test)] mod tests { use super::*; - use sha2::{Digest, Sha256}; use std::fs; + #[test] + fn fixture_iter_yields_sorted_paths() -> Result<()> { + let dir = tempfile::tempdir()?; + + let paths = (24950000..) + .take(100) + .map(|block_number| dir.path().join(format!("{block_number}.json"))) + .collect::>(); + + for path in &paths { + fs::File::create(path)?; + } + + assert_eq!(paths, benchmark_fixture_paths(dir.path())?); + + Ok(()) + } + #[test] fn fixture_prefix_matching_accepts_safe_and_original_eest_names() -> Result<()> { let dir = tempfile::tempdir()?; let fixture_path = dir.path().join("mcopy.json"); fs::write(&fixture_path, sample_eest_fixture())?; - let fixtures = load_benchmark_fixtures(&fixture_path, dir.path())?; + let fixtures = load_benchmark_fixtures(&fixture_path, dir.path(), ExecutionClient::Reth)?; let fixture = fixtures .iter() .find(|fixture| { @@ -276,7 +303,7 @@ mod tests { } #[test] - fn eest_fixture_iter_yields_raw_input_and_hashed_output() -> Result<()> { + fn eest_fixture_iter_yields_raw_input_and_output() -> Result<()> { let dir = tempfile::tempdir()?; let fixture_path = dir.path().join("mcopy.json"); fs::write(&fixture_path, sample_eest_fixture())?; @@ -293,10 +320,7 @@ mod tests { let input = guest_fixture.input()?; assert_eq!(input.stdin(), [0x00, 0x01, 0x02]); - assert_eq!( - guest_fixture.expected_public_values()?, - Sha256::digest([0xaa, 0xbb]).to_vec() - ); + assert_eq!(guest_fixture.expected_public_values()?, [0xaa, 0xbb]); let metadata = guest_fixture.metadata(); assert_eq!(metadata["fixture_format"], "eest"); @@ -309,6 +333,24 @@ mod tests { Ok(()) } + #[test] + fn legacy_fixtures_are_rejected_for_canonical_clients() -> Result<()> { + let dir = tempfile::tempdir()?; + let fixture_path = dir.path().join("legacy.json"); + fs::write(&fixture_path, r#"{"stateless_input": {}}"#)?; + + for client in [ + ExecutionClient::Reth, + ExecutionClient::Ethrex, + ExecutionClient::Zesu, + ] { + let err = load_benchmark_fixtures(&fixture_path, dir.path(), client).unwrap_err(); + assert!(err.to_string().contains("supported only for Zilkworm")); + } + + Ok(()) + } + #[test] fn fixture_path_discovery_skips_meta_and_non_json_files() -> Result<()> { let dir = tempfile::tempdir()?; diff --git a/crates/benchmark-runner/src/stateless_validator/inputs.rs b/crates/benchmark-runner/src/stateless_validator/inputs.rs index fbcce1c0..267903ad 100644 --- a/crates/benchmark-runner/src/stateless_validator/inputs.rs +++ b/crates/benchmark-runner/src/stateless_validator/inputs.rs @@ -4,19 +4,11 @@ use crate::{ eest::EestStatelessFixture, fixtures::BenchmarkFixture, BlockMetadata, ExecutionClient, }, }; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use ere_dockerized::Input; -use ere_guests_guest::Guest; -use ere_guests_integration_tests::NoopPlatform; -use ere_guests_stateless_validator_ethrex::{ - guest::StatelessValidatorEthrexGuest, - host::{build_eip8025_input, Eip8025InputSource}, -}; -use ere_guests_stateless_validator_reth::guest::{ - StatelessValidatorRethGuest, StatelessValidatorRethInput, -}; use serde::Serialize; -use sha2::{Digest, Sha256}; +use stateless_validator_common::guest::input::{ProtocolFork, StatelessInput}; +use stateless_validator_zilkworm::StatelessValidatorZilkwormInput; use tracing::info; use witness_generator::StatelessValidationFixture; @@ -38,11 +30,48 @@ pub(crate) fn stateless_validator_input_from_fixture( ) -> Result> { match fixture { BenchmarkFixture::Legacy(fixture) => match el { - ExecutionClient::Reth => reth_input_from_fixture(*fixture), - ExecutionClient::Ethrex => ethrex_input_from_fixture(*fixture), + ExecutionClient::Zilkworm => zilkworm_input_from_fixture(*fixture), + ExecutionClient::Reth + | ExecutionClient::Ethrex + | ExecutionClient::Zesu + | ExecutionClient::Nethermind => { + bail!( + "{el:?} supports only canonical blockchain_tests fixtures with statelessInputBytes/statelessOutputBytes; legacy stateless_input fixtures are supported only for Zilkworm" + ) + } }, - BenchmarkFixture::Eest(fixture) => raw_eest_input_from_fixture(fixture), + BenchmarkFixture::Eest(fixture) => match el { + // Nethermind's C# guest reads the canonical statelessInputBytes and returns the raw + // StatelessValidationResult, compared byte-for-byte against statelessOutputBytes. + ExecutionClient::Reth | ExecutionClient::Ethrex | ExecutionClient::Nethermind => { + raw_eest_input_from_fixture(fixture) + } + ExecutionClient::Zesu => zesu_input_from_fixture(fixture), + ExecutionClient::Zilkworm => { + bail!("EEST fixture format not yet supported for Zilkworm") + } + }, + } +} + +fn zesu_input_from_fixture(fixture: EestStatelessFixture) -> Result> { + let input = StatelessInput::from_schema_prefixed_ssz(&fixture.stateless_input_bytes) + .with_context(|| { + format!( + "failed to decode canonical stateless input for Zesu fixture {}", + fixture.name + ) + })?; + let fork = input.chain_config.active_fork.fork; + if fork != ProtocolFork::Amsterdam { + bail!( + "Zesu {} supports only Glamsterdam inputs (ProtocolFork::Amsterdam), but fixture {} targets {fork:?}", + ExecutionClient::Zesu.version(), + fixture.name + ); } + + raw_eest_input_from_fixture(fixture) } fn raw_eest_input_from_fixture(fixture: EestStatelessFixture) -> Result> { @@ -56,65 +85,63 @@ fn raw_eest_input_from_fixture(fixture: EestStatelessFixture) -> Result { + let fixture = GenericGuestFixture:: { name: fixture.name, input: Input::new().with_stdin(fixture.stateless_input_bytes), - expected_public_values, + expected_public_values: fixture.stateless_output_bytes, metadata, - } - .into_boxed()) + }; + + Ok(fixture.into_boxed()) } -fn ethrex_input_from_fixture(fixture: StatelessValidationFixture) -> Result> { +fn zilkworm_input_from_fixture( + fixture: StatelessValidationFixture, +) -> Result> { let StatelessValidationFixture { name, stateless_input, success, } = fixture; - let input = build_eip8025_input(Eip8025InputSource::Legacy { - stateless_input: &stateless_input, - valid_block: success, - }) - .context("Failed to create Ethrex stateless validator input")?; - let output = StatelessValidatorEthrexGuest::compute::(input.clone()); + info!("Preparing Zilkworm stateless validator input for fixture {name}"); + let zilkworm_input = StatelessValidatorZilkwormInput::new(&stateless_input, success) + .with_context(|| format!("building Zilkworm input for {name}"))?; let metadata = BlockMetadata { block_used_gas: stateless_input.block.gas_used, }; - Ok( - GenericGuestFixture::::new::( - name, input, output, metadata, - )? - .output_sha256() - .into_boxed(), - ) + Ok(Box::new(ZilkwormGuestFixture { + name, + unified_rlp: zilkworm_input.unified_rlp, + metadata, + })) } -fn reth_input_from_fixture(fixture: StatelessValidationFixture) -> Result> { - let StatelessValidationFixture { - name, - stateless_input, - success, - } = fixture; - info!( - "Preparing Reth stateless validator input for fixture {}", - name - ); - let input = StatelessValidatorRethInput::new(&stateless_input, success) - .context("Failed to create Reth stateless validator input")?; +#[derive(Debug)] +struct ZilkwormGuestFixture { + name: String, + unified_rlp: Vec, + metadata: BlockMetadata, +} - let output = StatelessValidatorRethGuest::compute::(input.clone()); - let metadata = BlockMetadata { - block_used_gas: stateless_input.block.gas_used, - }; +impl GuestFixture for ZilkwormGuestFixture { + fn name(&self) -> String { + self.name.clone() + } + + fn metadata(&self) -> serde_json::Value { + serde_json::to_value(&self.metadata).unwrap() + } - Ok( - GenericGuestFixture::::new::( - name, input, output, metadata, - )? - .output_sha256() - .into_boxed(), - ) + fn input(&self) -> Result { + let bundle = stateless_validator_zilkworm::encode_unified_rlp_bundle(&[&self.unified_rlp]); + Ok(Input::new().with_stdin(bundle)) + } + + fn expected_public_values(&self) -> Result> { + let mut out = Vec::with_capacity(4 + 8); + out.extend_from_slice(&1u32.to_le_bytes()); + out.extend_from_slice(&self.metadata.block_used_gas.to_le_bytes()); + Ok(out) + } } diff --git a/crates/ere-hosts/src/cli.rs b/crates/ere-hosts/src/cli.rs index e8cc4660..ffd38285 100644 --- a/crates/ere-hosts/src/cli.rs +++ b/crates/ere-hosts/src/cli.rs @@ -63,9 +63,13 @@ pub struct Cli { /// Base path for pre-compiled guest program binaries. If not set, they will be downloaded /// from the resolved ere-guests release or commit artifacts. - #[arg(long)] + #[arg(long, conflicts_with = "guest_artifact_base_url")] pub bin_path: Option, + /// Base URL for pre-compiled guest program artifacts. + #[arg(long, conflicts_with = "bin_path")] + pub guest_artifact_base_url: Option, + /// Timeout for the selected action only, for example `15m`, `5m`, or `2s`. #[arg(long, value_name = "DURATION", value_parser = parse_duration)] pub timeout: Option, @@ -94,8 +98,6 @@ pub enum GuestProgramCommand { #[arg(short, long)] execution_client: ExecutionClient, }, - /// Empty program - EmptyProgram, } /// Execution clients for the stateless validator @@ -105,6 +107,14 @@ pub enum ExecutionClient { Reth, /// Ethrex execution client Ethrex, + /// Zilkworm execution client + Zilkworm, + /// Zesu execution client + Zesu, + /// Nethermind execution client (C#, Zisk-only, externally built — requires + /// `--bin-path` pointing at a directory with + /// `stateless-validator-nethermind-zisk.elf`). + Nethermind, } impl ExecutionClient { @@ -113,6 +123,9 @@ impl ExecutionClient { let path = match self { Self::Reth => "stateless-validator/reth", Self::Ethrex => "stateless-validator/ethrex", + Self::Zilkworm => "stateless-validator/zilkworm", + Self::Zesu => "stateless-validator/zesu", + Self::Nethermind => "stateless-validator/nethermind", }; PathBuf::from(path) } @@ -176,6 +189,9 @@ impl From for stateless_validator::ExecutionClient { match client { ExecutionClient::Reth => Self::Reth, ExecutionClient::Ethrex => Self::Ethrex, + ExecutionClient::Zilkworm => Self::Zilkworm, + ExecutionClient::Zesu => Self::Zesu, + ExecutionClient::Nethermind => Self::Nethermind, } } } diff --git a/crates/ere-hosts/src/main.rs b/crates/ere-hosts/src/main.rs index d81eeb69..0d3e3c62 100644 --- a/crates/ere-hosts/src/main.rs +++ b/crates/ere-hosts/src/main.rs @@ -4,10 +4,9 @@ use anyhow::{Context, Result, bail}; use benchmark_runner::{ - empty_program, runner::{ - Action, ProfileConfig, RunConfig, benchmark_output_dir, get_el_zkvm_instances, - get_guest_zkvm_instances, run_benchmark_iter, + Action, GuestProgramSource, ProfileConfig, RunConfig, benchmark_output_dir, + get_el_zkvm_instances, run_benchmark_iter, }, stateless_validator::{self}, verification::{download_and_extract_proofs, resolve_extracted_root, run_verify_from_disk}, @@ -97,7 +96,12 @@ async fn main() -> Result<()> { } else { (None, cli.proofs_folder) }; - let bin_path = cli.bin_path.as_deref(); + let guest_source = match (cli.bin_path, cli.guest_artifact_base_url) { + (Some(path), None) => GuestProgramSource::LocalPath(path), + (None, Some(url)) => GuestProgramSource::ArtifactBaseUrl(url), + (None, None) => GuestProgramSource::Default, + (Some(_), Some(_)) => unreachable!("clap conflicts_with should reject this combination"), + }; let config_base = RunConfig { output_folder: cli.output_folder, sub_folder: None, @@ -115,15 +119,38 @@ async fn main() -> Result<()> { execution_client, } => { let el: stateless_validator::ExecutionClient = execution_client.into(); + validate_guest_compatibility(el, &cli.zkvms, &guest_source)?; let el_name = el.as_ref().to_lowercase(); - let el_str = format!("{}-{}", el_name, el.version()); + let el_version = match el { + // Externally built (build-nethermind-guest.sh): prefer the git sha recorded in + // the sidecar next to the ELF, then the dir name. + stateless_validator::ExecutionClient::Nethermind => { + if let GuestProgramSource::LocalPath(path) = &guest_source { + std::fs::read_to_string( + path.join("stateless-validator-nethermind-zisk.version"), + ) + .ok() + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + } else { + None + } + .or_else(|| guest_source.version_label()) + .unwrap_or_else(|| el.version().to_string()) + } + stateless_validator::ExecutionClient::Zesu => guest_source + .version_label() + .unwrap_or_else(|| el.version().to_string()), + _ => el.version().to_string(), + }; + let el_str = format!("{}-{}", el_name, el_version); let zkvms = get_el_zkvm_instances( &el_name, &cli.zkvms, resource, zkvm_config.clone(), - bin_path, + &guest_source, ) .await .context("Failed to get EL zkvm instances")?; @@ -159,33 +186,49 @@ async fn main() -> Result<()> { } } } - GuestProgramCommand::EmptyProgram => { - info!("Running empty-program benchmarks"); - let zkvms = get_guest_zkvm_instances( - "empty", - &cli.zkvms, - resource, - zkvm_config.clone(), - bin_path, - ) - .await - .context("Failed to get guest zkvm instances")?; + } - match action { - Action::Verify => { - for instance in &zkvms { - run_verify_from_disk(instance, &config_base, &proofs_folder)?; - } - } - _ => { - for zkvm in zkvms { - let guest_io = empty_program::empty_program_input() - .context("Failed to create empty program input")?; - run_benchmark_iter(&zkvm, &config_base, std::iter::once(Ok(guest_io)))?; - } - } - } + Ok(()) +} + +fn validate_guest_compatibility( + el: stateless_validator::ExecutionClient, + zkvms: &[zkVMKind], + guest_source: &GuestProgramSource, +) -> Result<()> { + // Nethermind's guest is a C#/.NET program that only targets ZisK. The default downloader + // resolves artifacts from eth-act/ere-guests, which does not publish it, so the guest must be + // supplied explicitly — either locally built (build-nethermind-guest.sh) or from a release. + if matches!(el, stateless_validator::ExecutionClient::Nethermind) { + if zkvms.iter().any(|zkvm| *zkvm != zkVMKind::Zisk) { + bail!("--execution-client nethermind requires --zkvms zisk"); } + if matches!(guest_source, GuestProgramSource::Default) { + bail!( + "--execution-client nethermind requires --bin-path or --guest-artifact-base-url \ + (e.g. https://github.com/NethermindEth/ere-guests/releases/download/v0.14.2/)" + ); + } + return Ok(()); + } + + if !matches!(el, stateless_validator::ExecutionClient::Zesu) + || !matches!(guest_source, GuestProgramSource::Default) + { + return Ok(()); + } + + let unsupported = zkvms + .iter() + .filter(|zkvm| **zkvm != zkVMKind::Zisk) + .map(|zkvm| zkvm.as_str()) + .collect::>(); + if !unsupported.is_empty() { + bail!( + "the default Zesu {} artifact is available only for ZisK; unsupported --zkvms: {}", + el.version(), + unsupported.join(", ") + ); } Ok(()) diff --git a/crates/witness-generator-cli/src/main.rs b/crates/witness-generator-cli/src/main.rs index ff68fd16..0a0fb351 100644 --- a/crates/witness-generator-cli/src/main.rs +++ b/crates/witness-generator-cli/src/main.rs @@ -81,6 +81,10 @@ enum SourceCommand { /// Optional path to a geth-style genesis.json file for custom/devnet chain config #[arg(long, value_name = "PATH")] genesis: Option, + + /// Maximum number of blocks fetched concurrently while catching up, only applies to `--follow` + #[arg(long, default_value_t = 1)] + max_concurrency: usize, }, } @@ -153,8 +157,10 @@ async fn build_generator(source: SourceCommand) -> Result { - let mut builder = RpcBlocksAndWitnessesBuilder::new(rpc_url); + let mut builder = + RpcBlocksAndWitnessesBuilder::new(rpc_url).max_concurrency(max_concurrency); if let Some(rpc_header) = rpc_header { let headers = RpcFlatHeaderKeyValues::new(rpc_header) diff --git a/crates/witness-generator-spec-cli/Cargo.toml b/crates/witness-generator-spec-cli/Cargo.toml new file mode 100644 index 00000000..01faa5a5 --- /dev/null +++ b/crates/witness-generator-spec-cli/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "witness-generator-spec-cli" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true + +[lints] +workspace = true + +[[bin]] +name = "witness-generator-spec-cli" +path = "src/main.rs" + +[dependencies] +anyhow.workspace = true +clap = { workspace = true, features = ["derive"] } +humantime.workspace = true +reqwest = { workspace = true, features = ["json"] } +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +sha2.workspace = true +tar.workspace = true +time.workspace = true +tokio = { workspace = true, features = ["macros", "rt-multi-thread"] } +toml.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +zstd.workspace = true + +# alloy +alloy-consensus = { workspace = true, features = ["k256"] } +alloy-eips.workspace = true +alloy-primitives = { workspace = true, features = ["k256", "serde", "std"] } +alloy-rlp.workspace = true + +# local +stateless-validator-common.workspace = true diff --git a/crates/witness-generator-spec-cli/src/artifact.rs b/crates/witness-generator-spec-cli/src/artifact.rs new file mode 100644 index 00000000..81fbbdbe --- /dev/null +++ b/crates/witness-generator-spec-cli/src/artifact.rs @@ -0,0 +1,351 @@ +use std::{ + fs::{self, OpenOptions}, + io::{BufReader, Read, Write}, + path::{Path, PathBuf}, +}; + +use alloy_primitives::hex; +use anyhow::Context; +use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; +use time::{OffsetDateTime, format_description::well_known::Rfc3339}; +use witness_generator_spec_cli::GeneratedInput; + +const ARTIFACT_SCHEMA_VERSION: u64 = 1; +const STATELESS_INPUT_SCHEMA_ID: &str = "0x0001"; +const ZSTD_LEVEL: i32 = 3; + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct StatelessInputArtifact { + pub(crate) schema_version: u64, + pub(crate) network: String, + pub(crate) chain_id: u64, + pub(crate) block_number: u64, + pub(crate) block_hash: String, + pub(crate) slot_number: u64, + pub(crate) collection_mode: String, + pub(crate) collected_at: String, + pub(crate) generator_package: String, + pub(crate) generator_version: String, + pub(crate) generator_git_commit: String, + pub(crate) stateless_input_schema_id: String, + pub(crate) stateless_input_byte_length: usize, + pub(crate) stateless_input_sha256: String, + pub(crate) stateless_input_bytes: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct ArtifactIndexEntry { + pub(crate) schema_version: u64, + pub(crate) network: String, + pub(crate) chain_id: u64, + pub(crate) block_number: u64, + pub(crate) block_hash: String, + pub(crate) slot_number: u64, + pub(crate) collection_mode: String, + pub(crate) collected_at: String, + pub(crate) stateless_input_byte_length: usize, + pub(crate) stateless_input_sha256: String, + pub(crate) path: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct ArtifactWriteResult { + pub(crate) path: PathBuf, + pub(crate) relative_path: PathBuf, + pub(crate) created: bool, +} + +impl StatelessInputArtifact { + pub(crate) fn from_generated( + network: &str, + collection_mode: &str, + generated: &GeneratedInput, + ) -> anyhow::Result { + Self::from_generated_at( + network, + collection_mode, + generated, + &utc_now_rfc3339()?, + generator_git_commit(), + ) + } + + pub(crate) fn from_generated_at( + network: &str, + collection_mode: &str, + generated: &GeneratedInput, + collected_at: &str, + generator_git_commit: String, + ) -> anyhow::Result { + Ok(Self { + schema_version: ARTIFACT_SCHEMA_VERSION, + network: network.to_owned(), + chain_id: generated.chain_id, + block_number: generated.block_number, + block_hash: generated.block_hash.to_string(), + slot_number: generated.slot_number, + collection_mode: collection_mode.to_owned(), + collected_at: collected_at.to_owned(), + generator_package: env!("CARGO_PKG_NAME").to_owned(), + generator_version: env!("CARGO_PKG_VERSION").to_owned(), + generator_git_commit, + stateless_input_schema_id: STATELESS_INPUT_SCHEMA_ID.to_owned(), + stateless_input_byte_length: generated.bytes.len(), + stateless_input_sha256: sha256_hex(&generated.bytes), + stateless_input_bytes: format!("0x{}", hex::encode(&generated.bytes)), + }) + } + + pub(crate) fn index_entry(&self, path: &Path) -> ArtifactIndexEntry { + ArtifactIndexEntry { + schema_version: self.schema_version, + network: self.network.clone(), + chain_id: self.chain_id, + block_number: self.block_number, + block_hash: self.block_hash.clone(), + slot_number: self.slot_number, + collection_mode: self.collection_mode.clone(), + collected_at: self.collected_at.clone(), + stateless_input_byte_length: self.stateless_input_byte_length, + stateless_input_sha256: self.stateless_input_sha256.clone(), + path: path_to_slash_string(path), + } + } +} + +pub(crate) fn write_artifact_atomic( + blocks_root: &Path, + artifact: &StatelessInputArtifact, +) -> anyhow::Result { + let relative_path = relative_artifact_path(artifact); + let path = blocks_root.join(&relative_path); + if path.exists() { + return Ok(ArtifactWriteResult { + path, + relative_path, + created: false, + }); + } + + let json = serde_json::to_vec_pretty(artifact).context("failed to serialize artifact JSON")?; + let compressed = + zstd::bulk::compress(&json, ZSTD_LEVEL).context("failed to compress artifact")?; + write_bytes_atomic(&path, &compressed)?; + + Ok(ArtifactWriteResult { + path, + relative_path, + created: true, + }) +} + +pub(crate) fn read_artifact(path: &Path) -> anyhow::Result { + let file = fs::File::open(path) + .with_context(|| format!("failed to open artifact {}", path.display()))?; + let json = zstd::stream::decode_all(file) + .with_context(|| format!("failed to decompress artifact {}", path.display()))?; + serde_json::from_slice(&json) + .with_context(|| format!("failed to decode artifact JSON {}", path.display())) +} + +pub(crate) fn append_index_entry( + index_path: &Path, + entry: &ArtifactIndexEntry, +) -> anyhow::Result<()> { + if let Some(parent) = index_path.parent() { + fs::create_dir_all(parent) + .with_context(|| format!("failed to create index directory {}", parent.display()))?; + } + let mut file = OpenOptions::new() + .create(true) + .append(true) + .open(index_path) + .with_context(|| format!("failed to open index {}", index_path.display()))?; + serde_json::to_writer(&mut file, entry).context("failed to serialize index entry")?; + file.write_all(b"\n") + .context("failed to write index entry")?; + Ok(()) +} + +pub(crate) fn write_json_atomic(path: &Path, value: &T) -> anyhow::Result<()> +where + T: Serialize, +{ + let bytes = serde_json::to_vec_pretty(value).context("failed to serialize JSON")?; + write_bytes_atomic(path, &bytes) +} + +pub(crate) fn write_bytes_atomic(path: &Path, bytes: &[u8]) -> anyhow::Result<()> { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent) + .with_context(|| format!("failed to create directory {}", parent.display()))?; + } + let part_path = part_path(path); + fs::write(&part_path, bytes) + .with_context(|| format!("failed to write partial file {}", part_path.display()))?; + fs::rename(&part_path, path).with_context(|| { + format!( + "failed to atomically rename {} to {}", + part_path.display(), + path.display() + ) + })?; + Ok(()) +} + +pub(crate) fn relative_artifact_path(artifact: &StatelessInputArtifact) -> PathBuf { + let chunk = artifact.block_number / 1_000; + PathBuf::from(format!("{chunk:06}")).join(format!( + "{}-{}.json.zst", + artifact.block_number, + filename_hash(&artifact.block_hash) + )) +} + +pub(crate) fn path_to_slash_string(path: &Path) -> String { + path.components() + .map(|component| component.as_os_str().to_string_lossy()) + .collect::>() + .join("/") +} + +pub(crate) fn sha256_hex(bytes: &[u8]) -> String { + format!("0x{}", hex::encode(Sha256::digest(bytes))) +} + +pub(crate) fn file_sha256_hex(path: &Path) -> anyhow::Result { + let file = + fs::File::open(path).with_context(|| format!("failed to open {}", path.display()))?; + let mut reader = BufReader::new(file); + let mut hasher = Sha256::new(); + let mut buffer = [0_u8; 64 * 1024]; + + loop { + let bytes_read = reader + .read(&mut buffer) + .with_context(|| format!("failed to read {}", path.display()))?; + if bytes_read == 0 { + break; + } + hasher.update(&buffer[..bytes_read]); + } + + Ok(format!("0x{}", hex::encode(hasher.finalize()))) +} + +pub(crate) fn utc_now_rfc3339() -> anyhow::Result { + OffsetDateTime::now_utc() + .format(&Rfc3339) + .context("failed to format current timestamp") +} + +fn part_path(path: &Path) -> PathBuf { + path.with_extension( + match path.extension().and_then(|extension| extension.to_str()) { + Some(extension) => format!("{extension}.part"), + None => "part".to_owned(), + }, + ) +} + +fn filename_hash(hash: &str) -> &str { + hash.strip_prefix("0x").unwrap_or(hash) +} + +fn generator_git_commit() -> String { + std::env::var("WITNESS_GENERATOR_SPEC_GIT_COMMIT") + .ok() + .filter(|value| !value.trim().is_empty()) + .or_else(|| option_env!("GIT_COMMIT").map(str::to_owned)) + .unwrap_or_else(|| "unknown".to_owned()) +} + +#[cfg(test)] +mod tests { + use alloy_primitives::B256; + + use super::*; + + #[test] + fn artifact_serializes_and_roundtrips_through_zstd() { + let dir = temp_dir("artifact_roundtrip"); + let generated = generated_input(42, B256::repeat_byte(0xaa)); + let artifact = StatelessInputArtifact::from_generated_at( + "glamsterdam-devnet-5", + "head", + &generated, + "2026-06-11T00:00:00Z", + "test-commit".to_owned(), + ) + .unwrap(); + + let result = write_artifact_atomic(&dir, &artifact).unwrap(); + let decoded = read_artifact(&result.path).unwrap(); + + assert!(result.created); + assert_eq!(decoded, artifact); + assert_eq!(decoded.stateless_input_byte_length, generated.bytes.len()); + assert_eq!(decoded.stateless_input_sha256, sha256_hex(&generated.bytes)); + assert_eq!(decoded.stateless_input_bytes, "0x00010203"); + assert_eq!(decoded.collection_mode, "head"); + + let file = fs::File::open(&result.path).unwrap(); + let json = zstd::stream::decode_all(file).unwrap(); + let value: serde_json::Value = serde_json::from_slice(&json).unwrap(); + assert_eq!(value["collectionMode"], "head"); + assert!(value.get("selector").is_none()); + assert!(value.get("canonicality").is_none()); + } + + #[test] + fn artifact_paths_include_block_number_and_hash() { + let first = StatelessInputArtifact::from_generated_at( + "glamsterdam-devnet-5", + "head", + &generated_input(2_381, B256::repeat_byte(0xaa)), + "2026-06-11T00:00:00Z", + "test-commit".to_owned(), + ) + .unwrap(); + let second = StatelessInputArtifact::from_generated_at( + "glamsterdam-devnet-5", + "head", + &generated_input(2_381, B256::repeat_byte(0xbb)), + "2026-06-11T00:00:00Z", + "test-commit".to_owned(), + ) + .unwrap(); + + assert_ne!( + relative_artifact_path(&first), + relative_artifact_path(&second) + ); + assert_eq!( + relative_artifact_path(&first), + PathBuf::from("000002").join(format!("2381-{}.json.zst", "aa".repeat(32))) + ); + } + + fn generated_input(block_number: u64, block_hash: B256) -> GeneratedInput { + GeneratedInput { + bytes: vec![0x00, 0x01, 0x02, 0x03], + block_hash, + block_number, + slot_number: 64, + chain_id: 1, + } + } + + fn temp_dir(name: &str) -> PathBuf { + let path = std::env::temp_dir().join(format!( + "witness-generator-spec-cli-{name}-{}", + std::process::id() + )); + let _ = fs::remove_dir_all(&path); + fs::create_dir_all(&path).unwrap(); + path + } +} diff --git a/crates/witness-generator-spec-cli/src/builder.rs b/crates/witness-generator-spec-cli/src/builder.rs new file mode 100644 index 00000000..bcf6d70b --- /dev/null +++ b/crates/witness-generator-spec-cli/src/builder.rs @@ -0,0 +1,494 @@ +//! Assembly of canonical Amsterdam stateless input bytes. + +use alloy_consensus::{Header, Transaction as AlloyTransaction, TxEnvelope}; +use alloy_eips::eip2718::Decodable2718; +use alloy_primitives::{B256, Bytes}; +use alloy_rlp::Decodable; +use anyhow::{Context, ensure}; +use stateless_validator_common::{ + SszList, + guest::input::{ + ExecutionWitness, MAX_BYTES_PER_CODE, MAX_BYTES_PER_HEADER, MAX_BYTES_PER_WITNESS_NODE, + MAX_PUBLIC_KEYS, MAX_WITNESS_CODES, MAX_WITNESS_HEADERS, MAX_WITNESS_NODES, + PUBLIC_KEY_BYTES, StatelessInput, + new_payload_request::{ + BlockAccessList, ConsolidationRequest, ConsolidationRequests, DepositRequest, + DepositRequests, ExecutionPayloadV4, ExecutionRequests, ExtraData, NewPayloadRequest, + NewPayloadRequestGloas, Transaction as PayloadTransaction, Transactions, + VersionedHashes, Withdrawal, WithdrawalRequest, WithdrawalRequests, Withdrawals, + }, + }, +}; + +use crate::{ + chain_config, + rpc::{ + BeaconExecutionPayload, ConsolidationRequestJson, DepositRequestJson, + ExecutionPayloadEnvelope, ExecutionRequestsJson, RpcExecutionWitness, WithdrawalJson, + WithdrawalRequestJson, + }, +}; + +/// Canonical stateless guest input generated from network RPC data. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GeneratedInput { + /// Bytes encoded as `0x0001 || SSZ(SszStatelessInput)`. + pub bytes: Vec, + /// Execution block hash. + pub block_hash: B256, + /// Execution block number. + pub block_number: u64, + /// Consensus slot number copied from the Amsterdam execution payload. + pub slot_number: u64, + /// Execution chain id. + pub chain_id: u64, +} + +pub(crate) fn build_generated_input( + envelope: ExecutionPayloadEnvelope, + witness: RpcExecutionWitness, + chain_id: u64, +) -> anyhow::Result { + let payload = &envelope.payload; + let slot_number = payload.slot_number; + let block_hash = payload.block_hash; + let block_number = payload.block_number; + let transaction_artifacts = decode_transaction_artifacts(&payload.transactions)?; + + let new_payload_request = NewPayloadRequest::Gloas(NewPayloadRequestGloas { + execution_payload: convert_execution_payload(payload)?, + versioned_hashes: transaction_artifacts.versioned_hashes, + parent_beacon_block_root: envelope.parent_beacon_block_root.0, + execution_requests: convert_execution_requests(&envelope.execution_requests)?, + }); + + let witness = convert_witness(witness, block_number)?; + let chain_config = chain_config::amsterdam(chain_id); + let public_keys = SszList::<[u8; PUBLIC_KEY_BYTES], MAX_PUBLIC_KEYS>::try_from( + transaction_artifacts.public_keys, + ) + .map_err(|err| anyhow::anyhow!("public_keys exceed SSZ bound: {err:?}"))?; + + let stateless_input = StatelessInput { + new_payload_request, + witness, + chain_config, + public_keys, + }; + + let bytes = stateless_input.to_schema_prefixed_ssz(); + + Ok(GeneratedInput { + bytes, + block_hash, + block_number, + slot_number, + chain_id, + }) +} + +fn convert_execution_payload( + payload: &BeaconExecutionPayload, +) -> anyhow::Result { + let transactions = payload + .transactions + .iter() + .enumerate() + .map(|(i, tx)| { + PayloadTransaction::try_from(tx.to_vec()) + .map_err(|err| anyhow::anyhow!("transaction #{i} exceeds SSZ bound: {err:?}")) + }) + .collect::>>()?; + let withdrawals = payload + .withdrawals + .iter() + .map(convert_withdrawal) + .collect::>(); + + Ok(ExecutionPayloadV4 { + parent_hash: payload.parent_hash.0, + fee_recipient: payload.fee_recipient.into_array(), + state_root: payload.state_root.0, + receipts_root: payload.receipts_root.0, + logs_bloom: payload.logs_bloom.0, + prev_randao: payload.prev_randao.0, + block_number: payload.block_number, + gas_limit: payload.gas_limit, + gas_used: payload.gas_used, + timestamp: payload.timestamp, + extra_data: ExtraData::try_from(payload.extra_data.to_vec()) + .map_err(|err| anyhow::anyhow!("extra_data exceeds SSZ bound: {err:?}"))?, + base_fee_per_gas: payload.base_fee_per_gas.to_le_bytes(), + block_hash: payload.block_hash.0, + transactions: Transactions::try_from(transactions) + .map_err(|err| anyhow::anyhow!("transactions exceed SSZ bound: {err:?}"))?, + withdrawals: Withdrawals::try_from(withdrawals) + .map_err(|err| anyhow::anyhow!("withdrawals exceed SSZ bound: {err:?}"))?, + blob_gas_used: payload.blob_gas_used, + excess_blob_gas: payload.excess_blob_gas, + block_access_list: BlockAccessList::try_from(payload.block_access_list.to_vec()) + .map_err(|err| anyhow::anyhow!("block_access_list exceeds SSZ bound: {err:?}"))?, + slot_number: payload.slot_number, + }) +} + +const fn convert_withdrawal(withdrawal: &WithdrawalJson) -> Withdrawal { + Withdrawal { + index: withdrawal.index, + validator_index: withdrawal.validator_index, + address: withdrawal.address.into_array(), + amount: withdrawal.amount, + } +} + +fn convert_execution_requests( + requests: &ExecutionRequestsJson, +) -> anyhow::Result { + let deposits = requests + .deposits + .iter() + .map(convert_deposit_request) + .collect::>(); + let withdrawals = requests + .withdrawals + .iter() + .map(convert_withdrawal_request) + .collect::>(); + let consolidations = requests + .consolidations + .iter() + .map(convert_consolidation_request) + .collect::>(); + + Ok(ExecutionRequests { + deposits: DepositRequests::try_from(deposits) + .map_err(|err| anyhow::anyhow!("deposit requests exceed SSZ bound: {err:?}"))?, + withdrawals: WithdrawalRequests::try_from(withdrawals) + .map_err(|err| anyhow::anyhow!("withdrawal requests exceed SSZ bound: {err:?}"))?, + consolidations: ConsolidationRequests::try_from(consolidations) + .map_err(|err| anyhow::anyhow!("consolidation requests exceed SSZ bound: {err:?}"))?, + }) +} + +const fn convert_deposit_request(request: &DepositRequestJson) -> DepositRequest { + DepositRequest { + pubkey: request.pubkey.0, + withdrawal_credentials: request.withdrawal_credentials.0, + amount: request.amount, + signature: request.signature.0, + index: request.index, + } +} + +const fn convert_withdrawal_request(request: &WithdrawalRequestJson) -> WithdrawalRequest { + WithdrawalRequest { + source_address: request.source_address.into_array(), + validator_pubkey: request.validator_pubkey.0, + amount: request.amount, + } +} + +const fn convert_consolidation_request(request: &ConsolidationRequestJson) -> ConsolidationRequest { + ConsolidationRequest { + source_address: request.source_address.into_array(), + source_pubkey: request.source_pubkey.0, + target_pubkey: request.target_pubkey.0, + } +} + +fn convert_witness( + witness: RpcExecutionWitness, + block_number: u64, +) -> anyhow::Result { + let headers = normalize_headers(witness.headers, block_number)?; + Ok(ExecutionWitness { + state: bytes_to_nested_ssz_list::( + witness.state, + "witness.state", + )?, + codes: bytes_to_nested_ssz_list::( + witness.codes, + "witness.codes", + )?, + headers: bytes_to_nested_ssz_list::( + headers, + "witness.headers", + )?, + }) +} + +fn bytes_to_nested_ssz_list( + values: Vec, + label: &str, +) -> anyhow::Result, MAX_ITEMS>> { + let values = values + .into_iter() + .enumerate() + .map(|(i, bytes)| { + SszList::::try_from(bytes.to_vec()) + .map_err(|err| anyhow::anyhow!("{label}[{i}] exceeds SSZ byte bound: {err:?}")) + }) + .collect::>>()?; + SszList::, MAX_ITEMS>::try_from(values) + .map_err(|err| anyhow::anyhow!("{label} exceeds SSZ item bound: {err:?}")) +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct TransactionArtifacts { + public_keys: Vec<[u8; 65]>, + versioned_hashes: VersionedHashes, +} + +fn decode_transaction_artifacts(transactions: &[Bytes]) -> anyhow::Result { + let mut public_keys = Vec::with_capacity(transactions.len()); + let mut versioned_hashes = Vec::new(); + + for (i, tx) in transactions.iter().enumerate() { + let envelope = TxEnvelope::decode_2718_exact(tx.as_ref()) + .with_context(|| format!("failed to decode transaction #{i}"))?; + let public_key = envelope + .signature() + .recover_from_prehash(&envelope.signature_hash()) + .map(|key| key.to_encoded_point(false).as_bytes().try_into().unwrap()) + .with_context(|| format!("failed to recover public key for transaction #{i}"))?; + public_keys.push(public_key); + + if let Some(hashes) = envelope.blob_versioned_hashes() { + versioned_hashes.extend(hashes.iter().map(|hash| hash.0)); + } + } + + let versioned_hashes = VersionedHashes::try_from(versioned_hashes) + .map_err(|err| anyhow::anyhow!("versioned_hashes exceed SSZ bound: {err:?}"))?; + + Ok(TransactionArtifacts { + public_keys, + versioned_hashes, + }) +} + +#[cfg(test)] +fn recover_public_keys(transactions: &[Bytes]) -> anyhow::Result> { + Ok(decode_transaction_artifacts(transactions)?.public_keys) +} + +#[cfg(test)] +fn decode_versioned_hashes(transactions: &[Bytes]) -> anyhow::Result { + Ok(decode_transaction_artifacts(transactions)?.versioned_hashes) +} + +fn normalize_headers(headers: Vec, block_number: u64) -> anyhow::Result> { + ensure!( + block_number > 0, + "cannot require parent header for genesis block" + ); + let parent_number = block_number - 1; + let mut headers = headers + .into_iter() + .enumerate() + .map(|(i, bytes)| { + let number = decode_header_number(&bytes) + .with_context(|| format!("failed to decode witness header #{i}"))?; + Ok((number, bytes)) + }) + .collect::>>()?; + headers.sort_by_key(|(number, _)| *number); + + for pair in headers.windows(2) { + let prev = pair[0].0; + let next = pair[1].0; + ensure!(next != prev, "duplicate witness header for block #{next}"); + ensure!( + next == prev + 1, + "witness headers are not contiguous: block #{prev} followed by block #{next}", + ); + } + + ensure!( + headers + .last() + .map(|(number, _)| *number) + .is_some_and(|number| number == parent_number), + "witness parent header for block #{parent_number} is absent", + ); + + Ok(headers.into_iter().map(|(_, bytes)| bytes).collect()) +} + +fn decode_header_number(bytes: &[u8]) -> anyhow::Result { + let mut slice = bytes; + let header = Header::decode(&mut slice).context("invalid RLP header")?; + ensure!(slice.is_empty(), "RLP header has trailing bytes"); + Ok(header.number) +} + +#[cfg(test)] +mod tests { + use alloy_primitives::{b256, hex}; + use alloy_rlp::Encodable; + use stateless_validator_common::guest::input::{ProtocolFork, STATELESS_INPUT_SCHEMA_ID}; + + use super::*; + + #[test] + fn extracts_versioned_hashes_from_blob_transaction() { + let raw = hex::decode( + "0x03f9011d83aa36a7820fa28477359400852e90edd0008252089411e9ca82a3a762b4b5bd264d4173a242e7a770648080c08504a817c800f8a5a0012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921aa00152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4a0013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7a001148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1a0011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e654901a0c8de4cced43169f9aa3d36506363b2d2c44f6c49fc1fd91ea114c86f3757077ea01e11fdd0d1934eda0492606ee0bb80a7bf8f35cc5f86ec60fe5031ba48bfd544", + ) + .unwrap(); + + let hashes = decode_versioned_hashes(&[Bytes::from(raw)]).unwrap(); + + assert_eq!( + &*hashes, + &[ + b256!("012ec3d6f66766bedb002a190126b3549fce0047de0d4c25cffce0dc1c57921a").0, + b256!("0152d8e24762ff22b1cfd9f8c0683786a7ca63ba49973818b3d1e9512cd2cec4").0, + b256!("013b98c6c83e066d5b14af2b85199e3d4fc7d1e778dd53130d180f5077e2d1c7").0, + b256!("01148b495d6e859114e670ca54fb6e2657f0cbae5b08063605093a4b3dc9f8f1").0, + b256!("011ac212f13c5dff2b2c6b600a79635103d6f580a4221079951181b25c7e6549").0, + ] + ); + } + + #[test] + fn recovers_public_key_from_raw_transaction() { + let raw = hex::decode( + "f86e81fa843127403882f61894db8d964741c53e55df9c2d4e9414c6c96482874e870aa87bee538000808360306ca03aa421df67a101c45ff9cb06ce28f518a5d8d8dbb76a79361280071909650a27a05a447ff053c4ae601cfe81859b58d5603f2d0a73481c50f348089032feb0b073", + ) + .unwrap(); + + let public_keys = recover_public_keys(&[Bytes::from(raw)]).unwrap(); + + assert_eq!(public_keys.len(), 1); + assert_eq!(public_keys[0][0], 0x04); + } + + #[test] + fn normalizes_headers_to_oldest_first_and_parent_last() { + let header_8 = rlp_header(8); + let header_9 = rlp_header(9); + + let normalized = normalize_headers(vec![header_9.clone(), header_8.clone()], 10).unwrap(); + + assert_eq!(normalized, vec![header_8, header_9]); + } + + #[test] + fn rejects_missing_parent_header() { + let err = normalize_headers(vec![rlp_header(7), rlp_header(8)], 10).unwrap_err(); + + assert!( + err.to_string() + .contains("witness parent header for block #9 is absent") + ); + } + + #[test] + fn rejects_non_contiguous_headers() { + let err = normalize_headers(vec![rlp_header(7), rlp_header(9)], 10).unwrap_err(); + + assert!(err.to_string().contains("not contiguous")); + } + + #[test] + fn witness_lists_enforce_bounds() { + let oversized = Bytes::from(vec![0_u8; MAX_BYTES_PER_HEADER + 1]); + let err = bytes_to_nested_ssz_list::( + vec![oversized], + "headers", + ) + .unwrap_err(); + + assert!(err.to_string().contains("exceeds SSZ byte bound")); + } + + #[test] + fn fixture_json_builds_deterministic_stateless_input_bytes() { + let parent_header = rlp_header(9); + let envelope_response = serde_json::json!({ + "version": "gloas", + "execution_optimistic": false, + "finalized": false, + "data": { + "message": { + "payload": { + "parent_hash": format!("0x{}", "01".repeat(32)), + "fee_recipient": format!("0x{}", "02".repeat(20)), + "state_root": format!("0x{}", "03".repeat(32)), + "receipts_root": format!("0x{}", "04".repeat(32)), + "logs_bloom": format!("0x{}", "00".repeat(256)), + "prev_randao": format!("0x{}", "05".repeat(32)), + "block_number": "10", + "gas_limit": "30000000", + "gas_used": "21000", + "timestamp": "1000", + "extra_data": "0x", + "base_fee_per_gas": "0x7", + "block_hash": format!("0x{}", "06".repeat(32)), + "transactions": [], + "withdrawals": [], + "blob_gas_used": "0", + "excess_blob_gas": "0", + "block_access_list": "0xc0", + "slot_number": "64" + }, + "execution_requests": {}, + "builder_index": "0", + "beacon_block_root": format!("0x{}", "bb".repeat(32)), + "parent_beacon_block_root": format!("0x{}", "aa".repeat(32)) + } + } + }); + let witness_json = serde_json::json!({ + "state": ["0x80"], + "keys": ["0x01"], + "codes": ["0x"], + "headers": [format!("0x{}", hex::encode(&parent_header))] + }); + let parsed: crate::rpc::ExecutionPayloadEnvelopeResponse = + serde_json::from_value(envelope_response).unwrap(); + let envelope = parsed.data.message; + let witness: RpcExecutionWitness = serde_json::from_value(witness_json).unwrap(); + + let first = build_generated_input(envelope.clone(), witness.clone(), 1).unwrap(); + let second = build_generated_input(envelope, witness, 1).unwrap(); + + assert_eq!(first.bytes, second.bytes); + assert_eq!(&first.bytes[..2], &STATELESS_INPUT_SCHEMA_ID.to_be_bytes()); + assert_eq!(first.block_number, 10); + assert_eq!(first.slot_number, 64); + assert_eq!(first.chain_id, 1); + + let decoded = StatelessInput::from_schema_prefixed_ssz(&first.bytes).unwrap(); + assert_eq!( + decoded.chain_config.active_fork.fork, + ProtocolFork::Amsterdam + ); + assert_eq!(decoded.witness.state.len(), 1); + assert_eq!(decoded.public_keys.len(), 0); + let NewPayloadRequest::Gloas(request) = decoded.new_payload_request else { + panic!("Amsterdam input must decode as a Gloas payload request"); + }; + assert_eq!(request.execution_payload.block_number, 10); + assert_eq!(request.execution_payload.slot_number, 64); + } + + fn rlp_header(number: u64) -> Bytes { + let header = Header { + number, + base_fee_per_gas: Some(7), + withdrawals_root: Some(B256::ZERO), + blob_gas_used: Some(0), + excess_blob_gas: Some(0), + parent_beacon_block_root: Some(B256::ZERO), + requests_hash: Some(B256::ZERO), + block_access_list_hash: Some(B256::ZERO), + slot_number: Some(number + 1), + ..Default::default() + }; + let mut bytes = Vec::new(); + header.encode(&mut bytes); + Bytes::from(bytes) + } +} diff --git a/crates/witness-generator-spec-cli/src/catalog.rs b/crates/witness-generator-spec-cli/src/catalog.rs new file mode 100644 index 00000000..c88199e7 --- /dev/null +++ b/crates/witness-generator-spec-cli/src/catalog.rs @@ -0,0 +1,598 @@ +use std::{ + fs, + io::Read, + path::{Path, PathBuf}, +}; + +use anyhow::{Context, ensure}; +use serde::{Deserialize, Serialize}; +use tar::Archive; + +use crate::{ + artifact::{self, path_to_slash_string, write_bytes_atomic}, + config::CollectorConfig, +}; + +const CATALOG_SCHEMA_VERSION: u64 = 2; +const CATALOG_KIND: &str = "stateless-inputs-public-catalog"; +const HTML_INDEX: &str = "index.html"; +const PUBLIC_MANIFEST: &str = "manifest.json"; +const PUBLIC_BATCHES_INDEX: &str = "batches.jsonl"; +const CHECKSUMS: &str = "SHA256SUMS"; +const BATCH_PREFIX: &str = "exports/batches"; +const STALE_PUBLIC_BLOCKS_INDEX: &str = "blocks.jsonl"; + +pub(crate) const REQUIRED_CATALOG_FILES: &[&str] = + &[HTML_INDEX, PUBLIC_MANIFEST, PUBLIC_BATCHES_INDEX, CHECKSUMS]; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct CatalogGeneration { + pub(crate) artifact_count: usize, + pub(crate) batch_count: usize, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct PublicManifest { + schema_version: u64, + kind: String, + network: String, + generated_at: String, + batch_size: u64, + paths: PublicManifestPaths, + batches: PublicBatchesSummary, + notes: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct PublicManifestPaths { + html: String, + manifest: String, + batches: String, + checksums: String, + batch_prefix: String, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct PublicBatchesSummary { + count: usize, + artifact_count: usize, + first_start_block: Option, + last_end_block: Option, + total_byte_length: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct PublicBatchEntry { + schema_version: u64, + network: String, + batch_start_block: u64, + batch_end_block: u64, + batch_size: u64, + artifact_count: usize, + created_at: String, + byte_length: u64, + sha256: String, + path: String, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct BatchArchiveManifest { + network: String, + batch_start_block: u64, + batch_end_block: u64, + batch_size: u64, + artifact_count: usize, + created_at: String, +} + +pub(crate) fn required_catalog_files(config: &CollectorConfig) -> Vec<(&'static str, PathBuf)> { + REQUIRED_CATALOG_FILES + .iter() + .map(|name| (*name, config.network_root().join(name))) + .collect() +} + +pub(crate) fn generate_catalog(config: &CollectorConfig) -> anyhow::Result { + let batches = read_batch_entries(config)?; + let artifact_count = batches.iter().map(|batch| batch.artifact_count).sum(); + let manifest = public_manifest(config, &batches)?; + + write_json(config.network_root().join(PUBLIC_MANIFEST), &manifest)?; + write_jsonl(config.network_root().join(PUBLIC_BATCHES_INDEX), &batches)?; + write_bytes_atomic( + &config.network_root().join(CHECKSUMS), + checksums_file(&batches).as_bytes(), + )?; + write_bytes_atomic( + &config.network_root().join(HTML_INDEX), + render_html(&manifest, &batches).as_bytes(), + )?; + remove_stale_public_file(config, STALE_PUBLIC_BLOCKS_INDEX)?; + + Ok(CatalogGeneration { + artifact_count, + batch_count: batches.len(), + }) +} + +fn read_batch_entries(config: &CollectorConfig) -> anyhow::Result> { + if !config.batches_root().exists() { + return Ok(Vec::new()); + } + + let mut archive_paths = Vec::new(); + for entry in fs::read_dir(config.batches_root()).with_context(|| { + format!( + "failed to read batch export directory {}", + config.batches_root().display() + ) + })? { + let entry = entry.with_context(|| { + format!( + "failed to read entry in batch export directory {}", + config.batches_root().display() + ) + })?; + let path = entry.path(); + if entry + .file_type() + .with_context(|| format!("failed to read file type for {}", path.display()))? + .is_file() + && is_batch_archive(&path) + { + archive_paths.push(path); + } + } + archive_paths.sort(); + + let mut batches = Vec::new(); + for archive_path in archive_paths { + let manifest = read_batch_manifest(&archive_path)?; + ensure!( + manifest.network == config.network, + "batch archive {} is for network {}, expected {}", + archive_path.display(), + manifest.network, + config.network + ); + let byte_length = fs::metadata(&archive_path) + .with_context(|| format!("failed to stat batch archive {}", archive_path.display()))? + .len(); + let sha256 = artifact::file_sha256_hex(&archive_path)?; + let relative_path = archive_path + .strip_prefix(config.network_root()) + .with_context(|| { + format!( + "batch archive {} is not under {}", + archive_path.display(), + config.network_root().display() + ) + })?; + batches.push(PublicBatchEntry { + schema_version: CATALOG_SCHEMA_VERSION, + network: manifest.network, + batch_start_block: manifest.batch_start_block, + batch_end_block: manifest.batch_end_block, + batch_size: manifest.batch_size, + artifact_count: manifest.artifact_count, + created_at: manifest.created_at, + byte_length, + sha256, + path: path_to_slash_string(relative_path), + }); + } + + batches.sort_by_key(|batch| (batch.batch_start_block, batch.batch_end_block)); + Ok(batches) +} + +fn public_manifest( + config: &CollectorConfig, + batches: &[PublicBatchEntry], +) -> anyhow::Result { + let first_start_block = batches.iter().map(|entry| entry.batch_start_block).min(); + let last_end_block = batches.iter().map(|entry| entry.batch_end_block).max(); + let artifact_count = batches.iter().map(|entry| entry.artifact_count).sum(); + let total_byte_length = batches.iter().map(|entry| entry.byte_length).sum(); + + Ok(PublicManifest { + schema_version: CATALOG_SCHEMA_VERSION, + kind: CATALOG_KIND.to_owned(), + network: config.network.clone(), + generated_at: artifact::utc_now_rfc3339()?, + batch_size: config.batch_size, + paths: PublicManifestPaths { + html: HTML_INDEX.to_owned(), + manifest: PUBLIC_MANIFEST.to_owned(), + batches: PUBLIC_BATCHES_INDEX.to_owned(), + checksums: CHECKSUMS.to_owned(), + batch_prefix: BATCH_PREFIX.to_owned(), + }, + batches: PublicBatchesSummary { + count: batches.len(), + artifact_count, + first_start_block, + last_end_block, + total_byte_length, + }, + notes: vec![ + "Public downloads are batch archives; individual block artifacts are inside those archives and are not published as standalone R2 objects.".to_owned(), + "Cloudflare R2 public buckets do not provide directory listing; use this page or the JSON indexes instead.".to_owned(), + ], + }) +} + +fn remove_stale_public_file(config: &CollectorConfig, name: &str) -> anyhow::Result<()> { + let path = config.network_root().join(name); + if path.exists() { + fs::remove_file(&path).with_context(|| { + format!( + "failed to remove stale public catalog file {}", + path.display() + ) + })?; + } + Ok(()) +} + +fn read_batch_manifest(path: &Path) -> anyhow::Result { + let file = fs::File::open(path) + .with_context(|| format!("failed to open batch archive {}", path.display()))?; + let decoder = zstd::stream::read::Decoder::new(file) + .with_context(|| format!("failed to create zstd decoder for {}", path.display()))?; + let mut archive = Archive::new(decoder); + for entry in archive + .entries() + .with_context(|| format!("failed to read tar entries from {}", path.display()))? + { + let mut entry = + entry.with_context(|| format!("failed to read tar entry from {}", path.display()))?; + if entry + .path() + .with_context(|| format!("failed to read tar entry path from {}", path.display()))? + .as_ref() + == Path::new("manifest.json") + { + let mut bytes = Vec::new(); + entry + .read_to_end(&mut bytes) + .with_context(|| format!("failed to read manifest.json from {}", path.display()))?; + let manifest = serde_json::from_slice(&bytes).with_context(|| { + format!("failed to decode manifest.json from {}", path.display()) + })?; + return Ok(manifest); + } + } + anyhow::bail!( + "batch archive {} does not contain manifest.json", + path.display() + ) +} + +fn write_json(path: PathBuf, value: &T) -> anyhow::Result<()> +where + T: Serialize, +{ + let bytes = serde_json::to_vec_pretty(value).context("failed to serialize JSON")?; + write_bytes_atomic(&path, &bytes) +} + +fn write_jsonl(path: PathBuf, entries: &[T]) -> anyhow::Result<()> +where + T: Serialize, +{ + let mut bytes = Vec::new(); + for entry in entries { + serde_json::to_writer(&mut bytes, entry).context("failed to serialize JSONL entry")?; + bytes.push(b'\n'); + } + write_bytes_atomic(&path, &bytes) +} + +fn checksums_file(batches: &[PublicBatchEntry]) -> String { + let mut checksums = String::new(); + for batch in batches { + checksums.push_str(batch.sha256.strip_prefix("0x").unwrap_or(&batch.sha256)); + checksums.push_str(" "); + checksums.push_str(batch.path.rsplit('/').next().unwrap_or(&batch.path)); + checksums.push('\n'); + } + checksums +} + +fn render_html(manifest: &PublicManifest, batches: &[PublicBatchEntry]) -> String { + let mut html = String::new(); + html.push_str("\n\n\n\n"); + html.push_str("\n"); + html.push_str(""); + push_escaped(&mut html, &manifest.network); + html.push_str(" stateless inputs\n\n\n\n
\n"); + html.push_str("

"); + push_escaped(&mut html, &manifest.network); + html.push_str(" stateless inputs

\n"); + html.push_str("

Batch-first public dataset catalog generated at "); + push_escaped(&mut html, &manifest.generated_at); + html.push_str(".

\n"); + + html.push_str("
\n"); + push_metric( + &mut html, + "Block artifacts", + manifest.batches.artifact_count, + ); + push_metric(&mut html, "Batches", manifest.batches.count); + push_metric(&mut html, "Batch size", manifest.batch_size); + push_metric( + &mut html, + "Total batch bytes", + manifest.batches.total_byte_length, + ); + html.push_str("
\n"); + + html.push_str("
\n

How to download

\n"); + html.push_str("

Use the batch archive links below. Each archive contains block artifacts plus a manifest.json.

\n"); + if let Some(first_batch) = batches.first() { + html.push_str("
curl -LO ");
+        push_escaped(&mut html, &first_batch.path);
+        html.push_str("\ntar --zstd -xf ");
+        push_escaped(
+            &mut html,
+            first_batch
+                .path
+                .rsplit('/')
+                .next()
+                .unwrap_or(&first_batch.path),
+        );
+        html.push_str("
\n"); + } else { + html.push_str("

No complete batch archives are available yet.

\n"); + } + html.push_str("

Verify downloads with SHA256SUMS.

\n
\n"); + + html.push_str("
\n

Machine-readable indexes

\n
    \n"); + push_link_item(&mut html, &manifest.paths.manifest, "Dataset manifest"); + push_link_item(&mut html, &manifest.paths.batches, "Batch index"); + html.push_str("
\n

R2 public buckets do not provide directory listing; use these files instead of folder URLs.

\n
\n"); + + html.push_str("

Batch archives

\n"); + if batches.is_empty() { + html.push_str("

No completed batch archives have been exported yet.

\n"); + } else { + html.push_str("\n\n\n"); + for batch in batches { + html.push_str("\n"); + } + html.push_str("\n
BlocksArtifactsSizeSHA-256Download
"); + push_escaped( + &mut html, + &format!("{}-{}", batch.batch_start_block, batch.batch_end_block), + ); + html.push_str(""); + push_escaped(&mut html, &batch.artifact_count.to_string()); + html.push_str(""); + push_escaped(&mut html, &batch.byte_length.to_string()); + html.push_str(""); + push_escaped(&mut html, &short_sha256(&batch.sha256)); + html.push_str(""); + push_escaped( + &mut html, + batch.path.rsplit('/').next().unwrap_or(&batch.path), + ); + html.push_str("
\n"); + } + + html.push_str("
\n\n\n"); + html +} + +fn push_metric(html: &mut String, label: &str, value: T) +where + T: std::fmt::Display, +{ + html.push_str("
"); + push_escaped(html, label); + html.push_str(""); + push_escaped(html, &value.to_string()); + html.push_str("
\n"); +} + +fn push_link_item(html: &mut String, href: &str, label: &str) { + html.push_str("
  • "); + push_escaped(html, href); + html.push_str(" "); + push_escaped(html, label); + html.push_str("
  • \n"); +} + +fn push_escaped_attr(out: &mut String, input: &str) { + push_escaped(out, input); +} + +fn push_escaped(out: &mut String, input: &str) { + for ch in input.chars() { + match ch { + '&' => out.push_str("&"), + '<' => out.push_str("<"), + '>' => out.push_str(">"), + '"' => out.push_str("""), + '\'' => out.push_str("'"), + _ => out.push(ch), + } + } +} + +fn short_sha256(sha256: &str) -> String { + let hash = sha256.strip_prefix("0x").unwrap_or(sha256); + match hash.get(..16) { + Some(prefix) => format!("0x{prefix}..."), + None => sha256.to_owned(), + } +} + +fn is_batch_archive(path: &Path) -> bool { + path.file_name() + .and_then(|name| name.to_str()) + .is_some_and(|name| name.ends_with(".tar.zst")) +} + +#[cfg(test)] +mod tests { + use std::time::Duration; + + use alloy_primitives::B256; + use serde_json::Value; + use witness_generator_spec_cli::GeneratedInput; + + use crate::{ + artifact::{StatelessInputArtifact, append_index_entry, write_artifact_atomic}, + export, + }; + + use super::*; + + #[test] + fn generates_public_catalog_for_completed_batches() { + let config = test_config("completed_batches", 2); + write_generated_artifact(&config, 0, B256::repeat_byte(0xaa)); + write_generated_artifact(&config, 1, B256::repeat_byte(0xbb)); + write_generated_artifact(&config, 2, B256::repeat_byte(0xcc)); + export::export_batches(&config, false).unwrap(); + + let generation = generate_catalog(&config).unwrap(); + + assert_eq!(generation.artifact_count, 2); + assert_eq!(generation.batch_count, 1); + assert!(config.network_root().join("index.html").is_file()); + assert!(config.network_root().join("manifest.json").is_file()); + assert!(config.network_root().join("batches.jsonl").is_file()); + assert!(!config.network_root().join("blocks.jsonl").exists()); + assert!(config.network_root().join("SHA256SUMS").is_file()); + + let manifest: Value = + serde_json::from_slice(&fs::read(config.network_root().join("manifest.json")).unwrap()) + .unwrap(); + assert_eq!(manifest["network"], "glamsterdam-devnet-5"); + assert!(manifest.get("blocks").is_none()); + assert!(manifest["paths"].get("blocks").is_none()); + assert!(manifest["paths"].get("legacyBlockIndex").is_none()); + assert_eq!(manifest["batches"]["count"], 1); + assert_eq!(manifest["batches"]["artifactCount"], 2); + + let batches = read_jsonl_values(&config.network_root().join("batches.jsonl")); + assert_eq!(batches.len(), 1); + assert_eq!(batches[0]["path"], "exports/batches/0-1.tar.zst"); + assert_eq!( + batches[0]["byteLength"], + fs::metadata(config.batches_root().join("0-1.tar.zst")) + .unwrap() + .len() + ); + assert_eq!( + batches[0]["sha256"], + artifact::file_sha256_hex(&config.batches_root().join("0-1.tar.zst")).unwrap() + ); + + let checksums = fs::read_to_string(config.network_root().join("SHA256SUMS")).unwrap(); + assert!(checksums.contains(" 0-1.tar.zst\n")); + + let html = fs::read_to_string(config.network_root().join("index.html")).unwrap(); + assert!(html.contains("glamsterdam-devnet-5 stateless inputs")); + assert!(html.contains("exports/batches/0-1.tar.zst")); + assert!(!html.contains("blocks.jsonl")); + assert!(!html.contains("index.jsonl")); + } + + #[test] + fn incomplete_ranges_are_omitted_from_public_catalog() { + let config = test_config("incomplete_ranges", 2); + write_generated_artifact(&config, 3, B256::repeat_byte(0xdd)); + fs::write(config.network_root().join("blocks.jsonl"), b"stale\n").unwrap(); + export::export_batches(&config, false).unwrap(); + + let generation = generate_catalog(&config).unwrap(); + + assert_eq!(generation.artifact_count, 0); + assert_eq!(generation.batch_count, 0); + assert!(read_jsonl_values(&config.network_root().join("batches.jsonl")).is_empty()); + assert!(!config.network_root().join("blocks.jsonl").exists()); + } + + #[test] + fn catalog_includes_existing_archives_when_export_skips_them() { + let config = test_config("skipped_archives", 2); + write_generated_artifact(&config, 0, B256::repeat_byte(0xaa)); + write_generated_artifact(&config, 1, B256::repeat_byte(0xbb)); + + assert_eq!(export::export_batches(&config, false).unwrap().len(), 1); + assert!(export::export_batches(&config, false).unwrap().is_empty()); + let generation = generate_catalog(&config).unwrap(); + + assert_eq!(generation.artifact_count, 2); + assert_eq!(generation.batch_count, 1); + assert_eq!( + read_jsonl_values(&config.network_root().join("batches.jsonl")).len(), + 1 + ); + } + + fn write_generated_artifact(config: &CollectorConfig, block_number: u64, block_hash: B256) { + let generated = GeneratedInput { + bytes: vec![0x00, 0x01, block_number as u8], + block_hash, + block_number, + slot_number: block_number + 100, + chain_id: 1, + }; + let artifact = StatelessInputArtifact::from_generated_at( + &config.network, + "head", + &generated, + "2026-06-11T00:00:00Z", + "test-commit".to_owned(), + ) + .unwrap(); + let write = write_artifact_atomic(&config.blocks_root(), &artifact).unwrap(); + let index_entry = artifact.index_entry(&PathBuf::from("blocks").join(write.relative_path)); + append_index_entry(&config.index_path(), &index_entry).unwrap(); + } + + fn read_jsonl_values(path: &Path) -> Vec { + let contents = fs::read_to_string(path).unwrap(); + contents + .lines() + .filter(|line| !line.trim().is_empty()) + .map(|line| serde_json::from_str(line).unwrap()) + .collect() + } + + fn test_config(name: &str, batch_size: u64) -> CollectorConfig { + let out_root = std::env::temp_dir().join(format!( + "witness-generator-spec-cli-catalog-{name}-{}", + std::process::id() + )); + let _ = fs::remove_dir_all(&out_root); + CollectorConfig { + network: "glamsterdam-devnet-5".to_owned(), + cl_url: "http://cl".to_owned(), + el_url: "http://el".to_owned(), + out_root, + poll_interval: Duration::from_secs(4), + request_timeout: Duration::from_secs(30), + batch_size, + r2: None, + } + } +} diff --git a/crates/witness-generator-spec-cli/src/chain_config.rs b/crates/witness-generator-spec-cli/src/chain_config.rs new file mode 100644 index 00000000..8bd1a73c --- /dev/null +++ b/crates/witness-generator-spec-cli/src/chain_config.rs @@ -0,0 +1,39 @@ +//! Amsterdam chain configuration used by generated stateless inputs. + +use stateless_validator_common::guest::input::{ + BlobSchedule, ChainConfig, ForkActivation, ForkConfig, ProtocolFork, +}; + +pub(crate) fn amsterdam(chain_id: u64) -> ChainConfig { + ChainConfig { + chain_id, + active_fork: ForkConfig::new( + ProtocolFork::Amsterdam, + ForkActivation::new(None, Some(0)), + Some(BlobSchedule { + target: 14, + max: 21, + base_fee_update_fraction: 11_684_671, + }), + ), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn uses_amsterdam_spec_defaults() { + let cfg = amsterdam(1); + + assert_eq!(cfg.chain_id, 1); + assert_eq!(cfg.active_fork.fork, ProtocolFork::Amsterdam); + assert_eq!(cfg.active_fork.activation.block_number(), None); + assert_eq!(cfg.active_fork.activation.timestamp(), Some(0)); + let schedule = cfg.active_fork.blob_schedule().unwrap(); + assert_eq!(schedule.target, 14); + assert_eq!(schedule.max, 21); + assert_eq!(schedule.base_fee_update_fraction, 11_684_671); + } +} diff --git a/crates/witness-generator-spec-cli/src/collector.rs b/crates/witness-generator-spec-cli/src/collector.rs new file mode 100644 index 00000000..980de89b --- /dev/null +++ b/crates/witness-generator-spec-cli/src/collector.rs @@ -0,0 +1,187 @@ +use std::{fs, path::PathBuf}; + +use anyhow::Context; +use serde::{Deserialize, Serialize}; +use tokio::time; +use tracing::{info, warn}; +use witness_generator_spec_cli::{ + BlockSelector, GeneratedInput, NetworkWitnessClient, NetworkWitnessConfig, +}; + +use crate::{ + artifact::{ + self, ArtifactWriteResult, StatelessInputArtifact, append_index_entry, write_json_atomic, + }, + config::CollectorConfig, +}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct PersistedArtifact { + pub(crate) artifact: StatelessInputArtifact, + pub(crate) write: ArtifactWriteResult, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct CollectorState { + last_head_hash: String, + last_block_number: u64, + last_slot_number: u64, + updated_at: String, +} + +pub(crate) async fn collect(config: CollectorConfig, once: bool) -> anyhow::Result<()> { + let mut network_config = + NetworkWitnessConfig::new(config.cl_url.clone(), config.el_url.clone()); + network_config.timeout = config.request_timeout; + let client = NetworkWitnessClient::new(network_config)?; + let mut last_head_hash = read_state(&config.state_path())?.map(|state| state.last_head_hash); + + if once { + collect_head_once(&client, &config, &mut last_head_hash).await?; + return Ok(()); + } + + loop { + match collect_head_once(&client, &config, &mut last_head_hash).await { + Ok(Some(persisted)) => { + info!( + block_number = persisted.artifact.block_number, + block_hash = persisted.artifact.block_hash, + path = %persisted.write.path.display(), + "collected stateless input artifact", + ); + } + Ok(None) => {} + Err(error) => { + warn!(?error, "failed to collect stateless input artifact"); + } + } + time::sleep(config.poll_interval).await; + } +} + +async fn collect_head_once( + client: &NetworkWitnessClient, + config: &CollectorConfig, + last_head_hash: &mut Option, +) -> anyhow::Result> { + let generated = client + .stateless_input_bytes(BlockSelector::Head) + .await + .context("failed to generate stateless input bytes for head")?; + let persisted = collect_generated(config, generated, last_head_hash.as_deref())?; + if let Some(persisted) = &persisted { + *last_head_hash = Some(persisted.artifact.block_hash.clone()); + } + Ok(persisted) +} + +pub(crate) fn collect_generated( + config: &CollectorConfig, + generated: GeneratedInput, + last_head_hash: Option<&str>, +) -> anyhow::Result> { + let block_hash = generated.block_hash.to_string(); + if last_head_hash == Some(block_hash.as_str()) { + return Ok(None); + } + + let artifact = StatelessInputArtifact::from_generated(&config.network, "head", &generated)?; + let write = artifact::write_artifact_atomic(&config.blocks_root(), &artifact)?; + if write.created { + let index_entry = artifact.index_entry(&PathBuf::from("blocks").join(&write.relative_path)); + append_index_entry(&config.index_path(), &index_entry)?; + } + write_state(config, &artifact)?; + + Ok(Some(PersistedArtifact { artifact, write })) +} + +fn read_state(path: &std::path::Path) -> anyhow::Result> { + if !path.exists() { + return Ok(None); + } + let contents = + fs::read(path).with_context(|| format!("failed to read state {}", path.display()))?; + serde_json::from_slice(&contents) + .with_context(|| format!("failed to decode state {}", path.display())) +} + +fn write_state(config: &CollectorConfig, artifact: &StatelessInputArtifact) -> anyhow::Result<()> { + let state = CollectorState { + last_head_hash: artifact.block_hash.clone(), + last_block_number: artifact.block_number, + last_slot_number: artifact.slot_number, + updated_at: artifact::utc_now_rfc3339()?, + }; + write_json_atomic(&config.state_path(), &state) +} + +#[cfg(test)] +mod tests { + use alloy_primitives::B256; + + use super::*; + + #[test] + fn collect_generated_dedupes_unchanged_head() { + let config = test_config("dedupe"); + let generated = generated_input(42, B256::repeat_byte(0xaa)); + + let persisted = collect_generated(&config, generated.clone(), None) + .unwrap() + .unwrap(); + let skipped = + collect_generated(&config, generated, Some(&persisted.artifact.block_hash)).unwrap(); + + assert!(skipped.is_none()); + } + + #[test] + fn collect_generated_preserves_reorg_variants() { + let config = test_config("reorg"); + let first = collect_generated(&config, generated_input(42, B256::repeat_byte(0xaa)), None) + .unwrap() + .unwrap(); + let second = collect_generated( + &config, + generated_input(42, B256::repeat_byte(0xbb)), + Some(&first.artifact.block_hash), + ) + .unwrap() + .unwrap(); + + assert_ne!(first.write.path, second.write.path); + assert!(first.write.path.exists()); + assert!(second.write.path.exists()); + } + + fn test_config(name: &str) -> CollectorConfig { + let out_root = std::env::temp_dir().join(format!( + "witness-generator-spec-cli-collector-{name}-{}", + std::process::id() + )); + let _ = fs::remove_dir_all(&out_root); + CollectorConfig { + network: "glamsterdam-devnet-5".to_owned(), + cl_url: "http://cl".to_owned(), + el_url: "http://el".to_owned(), + out_root, + poll_interval: std::time::Duration::from_secs(4), + request_timeout: std::time::Duration::from_secs(30), + batch_size: 500, + r2: None, + } + } + + fn generated_input(block_number: u64, block_hash: B256) -> GeneratedInput { + GeneratedInput { + bytes: vec![0x00, 0x01, 0x02, 0x03], + block_hash, + block_number, + slot_number: 64, + chain_id: 1, + } + } +} diff --git a/crates/witness-generator-spec-cli/src/config.rs b/crates/witness-generator-spec-cli/src/config.rs new file mode 100644 index 00000000..3f80b94a --- /dev/null +++ b/crates/witness-generator-spec-cli/src/config.rs @@ -0,0 +1,218 @@ +use std::{env, fs, path::PathBuf, time::Duration}; + +use anyhow::{Context, ensure}; +use serde::Deserialize; + +const DEFAULT_OUT_ROOT: &str = "/var/lib/stateless-inputs"; +const DEFAULT_POLL_INTERVAL: Duration = Duration::from_secs(4); +const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(30); +const DEFAULT_BATCH_SIZE: u64 = 500; + +#[derive(Debug, Clone)] +pub(crate) struct CollectorConfig { + pub(crate) network: String, + pub(crate) cl_url: String, + pub(crate) el_url: String, + pub(crate) out_root: PathBuf, + pub(crate) poll_interval: Duration, + pub(crate) request_timeout: Duration, + pub(crate) batch_size: u64, + pub(crate) r2: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +pub(crate) struct R2PublishConfig { + pub(crate) bucket: String, + #[serde(default)] + pub(crate) prefix: String, + pub(crate) account_id: String, +} + +#[derive(Debug, Deserialize)] +struct ConfigFile { + network: String, + cl_url: Option, + el_url: Option, + out_root: Option, + poll_interval: Option, + request_timeout: Option, + batch_size: Option, + r2: Option, +} + +impl CollectorConfig { + pub(crate) fn from_path(path: impl Into) -> anyhow::Result { + let path = path.into(); + let contents = fs::read_to_string(&path) + .with_context(|| format!("failed to read config {}", path.display()))?; + Self::from_toml_str(&contents) + .with_context(|| format!("failed to parse config {}", path.display())) + } + + pub(crate) fn from_toml_str(contents: &str) -> anyhow::Result { + let file: ConfigFile = toml::from_str(contents)?; + ensure!(!file.network.trim().is_empty(), "network must not be empty"); + + let cl_url = endpoint_from_config_or_env(file.cl_url, "CL_RPC_URL") + .context("cl_url is required in config or CL_RPC_URL")?; + let el_url = endpoint_from_config_or_env(file.el_url, "EL_RPC_URL") + .context("el_url is required in config or EL_RPC_URL")?; + + let poll_interval = parse_duration_or_default( + file.poll_interval.as_deref(), + DEFAULT_POLL_INTERVAL, + "poll_interval", + )?; + let request_timeout = parse_duration_or_default( + file.request_timeout.as_deref(), + DEFAULT_REQUEST_TIMEOUT, + "request_timeout", + )?; + let batch_size = file.batch_size.unwrap_or(DEFAULT_BATCH_SIZE); + ensure!(batch_size > 0, "batch_size must be greater than zero"); + + Ok(Self { + network: file.network, + cl_url, + el_url, + out_root: file + .out_root + .unwrap_or_else(|| PathBuf::from(DEFAULT_OUT_ROOT)), + poll_interval, + request_timeout, + batch_size, + r2: file.r2.map(R2PublishConfig::normalize).transpose()?, + }) + } + + pub(crate) fn network_root(&self) -> PathBuf { + self.out_root.join(&self.network) + } + + pub(crate) fn blocks_root(&self) -> PathBuf { + self.network_root().join("blocks") + } + + pub(crate) fn exports_root(&self) -> PathBuf { + self.network_root().join("exports") + } + + pub(crate) fn batches_root(&self) -> PathBuf { + self.exports_root().join("batches") + } + + pub(crate) fn index_path(&self) -> PathBuf { + self.network_root().join("index.jsonl") + } + + pub(crate) fn state_path(&self) -> PathBuf { + self.network_root().join("state.json") + } +} + +impl R2PublishConfig { + fn normalize(mut self) -> anyhow::Result { + ensure!( + !self.bucket.trim().is_empty(), + "r2.bucket must not be empty" + ); + ensure!( + !self.account_id.trim().is_empty(), + "r2.account_id must not be empty" + ); + + self.bucket = self.bucket.trim().to_owned(); + self.prefix = self.prefix.trim_matches('/').to_owned(); + self.account_id = self.account_id.trim().to_owned(); + Ok(self) + } + + pub(crate) fn endpoint_url(&self) -> String { + format!("https://{}.r2.cloudflarestorage.com", self.account_id) + } +} + +fn endpoint_from_config_or_env(file_value: Option, env_name: &str) -> Option { + file_value + .filter(|value| !value.trim().is_empty()) + .or_else(|| { + env::var(env_name) + .ok() + .filter(|value| !value.trim().is_empty()) + }) +} + +fn parse_duration_or_default( + value: Option<&str>, + default: Duration, + label: &str, +) -> anyhow::Result { + let Some(value) = value else { + return Ok(default); + }; + let duration = humantime::parse_duration(value) + .with_context(|| format!("failed to parse {label} duration `{value}`"))?; + ensure!(!duration.is_zero(), "{label} must be greater than zero",); + Ok(duration) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_defaults_and_r2_config() { + let config = CollectorConfig::from_toml_str( + r#" +network = "glamsterdam-devnet-5" +cl_url = "http://cl" +el_url = "http://el" + +[r2] +bucket = "stateless-inputs" +prefix = "/devnets/" +account_id = "abc123" +"#, + ) + .unwrap(); + + assert_eq!(config.network, "glamsterdam-devnet-5"); + assert_eq!(config.out_root, PathBuf::from(DEFAULT_OUT_ROOT)); + assert_eq!(config.poll_interval, DEFAULT_POLL_INTERVAL); + assert_eq!(config.batch_size, DEFAULT_BATCH_SIZE); + let r2 = config.r2.unwrap(); + assert_eq!(r2.bucket, "stateless-inputs"); + assert_eq!(r2.prefix, "devnets"); + assert_eq!(r2.account_id, "abc123"); + assert_eq!(r2.endpoint_url(), "https://abc123.r2.cloudflarestorage.com"); + } + + #[test] + fn parses_custom_duration_values() { + let config = CollectorConfig::from_toml_str( + r#" +network = "glamsterdam-devnet-5" +cl_url = "http://cl" +el_url = "http://el" +out_root = "/tmp/stateless" +poll_interval = "10s" +request_timeout = "45s" +batch_size = 100 +"#, + ) + .unwrap(); + + assert_eq!(config.out_root, PathBuf::from("/tmp/stateless")); + assert_eq!(config.poll_interval, Duration::from_secs(10)); + assert_eq!(config.request_timeout, Duration::from_secs(45)); + assert_eq!(config.batch_size, 100); + } + + #[test] + fn endpoint_resolution_prefers_config_over_env() { + let from_config = + endpoint_from_config_or_env(Some("http://from-config".to_owned()), "CL_RPC_URL"); + + assert_eq!(from_config.as_deref(), Some("http://from-config")); + } +} diff --git a/crates/witness-generator-spec-cli/src/export.rs b/crates/witness-generator-spec-cli/src/export.rs new file mode 100644 index 00000000..74555f41 --- /dev/null +++ b/crates/witness-generator-spec-cli/src/export.rs @@ -0,0 +1,381 @@ +use std::{ + collections::{BTreeMap, BTreeSet}, + fs, + path::{Path, PathBuf}, +}; + +use anyhow::Context; +use serde::{Deserialize, Serialize}; +use tar::{Builder, Header}; + +use crate::{ + artifact::{ + self, StatelessInputArtifact, file_sha256_hex, path_to_slash_string, read_artifact, + }, + config::CollectorConfig, +}; + +const ZSTD_LEVEL: i32 = 3; + +#[derive(Debug, Clone)] +struct ArtifactFile { + path: PathBuf, + relative_path: PathBuf, + artifact: StatelessInputArtifact, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct BatchManifest { + schema_version: u64, + network: String, + batch_start_block: u64, + batch_end_block: u64, + batch_size: u64, + artifact_count: usize, + created_at: String, + artifacts: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +struct BatchManifestArtifact { + archive_path: String, + block_number: u64, + block_hash: String, + slot_number: u64, + chain_id: u64, + stateless_input_sha256: String, + stateless_input_byte_length: usize, + compressed_file_sha256: String, + compressed_file_byte_length: u64, +} + +pub(crate) fn export_batches( + config: &CollectorConfig, + force: bool, +) -> anyhow::Result> { + let by_block = artifacts_by_block(config)?; + let complete_batches = complete_batch_starts(&by_block, config.batch_size); + let mut exported = Vec::new(); + + fs::create_dir_all(config.batches_root()).with_context(|| { + format!( + "failed to create batch export directory {}", + config.batches_root().display() + ) + })?; + + for start in complete_batches { + let end = start + config.batch_size - 1; + let out_path = config.batches_root().join(format!("{start}-{end}.tar.zst")); + if out_path.exists() && !force { + continue; + } + + let artifacts = batch_artifacts(&by_block, start, end); + write_batch_archive(config, start, end, &artifacts, &out_path, force)?; + exported.push(out_path); + } + + Ok(exported) +} + +fn artifacts_by_block( + config: &CollectorConfig, +) -> anyhow::Result>> { + let mut files = Vec::new(); + collect_artifact_files(&config.blocks_root(), &mut files)?; + files.sort(); + + let mut by_block: BTreeMap> = BTreeMap::new(); + for path in files { + let artifact = read_artifact(&path)?; + let relative_path = path + .strip_prefix(config.blocks_root()) + .with_context(|| { + format!( + "artifact {} is not under {}", + path.display(), + config.blocks_root().display() + ) + })? + .to_path_buf(); + by_block + .entry(artifact.block_number) + .or_default() + .push(ArtifactFile { + path, + relative_path, + artifact, + }); + } + + for files in by_block.values_mut() { + files.sort_by(|left, right| left.relative_path.cmp(&right.relative_path)); + } + + Ok(by_block) +} + +fn complete_batch_starts(by_block: &BTreeMap>, batch_size: u64) -> Vec { + let starts = by_block + .keys() + .map(|block_number| (block_number / batch_size) * batch_size) + .collect::>(); + + starts + .into_iter() + .filter(|start| { + let end = start + batch_size - 1; + (*start..=end).all(|block_number| by_block.contains_key(&block_number)) + }) + .collect() +} + +fn batch_artifacts( + by_block: &BTreeMap>, + start: u64, + end: u64, +) -> Vec { + (start..=end) + .flat_map(|block_number| by_block.get(&block_number).into_iter().flatten()) + .cloned() + .collect() +} + +fn write_batch_archive( + config: &CollectorConfig, + start: u64, + end: u64, + artifacts: &[ArtifactFile], + out_path: &Path, + force: bool, +) -> anyhow::Result<()> { + let manifest = build_manifest(config, start, end, artifacts)?; + let part_path = archive_part_path(out_path); + if let Some(parent) = part_path.parent() { + fs::create_dir_all(parent) + .with_context(|| format!("failed to create directory {}", parent.display()))?; + } + + let file = fs::File::create(&part_path) + .with_context(|| format!("failed to create partial archive {}", part_path.display()))?; + let encoder = zstd::stream::write::Encoder::new(file, ZSTD_LEVEL) + .context("failed to create zstd encoder")?; + let mut tar = Builder::new(encoder); + + for artifact in artifacts { + let archive_path = PathBuf::from("blocks").join(&artifact.relative_path); + tar.append_path_with_name(&artifact.path, &archive_path) + .with_context(|| { + format!( + "failed to append {} as {}", + artifact.path.display(), + archive_path.display() + ) + })?; + } + + let manifest_bytes = + serde_json::to_vec_pretty(&manifest).context("failed to serialize batch manifest")?; + let mut header = Header::new_gnu(); + header.set_size(manifest_bytes.len() as u64); + header.set_mode(0o644); + header.set_cksum(); + tar.append_data(&mut header, "manifest.json", manifest_bytes.as_slice()) + .context("failed to append batch manifest")?; + + let encoder = tar.into_inner().context("failed to finish tar archive")?; + encoder.finish().context("failed to finish zstd archive")?; + + if out_path.exists() && force { + fs::remove_file(out_path) + .with_context(|| format!("failed to replace archive {}", out_path.display()))?; + } + fs::rename(&part_path, out_path).with_context(|| { + format!( + "failed to atomically rename {} to {}", + part_path.display(), + out_path.display() + ) + })?; + + Ok(()) +} + +fn build_manifest( + config: &CollectorConfig, + start: u64, + end: u64, + artifacts: &[ArtifactFile], +) -> anyhow::Result { + let artifacts = artifacts + .iter() + .map(|file| { + let archive_path = PathBuf::from("blocks").join(&file.relative_path); + Ok(BatchManifestArtifact { + archive_path: path_to_slash_string(&archive_path), + block_number: file.artifact.block_number, + block_hash: file.artifact.block_hash.clone(), + slot_number: file.artifact.slot_number, + chain_id: file.artifact.chain_id, + stateless_input_sha256: file.artifact.stateless_input_sha256.clone(), + stateless_input_byte_length: file.artifact.stateless_input_byte_length, + compressed_file_sha256: file_sha256_hex(&file.path)?, + compressed_file_byte_length: fs::metadata(&file.path) + .with_context(|| format!("failed to stat {}", file.path.display()))? + .len(), + }) + }) + .collect::>>()?; + + Ok(BatchManifest { + schema_version: 1, + network: config.network.clone(), + batch_start_block: start, + batch_end_block: end, + batch_size: config.batch_size, + artifact_count: artifacts.len(), + created_at: artifact::utc_now_rfc3339()?, + artifacts, + }) +} + +fn collect_artifact_files(dir: &Path, files: &mut Vec) -> anyhow::Result<()> { + if !dir.exists() { + return Ok(()); + } + for entry in + fs::read_dir(dir).with_context(|| format!("failed to read directory {}", dir.display()))? + { + let entry = entry.with_context(|| format!("failed to read entry in {}", dir.display()))?; + let path = entry.path(); + let file_type = entry + .file_type() + .with_context(|| format!("failed to read file type for {}", path.display()))?; + if file_type.is_dir() { + collect_artifact_files(&path, files)?; + } else if is_artifact_file(&path) { + files.push(path); + } + } + Ok(()) +} + +fn is_artifact_file(path: &Path) -> bool { + path.file_name() + .and_then(|name| name.to_str()) + .is_some_and(|name| name.ends_with(".json.zst")) +} + +fn archive_part_path(path: &Path) -> PathBuf { + path.with_extension( + match path.extension().and_then(|extension| extension.to_str()) { + Some(extension) => format!("{extension}.part"), + None => "part".to_owned(), + }, + ) +} + +#[cfg(test)] +mod tests { + use std::io::Read; + + use alloy_primitives::B256; + use tar::Archive; + use witness_generator_spec_cli::GeneratedInput; + + use crate::artifact::{StatelessInputArtifact, write_artifact_atomic}; + + use super::*; + + #[test] + fn exports_complete_batches_with_manifest() { + let config = test_config("complete_batch", 2); + write_generated_artifact(&config, 0, B256::repeat_byte(0xaa)); + write_generated_artifact(&config, 1, B256::repeat_byte(0xbb)); + write_generated_artifact(&config, 2, B256::repeat_byte(0xcc)); + + let exported = export_batches(&config, false).unwrap(); + + assert_eq!(exported.len(), 1); + assert_eq!( + exported[0].file_name().and_then(|name| name.to_str()), + Some("0-1.tar.zst") + ); + let manifest = read_manifest_from_archive(&exported[0]); + assert_eq!(manifest.batch_start_block, 0); + assert_eq!(manifest.batch_end_block, 1); + assert_eq!(manifest.artifact_count, 2); + assert!( + manifest + .artifacts + .iter() + .all(|artifact| artifact.archive_path.starts_with("blocks/")) + ); + } + + #[test] + fn skips_existing_batch_without_force() { + let config = test_config("skip_existing", 2); + write_generated_artifact(&config, 0, B256::repeat_byte(0xaa)); + write_generated_artifact(&config, 1, B256::repeat_byte(0xbb)); + + assert_eq!(export_batches(&config, false).unwrap().len(), 1); + assert!(export_batches(&config, false).unwrap().is_empty()); + assert_eq!(export_batches(&config, true).unwrap().len(), 1); + } + + fn write_generated_artifact(config: &CollectorConfig, block_number: u64, block_hash: B256) { + let generated = GeneratedInput { + bytes: vec![0x00, 0x01, block_number as u8], + block_hash, + block_number, + slot_number: block_number + 100, + chain_id: 1, + }; + let artifact = StatelessInputArtifact::from_generated_at( + &config.network, + "head", + &generated, + "2026-06-11T00:00:00Z", + "test-commit".to_owned(), + ) + .unwrap(); + write_artifact_atomic(&config.blocks_root(), &artifact).unwrap(); + } + + fn read_manifest_from_archive(path: &Path) -> BatchManifest { + let file = fs::File::open(path).unwrap(); + let decoder = zstd::stream::read::Decoder::new(file).unwrap(); + let mut archive = Archive::new(decoder); + for entry in archive.entries().unwrap() { + let mut entry = entry.unwrap(); + if entry.path().unwrap().as_ref() == Path::new("manifest.json") { + let mut bytes = Vec::new(); + entry.read_to_end(&mut bytes).unwrap(); + return serde_json::from_slice(&bytes).unwrap(); + } + } + panic!("manifest not found"); + } + + fn test_config(name: &str, batch_size: u64) -> CollectorConfig { + let out_root = std::env::temp_dir().join(format!( + "witness-generator-spec-cli-export-{name}-{}", + std::process::id() + )); + let _ = fs::remove_dir_all(&out_root); + CollectorConfig { + network: "glamsterdam-devnet-5".to_owned(), + cl_url: "http://cl".to_owned(), + el_url: "http://el".to_owned(), + out_root, + poll_interval: std::time::Duration::from_secs(4), + request_timeout: std::time::Duration::from_secs(30), + batch_size, + r2: None, + } + } +} diff --git a/crates/witness-generator-spec-cli/src/lib.rs b/crates/witness-generator-spec-cli/src/lib.rs new file mode 100644 index 00000000..c840cb57 --- /dev/null +++ b/crates/witness-generator-spec-cli/src/lib.rs @@ -0,0 +1,195 @@ +//! Build canonical Amsterdam stateless guest input bytes from live network RPC data. + +mod builder; +mod chain_config; +mod rpc; +mod serde_helpers; + +use std::time::Duration; + +use alloy_primitives::B256; +use anyhow::{Context, ensure}; +pub use builder::GeneratedInput; +use reqwest::Client; + +// These dependencies are used by this package's CLI target. +use clap as _; +use humantime as _; +use sha2 as _; +use tar as _; +use time as _; +use tokio as _; +use toml as _; +use tracing as _; +use tracing_subscriber as _; +use zstd as _; + +/// Configuration for consensus-layer and execution-layer RPC access. +#[derive(Debug, Clone)] +pub struct NetworkWitnessConfig { + /// Consensus-layer Beacon API endpoint. + pub cl_endpoint: String, + /// Execution-layer JSON-RPC endpoint. + pub el_endpoint: String, + /// Request timeout. + pub timeout: Duration, + /// Extra HTTP headers sent to the consensus-layer endpoint. + pub cl_headers: Vec<(String, String)>, + /// Extra HTTP headers sent to the execution-layer endpoint. + pub el_headers: Vec<(String, String)>, +} + +impl NetworkWitnessConfig { + /// Creates a config with a conservative default timeout and no custom headers. + pub fn new(cl_endpoint: impl Into, el_endpoint: impl Into) -> Self { + Self { + cl_endpoint: cl_endpoint.into(), + el_endpoint: el_endpoint.into(), + timeout: Duration::from_secs(30), + cl_headers: Vec::new(), + el_headers: Vec::new(), + } + } +} + +/// Block selection for canonical stateless input generation. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub enum BlockSelector { + /// The current consensus head block. + #[default] + Head, + /// A Beacon API block id such as `head`, `finalized`, a slot, or a block root. + BeaconBlockId(String), + /// An execution block number. The matching CL slot is derived from the EL block timestamp. + ExecutionBlockNumber(u64), +} + +/// Client for fetching network data and building canonical stateless input bytes. +#[derive(Debug, Clone)] +pub struct NetworkWitnessClient { + rpc: rpc::RpcClient, +} + +impl NetworkWitnessClient { + /// Creates a new network witness client. + pub fn new(config: NetworkWitnessConfig) -> anyhow::Result { + let http = Client::builder() + .timeout(config.timeout) + .build() + .context("failed to build HTTP client")?; + + Ok(Self { + rpc: rpc::RpcClient::new(config, http), + }) + } + + /// Fetches network data for `selector` and returns canonical spec guest input bytes. + pub async fn stateless_input_bytes( + &self, + selector: BlockSelector, + ) -> anyhow::Result { + let envelope = self.fetch_payload_envelope(selector).await?; + let block_hash = envelope.payload.block_hash; + + let witness = self + .rpc + .debug_execution_witness_by_block_hash(block_hash) + .await + .with_context(|| format!("failed to fetch execution witness for {block_hash}"))?; + let chain_id = self.rpc.eth_chain_id().await?; + + builder::build_generated_input(envelope, witness, chain_id) + } + + async fn fetch_payload_envelope( + &self, + selector: BlockSelector, + ) -> anyhow::Result { + match selector { + BlockSelector::Head => self.rpc.execution_payload_envelope("head").await, + BlockSelector::BeaconBlockId(block_id) => { + self.rpc.execution_payload_envelope(&block_id).await + } + BlockSelector::ExecutionBlockNumber(number) => { + self.fetch_payload_envelope_for_execution_number(number) + .await + } + } + } + + async fn fetch_payload_envelope_for_execution_number( + &self, + number: u64, + ) -> anyhow::Result { + let el_block = self.rpc.eth_block_by_number(number).await?; + ensure!( + el_block.number == number, + "EL returned block number {} for requested block number {}", + el_block.number, + number, + ); + + let genesis = self.rpc.beacon_genesis().await?; + let spec = self.rpc.beacon_spec().await?; + ensure!( + spec.seconds_per_slot > 0, + "CL spec SECONDS_PER_SLOT must be greater than zero", + ); + ensure!( + el_block.timestamp >= genesis.genesis_time, + "EL block timestamp {} is before CL genesis time {}", + el_block.timestamp, + genesis.genesis_time, + ); + let slot = (el_block.timestamp - genesis.genesis_time) / spec.seconds_per_slot; + + let envelope = self + .rpc + .execution_payload_envelope(&slot.to_string()) + .await?; + ensure_matching_hash(envelope.payload.block_hash, el_block.hash, number, slot)?; + Ok(envelope) + } +} + +fn ensure_matching_hash( + cl_hash: B256, + el_hash: B256, + number: u64, + slot: u64, +) -> anyhow::Result<()> { + ensure!( + cl_hash == el_hash, + "CL payload envelope at slot {slot} has execution block hash {cl_hash}, expected EL block #{number} hash {el_hash}", + ); + Ok(()) +} + +#[cfg(test)] +mod tests { + use stateless_validator_common::guest::input::STATELESS_INPUT_SCHEMA_ID; + + use super::*; + + #[tokio::test] + #[ignore] + async fn live_generation_from_env() -> anyhow::Result<()> { + let (Ok(cl_endpoint), Ok(el_endpoint)) = + (std::env::var("CL_RPC_URL"), std::env::var("EL_RPC_URL")) + else { + return Ok(()); + }; + + let client = + NetworkWitnessClient::new(NetworkWitnessConfig::new(cl_endpoint, el_endpoint))?; + let generated = client.stateless_input_bytes(BlockSelector::Head).await?; + + assert!( + generated + .bytes + .starts_with(&STATELESS_INPUT_SCHEMA_ID.to_be_bytes()) + ); + assert!(generated.block_number > 0); + Ok(()) + } +} diff --git a/crates/witness-generator-spec-cli/src/main.rs b/crates/witness-generator-spec-cli/src/main.rs new file mode 100644 index 00000000..e402ceec --- /dev/null +++ b/crates/witness-generator-spec-cli/src/main.rs @@ -0,0 +1,265 @@ +//! CLI entry point for generating and collecting canonical stateless input bytes. + +#![allow( + unused_crate_dependencies, + reason = "library dependencies are compiled separately from this CLI target" +)] + +mod artifact; +mod catalog; +mod collector; +mod config; +mod export; +mod publish; + +use std::{ + fs, + io::{self, Write}, + path::PathBuf, +}; + +use alloy_primitives::hex; +use anyhow::Context; +use clap::{Args, Parser, Subcommand, ValueEnum}; +use config::CollectorConfig; +use tracing::info; +use tracing_subscriber::EnvFilter; +use witness_generator_spec_cli::{BlockSelector, NetworkWitnessClient, NetworkWitnessConfig}; + +#[derive(Debug, Parser)] +#[command( + name = "witness-generator-spec-cli", + about = "Generate and collect canonical Amsterdam stateless guest input bytes from CL/EL RPC endpoints.", + long_about = None +)] +struct Cli { + #[command(subcommand)] + command: Option, + + /// Consensus-layer Beacon API endpoint for legacy one-shot generation. + #[arg(long)] + cl_url: Option, + /// Execution-layer JSON-RPC endpoint for legacy one-shot generation. + #[arg(long)] + el_url: Option, + /// Beacon API block id: head, finalized, slot, or block root. Defaults to head. + #[arg(long)] + block_id: Option, + /// Execution block number to resolve to a CL slot via block timestamp. + #[arg(long, conflicts_with = "block_id")] + execution_block_number: Option, + /// Output encoding. + #[arg(long, value_enum, default_value_t = OutputFormat::Hex)] + format: OutputFormat, + /// Output file. Stdout is used when omitted. + #[arg(long)] + out: Option, +} + +#[derive(Debug, Subcommand)] +enum Command { + /// Generate canonical stateless input bytes once. + Generate(GenerateArgs), + /// Poll the live network head and store one artifact per observed block. + Collect(CollectArgs), + /// Package complete local block ranges into downloadable batch archives. + Export(ExportArgs), + /// Publish exported batches and indexes to Cloudflare R2. + PublishR2(PublishR2Args), +} + +#[derive(Debug, Clone, Args)] +struct GenerateArgs { + /// Consensus-layer Beacon API endpoint. + #[arg(long)] + cl_url: String, + /// Execution-layer JSON-RPC endpoint. + #[arg(long)] + el_url: String, + /// Beacon API block id: head, finalized, slot, or block root. Defaults to head. + #[arg(long)] + block_id: Option, + /// Execution block number to resolve to a CL slot via block timestamp. + #[arg(long, conflicts_with = "block_id")] + execution_block_number: Option, + /// Output encoding. + #[arg(long, value_enum, default_value_t = OutputFormat::Hex)] + format: OutputFormat, + /// Output file. Stdout is used when omitted. + #[arg(long)] + out: Option, +} + +#[derive(Debug, Clone, Args)] +struct CollectArgs { + /// TOML config path. + #[arg(long)] + config: PathBuf, + /// Collect one head block and exit. + #[arg(long)] + once: bool, +} + +#[derive(Debug, Clone, Args)] +struct ExportArgs { + /// TOML config path. + #[arg(long)] + config: PathBuf, + /// Rebuild existing batch archives. + #[arg(long)] + force: bool, +} + +#[derive(Debug, Clone, Args)] +struct PublishR2Args { + /// TOML config path. + #[arg(long)] + config: PathBuf, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)] +enum OutputFormat { + Hex, + Raw, +} + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::from_default_env()) + .with_writer(io::stderr) + .init(); + + let cli = Cli::parse(); + match cli.command { + Some(Command::Generate(args)) => run_generate(args).await, + Some(Command::Collect(args)) => { + let config = CollectorConfig::from_path(args.config)?; + collector::collect(config, args.once).await + } + Some(Command::Export(args)) => { + let config = CollectorConfig::from_path(args.config)?; + let exported = export::export_batches(&config, args.force)?; + let catalog = catalog::generate_catalog(&config)?; + info!(count = exported.len(), "exported batch archives"); + info!( + artifacts = catalog.artifact_count, + batches = catalog.batch_count, + "generated public catalog" + ); + Ok(()) + } + Some(Command::PublishR2(args)) => { + let config = CollectorConfig::from_path(args.config)?; + publish::publish_r2(&config) + } + None => run_generate(cli.into_generate_args()?).await, + } +} + +impl Cli { + fn into_generate_args(self) -> anyhow::Result { + Ok(GenerateArgs { + cl_url: self + .cl_url + .context("--cl-url is required when no subcommand is used")?, + el_url: self + .el_url + .context("--el-url is required when no subcommand is used")?, + block_id: self.block_id, + execution_block_number: self.execution_block_number, + format: self.format, + out: self.out, + }) + } +} + +async fn run_generate(args: GenerateArgs) -> anyhow::Result<()> { + let selector = block_selector(args.block_id.as_deref(), args.execution_block_number); + let client = NetworkWitnessClient::new(NetworkWitnessConfig::new(args.cl_url, args.el_url))?; + let generated = client.stateless_input_bytes(selector).await?; + let output = match args.format { + OutputFormat::Hex => format!("0x{}\n", hex::encode(&generated.bytes)).into_bytes(), + OutputFormat::Raw => generated.bytes, + }; + + if let Some(path) = args.out { + fs::write(path, output)?; + } else { + io::stdout().write_all(&output)?; + } + + Ok(()) +} + +fn block_selector(block_id: Option<&str>, execution_block_number: Option) -> BlockSelector { + match execution_block_number { + Some(number) => BlockSelector::ExecutionBlockNumber(number), + None => match block_id.unwrap_or("head") { + "head" => BlockSelector::Head, + other => BlockSelector::BeaconBlockId(other.to_owned()), + }, + } +} + +#[cfg(test)] +mod tests { + use clap::CommandFactory; + + use super::*; + + #[test] + fn parses_legacy_generate_invocation() { + let cli = Cli::try_parse_from([ + "witness-generator-spec-cli", + "--cl-url", + "http://cl", + "--el-url", + "http://el", + ]) + .unwrap(); + + assert!(cli.command.is_none()); + let args = cli.into_generate_args().unwrap(); + assert_eq!(args.cl_url, "http://cl"); + assert_eq!(args.el_url, "http://el"); + } + + #[test] + fn parses_generate_subcommand() { + let cli = Cli::try_parse_from([ + "witness-generator-spec-cli", + "generate", + "--cl-url", + "http://cl", + "--el-url", + "http://el", + "--format", + "raw", + ]) + .unwrap(); + + let Some(Command::Generate(args)) = cli.command else { + panic!("expected generate subcommand"); + }; + assert_eq!(args.format, OutputFormat::Raw); + } + + #[test] + fn parses_operational_subcommands() { + for command in ["collect", "export", "publish-r2"] { + Cli::try_parse_from([ + "witness-generator-spec-cli", + command, + "--config", + "/etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml", + ]) + .unwrap(); + } + } + + #[test] + fn clap_definition_is_valid() { + Cli::command().debug_assert(); + } +} diff --git a/crates/witness-generator-spec-cli/src/publish.rs b/crates/witness-generator-spec-cli/src/publish.rs new file mode 100644 index 00000000..329eeeae --- /dev/null +++ b/crates/witness-generator-spec-cli/src/publish.rs @@ -0,0 +1,279 @@ +use std::{path::PathBuf, process::Command}; + +use anyhow::{Context, bail}; +use tracing::info; + +use crate::{ + catalog, + config::{CollectorConfig, R2PublishConfig}, +}; + +const STALE_PUBLIC_OBJECTS: &[&str] = &["blocks.jsonl", "index.jsonl"]; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct AwsCommand { + pub(crate) program: String, + pub(crate) args: Vec, +} + +pub(crate) fn publish_r2(config: &CollectorConfig) -> anyhow::Result<()> { + let commands = build_publish_commands(config)?; + for command in commands { + info!(program = command.program, args = ?command.args, "running R2 publish command"); + let status = Command::new(&command.program) + .args(&command.args) + .status() + .with_context(|| format!("failed to run {}", command.program))?; + if !status.success() { + bail!("{} failed with status {status}", command.program); + } + } + Ok(()) +} + +pub(crate) fn build_publish_commands(config: &CollectorConfig) -> anyhow::Result> { + let r2 = config + .r2 + .as_ref() + .context("r2 config is required for publish-r2")?; + if !config.batches_root().is_dir() { + bail!( + "batch export directory {} does not exist; run export first", + config.batches_root().display() + ); + } + let catalog_files = catalog::required_catalog_files(config); + for (_, path) in &catalog_files { + if !path.is_file() { + bail!( + "public catalog file {} does not exist; run export first", + path.display() + ); + } + } + + let endpoint_url = r2.endpoint_url(); + let mut commands = vec![aws_s3_sync_command( + config.batches_root(), + r2_uri(r2, &config.network, "exports/batches"), + &endpoint_url, + )]; + + for (name, path) in catalog_files { + commands.push(aws_s3_cp_command( + path, + r2_uri(r2, &config.network, name), + &endpoint_url, + )); + } + + for stale_object in STALE_PUBLIC_OBJECTS { + commands.push(aws_s3_rm_command( + r2_uri(r2, &config.network, stale_object), + &endpoint_url, + )); + } + + Ok(commands) +} + +fn aws_s3_sync_command(source: PathBuf, destination: String, endpoint_url: &str) -> AwsCommand { + AwsCommand { + program: "aws".to_owned(), + args: vec![ + "s3".to_owned(), + "sync".to_owned(), + source.display().to_string(), + destination, + "--exclude".to_owned(), + "*.part".to_owned(), + "--endpoint-url".to_owned(), + endpoint_url.to_owned(), + ], + } +} + +fn aws_s3_cp_command(source: PathBuf, destination: String, endpoint_url: &str) -> AwsCommand { + AwsCommand { + program: "aws".to_owned(), + args: vec![ + "s3".to_owned(), + "cp".to_owned(), + source.display().to_string(), + destination, + "--endpoint-url".to_owned(), + endpoint_url.to_owned(), + ], + } +} + +fn aws_s3_rm_command(destination: String, endpoint_url: &str) -> AwsCommand { + AwsCommand { + program: "aws".to_owned(), + args: vec![ + "s3".to_owned(), + "rm".to_owned(), + destination, + "--endpoint-url".to_owned(), + endpoint_url.to_owned(), + ], + } +} + +fn r2_uri(r2: &R2PublishConfig, network: &str, suffix: &str) -> String { + let suffix = suffix.trim_matches('/'); + if r2.prefix.is_empty() { + format!("s3://{}/{network}/{suffix}", r2.bucket) + } else { + format!("s3://{}/{}/{network}/{suffix}", r2.bucket, r2.prefix) + } +} + +#[cfg(test)] +mod tests { + use std::{fs, time::Duration}; + + use crate::config::R2PublishConfig; + + use super::*; + + #[test] + fn builds_r2_publish_commands_without_invoking_aws() { + let config = test_config("commands"); + fs::create_dir_all(config.batches_root()).unwrap(); + fs::write(config.index_path(), "{}\n").unwrap(); + write_catalog_files(&config); + + let commands = build_publish_commands(&config).unwrap(); + + assert_eq!(commands.len(), 7); + assert_eq!(commands[0].program, "aws"); + assert_eq!( + commands[0].args, + vec![ + "s3", + "sync", + config.batches_root().to_str().unwrap(), + "s3://stateless-inputs/devnets/glamsterdam-devnet-5/exports/batches", + "--exclude", + "*.part", + "--endpoint-url", + "https://abc123.r2.cloudflarestorage.com", + ] + ); + assert_eq!( + commands[1].args, + vec![ + "s3", + "cp", + config.network_root().join("index.html").to_str().unwrap(), + "s3://stateless-inputs/devnets/glamsterdam-devnet-5/index.html", + "--endpoint-url", + "https://abc123.r2.cloudflarestorage.com", + ] + ); + assert_eq!( + commands[2].args, + vec![ + "s3", + "cp", + config + .network_root() + .join("manifest.json") + .to_str() + .unwrap(), + "s3://stateless-inputs/devnets/glamsterdam-devnet-5/manifest.json", + "--endpoint-url", + "https://abc123.r2.cloudflarestorage.com", + ] + ); + assert_eq!( + commands[3].args, + vec![ + "s3", + "cp", + config + .network_root() + .join("batches.jsonl") + .to_str() + .unwrap(), + "s3://stateless-inputs/devnets/glamsterdam-devnet-5/batches.jsonl", + "--endpoint-url", + "https://abc123.r2.cloudflarestorage.com", + ] + ); + assert_eq!( + commands[4].args, + vec![ + "s3", + "cp", + config.network_root().join("SHA256SUMS").to_str().unwrap(), + "s3://stateless-inputs/devnets/glamsterdam-devnet-5/SHA256SUMS", + "--endpoint-url", + "https://abc123.r2.cloudflarestorage.com", + ] + ); + assert_eq!( + commands[5].args, + vec![ + "s3", + "rm", + "s3://stateless-inputs/devnets/glamsterdam-devnet-5/blocks.jsonl", + "--endpoint-url", + "https://abc123.r2.cloudflarestorage.com", + ] + ); + assert_eq!( + commands[6].args, + vec![ + "s3", + "rm", + "s3://stateless-inputs/devnets/glamsterdam-devnet-5/index.jsonl", + "--endpoint-url", + "https://abc123.r2.cloudflarestorage.com", + ] + ); + } + + #[test] + fn publish_requires_generated_catalog_files() { + let config = test_config("missing_catalog"); + fs::create_dir_all(config.batches_root()).unwrap(); + + let error = build_publish_commands(&config).unwrap_err(); + + assert!( + error.to_string().contains("public catalog file"), + "{error:?}" + ); + assert!(error.to_string().contains("run export first")); + } + + fn write_catalog_files(config: &CollectorConfig) { + for name in catalog::REQUIRED_CATALOG_FILES { + fs::write(config.network_root().join(name), b"catalog").unwrap(); + } + } + + fn test_config(name: &str) -> CollectorConfig { + let out_root = std::env::temp_dir().join(format!( + "witness-generator-spec-cli-publish-{name}-{}", + std::process::id() + )); + let _ = fs::remove_dir_all(&out_root); + CollectorConfig { + network: "glamsterdam-devnet-5".to_owned(), + cl_url: "http://cl".to_owned(), + el_url: "http://el".to_owned(), + out_root, + poll_interval: Duration::from_secs(4), + request_timeout: Duration::from_secs(30), + batch_size: 500, + r2: Some(R2PublishConfig { + bucket: "stateless-inputs".to_owned(), + prefix: "devnets".to_owned(), + account_id: "abc123".to_owned(), + }), + } + } +} diff --git a/crates/witness-generator-spec-cli/src/rpc.rs b/crates/witness-generator-spec-cli/src/rpc.rs new file mode 100644 index 00000000..4017e5d0 --- /dev/null +++ b/crates/witness-generator-spec-cli/src/rpc.rs @@ -0,0 +1,441 @@ +//! Minimal Beacon API and JSON-RPC client/types for network witness generation. + +use alloy_primitives::{Address, B256, Bytes, FixedBytes}; +use anyhow::{Context, bail}; +use reqwest::{ + Client, RequestBuilder, + header::{HeaderName, HeaderValue}, +}; +use serde::{Deserialize, Serialize, de::DeserializeOwned}; +use serde_json::{Value, json}; + +use crate::{ + NetworkWitnessConfig, + serde_helpers::{de_u64, hex_quantity, parse_u64}, +}; + +#[derive(Debug, Clone)] +pub(crate) struct RpcClient { + config: NetworkWitnessConfig, + http: Client, +} + +#[derive(Debug, Clone, Deserialize)] +pub(crate) struct ExecutionPayloadEnvelopeResponse { + pub(crate) data: SignedExecutionPayloadEnvelope, +} + +#[derive(Debug, Clone, Deserialize)] +pub(crate) struct SignedExecutionPayloadEnvelope { + pub(crate) message: ExecutionPayloadEnvelope, +} + +#[derive(Debug, Clone, Deserialize)] +pub(crate) struct ExecutionPayloadEnvelope { + pub(crate) payload: BeaconExecutionPayload, + pub(crate) execution_requests: ExecutionRequestsJson, + pub(crate) parent_beacon_block_root: B256, +} + +#[derive(Debug, Clone, Deserialize)] +pub(crate) struct BeaconExecutionPayload { + pub(crate) parent_hash: B256, + pub(crate) fee_recipient: Address, + pub(crate) state_root: B256, + pub(crate) receipts_root: B256, + pub(crate) logs_bloom: FixedBytes<256>, + pub(crate) prev_randao: B256, + #[serde(deserialize_with = "de_u64")] + pub(crate) block_number: u64, + #[serde(deserialize_with = "de_u64")] + pub(crate) gas_limit: u64, + #[serde(deserialize_with = "de_u64")] + pub(crate) gas_used: u64, + #[serde(deserialize_with = "de_u64")] + pub(crate) timestamp: u64, + pub(crate) extra_data: Bytes, + #[serde(deserialize_with = "crate::serde_helpers::de_u256")] + pub(crate) base_fee_per_gas: alloy_primitives::U256, + pub(crate) block_hash: B256, + #[serde(default)] + pub(crate) transactions: Vec, + #[serde(default)] + pub(crate) withdrawals: Vec, + #[serde(deserialize_with = "de_u64")] + pub(crate) blob_gas_used: u64, + #[serde(deserialize_with = "de_u64")] + pub(crate) excess_blob_gas: u64, + pub(crate) block_access_list: Bytes, + #[serde(deserialize_with = "de_u64")] + pub(crate) slot_number: u64, +} + +#[derive(Debug, Clone, Deserialize)] +pub(crate) struct WithdrawalJson { + #[serde(deserialize_with = "de_u64")] + pub(crate) index: u64, + #[serde(deserialize_with = "de_u64")] + pub(crate) validator_index: u64, + pub(crate) address: Address, + #[serde(deserialize_with = "de_u64")] + pub(crate) amount: u64, +} + +#[derive(Debug, Clone, Default, Deserialize)] +pub(crate) struct ExecutionRequestsJson { + #[serde(default)] + pub(crate) deposits: Vec, + #[serde(default)] + pub(crate) withdrawals: Vec, + #[serde(default)] + pub(crate) consolidations: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +pub(crate) struct DepositRequestJson { + pub(crate) pubkey: FixedBytes<48>, + pub(crate) withdrawal_credentials: B256, + #[serde(deserialize_with = "de_u64")] + pub(crate) amount: u64, + pub(crate) signature: FixedBytes<96>, + #[serde(deserialize_with = "de_u64")] + pub(crate) index: u64, +} + +#[derive(Debug, Clone, Deserialize)] +pub(crate) struct WithdrawalRequestJson { + pub(crate) source_address: Address, + pub(crate) validator_pubkey: FixedBytes<48>, + #[serde(deserialize_with = "de_u64")] + pub(crate) amount: u64, +} + +#[derive(Debug, Clone, Deserialize)] +pub(crate) struct ConsolidationRequestJson { + pub(crate) source_address: Address, + pub(crate) source_pubkey: FixedBytes<48>, + pub(crate) target_pubkey: FixedBytes<48>, +} + +#[derive(Debug, Clone, Deserialize)] +pub(crate) struct RpcExecutionWitness { + #[serde(default)] + pub(crate) state: Vec, + #[serde(default)] + #[allow(dead_code)] + pub(crate) keys: Vec, + #[serde(default)] + pub(crate) codes: Vec, + #[serde(default)] + pub(crate) headers: Vec, +} + +#[derive(Debug, Clone)] +pub(crate) struct ElBlock { + pub(crate) hash: B256, + pub(crate) number: u64, + pub(crate) timestamp: u64, +} + +#[derive(Debug, Clone, Deserialize)] +struct ElBlockRpc { + hash: B256, + #[serde(deserialize_with = "de_u64")] + number: u64, + #[serde(deserialize_with = "de_u64")] + timestamp: u64, +} + +#[derive(Debug, Clone)] +pub(crate) struct BeaconGenesis { + pub(crate) genesis_time: u64, +} + +#[derive(Debug, Clone, Deserialize)] +struct BeaconGenesisResponse { + data: BeaconGenesisData, +} + +#[derive(Debug, Clone, Deserialize)] +struct BeaconGenesisData { + #[serde(deserialize_with = "de_u64")] + genesis_time: u64, +} + +#[derive(Debug, Clone)] +pub(crate) struct BeaconSpec { + pub(crate) seconds_per_slot: u64, +} + +#[derive(Debug, Clone, Deserialize)] +struct BeaconSpecResponse { + data: BeaconSpecData, +} + +#[derive(Debug, Clone, Deserialize)] +struct BeaconSpecData { + #[serde(rename = "SECONDS_PER_SLOT", deserialize_with = "de_u64")] + seconds_per_slot: u64, +} + +#[derive(Debug, Serialize)] +struct JsonRpcRequest<'a> { + jsonrpc: &'static str, + id: u64, + method: &'a str, + params: Value, +} + +#[derive(Debug, Deserialize)] +struct JsonRpcResponse { + result: Option, + error: Option, +} + +#[derive(Debug, Deserialize)] +struct JsonRpcError { + code: i64, + message: String, +} + +impl RpcClient { + pub(crate) const fn new(config: NetworkWitnessConfig, http: Client) -> Self { + Self { config, http } + } + + pub(crate) async fn execution_payload_envelope( + &self, + block_id: &str, + ) -> anyhow::Result { + let url = execution_payload_envelope_url(&self.config.cl_endpoint, block_id); + let response: ExecutionPayloadEnvelopeResponse = self + .send_get_with_headers(&url, &self.config.cl_headers) + .await + .with_context(|| format!("failed to fetch execution payload envelope `{block_id}`"))?; + Ok(response.data.message) + } + + pub(crate) async fn beacon_genesis(&self) -> anyhow::Result { + let url = format!( + "{}/eth/v1/beacon/genesis", + trim_endpoint(&self.config.cl_endpoint) + ); + let response: BeaconGenesisResponse = self + .send_get_with_headers(&url, &self.config.cl_headers) + .await + .context("failed to fetch CL genesis")?; + Ok(BeaconGenesis { + genesis_time: response.data.genesis_time, + }) + } + + pub(crate) async fn beacon_spec(&self) -> anyhow::Result { + let url = format!( + "{}/eth/v1/config/spec", + trim_endpoint(&self.config.cl_endpoint) + ); + let response: BeaconSpecResponse = self + .send_get_with_headers(&url, &self.config.cl_headers) + .await + .context("failed to fetch CL spec")?; + Ok(BeaconSpec { + seconds_per_slot: response.data.seconds_per_slot, + }) + } + + pub(crate) async fn debug_execution_witness_by_block_hash( + &self, + block_hash: B256, + ) -> anyhow::Result { + self.el_rpc( + "debug_executionWitnessByBlockHash", + json!([block_hash.to_string()]), + ) + .await + } + + pub(crate) async fn eth_chain_id(&self) -> anyhow::Result { + let chain_id: String = self.el_rpc("eth_chainId", json!([])).await?; + parse_u64(&chain_id).context("failed to parse eth_chainId") + } + + pub(crate) async fn eth_block_by_number(&self, number: u64) -> anyhow::Result { + let result: Option = self + .el_rpc("eth_getBlockByNumber", json!([hex_quantity(number), false])) + .await?; + let block = result.with_context(|| format!("EL block #{number} not found"))?; + Ok(ElBlock { + hash: block.hash, + number: block.number, + timestamp: block.timestamp, + }) + } + + async fn el_rpc(&self, method: &str, params: Value) -> anyhow::Result + where + T: DeserializeOwned, + { + let request = JsonRpcRequest { + jsonrpc: "2.0", + id: 1, + method, + params, + }; + let url = trim_endpoint(&self.config.el_endpoint).to_owned(); + let builder = self.http.post(url).json(&request); + let response: JsonRpcResponse = apply_headers(builder, &self.config.el_headers)? + .send() + .await + .with_context(|| format!("JSON-RPC method `{method}` failed"))? + .error_for_status() + .with_context(|| format!("JSON-RPC method `{method}` returned HTTP error"))? + .json() + .await + .with_context(|| format!("failed to decode JSON-RPC response for `{method}`"))?; + + if let Some(error) = response.error { + bail!( + "JSON-RPC method `{method}` returned error {}: {}", + error.code, + error.message + ); + } + response + .result + .with_context(|| format!("JSON-RPC method `{method}` returned no result")) + } + + async fn send_get_with_headers( + &self, + url: &str, + headers: &[(String, String)], + ) -> anyhow::Result + where + T: DeserializeOwned, + { + let builder = self.http.get(url); + apply_headers(builder, headers)? + .send() + .await? + .error_for_status()? + .json() + .await + .context("failed to decode JSON response") + } +} + +fn apply_headers( + mut builder: RequestBuilder, + headers: &[(String, String)], +) -> anyhow::Result { + for (name, value) in headers { + let name = HeaderName::from_bytes(name.as_bytes()) + .with_context(|| format!("invalid HTTP header name `{name}`"))?; + let value = HeaderValue::from_str(value) + .with_context(|| format!("invalid value for HTTP header `{name}`"))?; + builder = builder.header(name, value); + } + Ok(builder) +} + +fn trim_endpoint(endpoint: &str) -> &str { + endpoint.trim_end_matches('/') +} + +fn execution_payload_envelope_url(endpoint: &str, block_id: &str) -> String { + format!( + "{}/eth/v1/beacon/execution_payload_envelopes/{}", + trim_endpoint(endpoint), + block_id, + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn builds_plural_execution_payload_envelope_url() { + assert_eq!( + execution_payload_envelope_url("http://127.0.0.1:3500/", "head"), + "http://127.0.0.1:3500/eth/v1/beacon/execution_payload_envelopes/head", + ); + } + + #[test] + fn parses_execution_requests_with_mixed_quantities() { + let json = serde_json::json!({ + "deposits": [{ + "pubkey": format!("0x{}", "11".repeat(48)), + "withdrawal_credentials": format!("0x{}", "22".repeat(32)), + "amount": "32000000000", + "signature": format!("0x{}", "33".repeat(96)), + "index": "0x2a" + }], + "withdrawals": [{ + "source_address": format!("0x{}", "44".repeat(20)), + "validator_pubkey": format!("0x{}", "55".repeat(48)), + "amount": "0x01" + }], + "consolidations": [{ + "source_address": format!("0x{}", "66".repeat(20)), + "source_pubkey": format!("0x{}", "77".repeat(48)), + "target_pubkey": format!("0x{}", "88".repeat(48)) + }] + }); + + let parsed: ExecutionRequestsJson = serde_json::from_value(json).unwrap(); + + assert_eq!(parsed.deposits[0].amount, 32_000_000_000); + assert_eq!(parsed.deposits[0].index, 42); + assert_eq!(parsed.withdrawals[0].amount, 1); + assert_eq!(parsed.consolidations.len(), 1); + } + + #[test] + fn parses_execution_payload_envelope_with_amsterdam_fields() { + let response = serde_json::json!({ + "version": "gloas", + "execution_optimistic": false, + "finalized": false, + "data": { + "message": { + "payload": { + "parent_hash": format!("0x{}", "01".repeat(32)), + "fee_recipient": format!("0x{}", "02".repeat(20)), + "state_root": format!("0x{}", "03".repeat(32)), + "receipts_root": format!("0x{}", "04".repeat(32)), + "logs_bloom": format!("0x{}", "00".repeat(256)), + "prev_randao": format!("0x{}", "05".repeat(32)), + "block_number": "11", + "gas_limit": "30000000", + "gas_used": "21000", + "timestamp": "1000", + "extra_data": "0x", + "base_fee_per_gas": "0x7", + "block_hash": format!("0x{}", "06".repeat(32)), + "transactions": [], + "withdrawals": [], + "blob_gas_used": "0", + "excess_blob_gas": "0", + "block_access_list": "0xc0", + "slot_number": "64" + }, + "execution_requests": {}, + "builder_index": "0", + "beacon_block_root": format!("0x{}", "bb".repeat(32)), + "parent_beacon_block_root": format!("0x{}", "aa".repeat(32)) + } + } + }); + + let parsed: ExecutionPayloadEnvelopeResponse = serde_json::from_value(response).unwrap(); + + assert_eq!(parsed.data.message.payload.block_number, 11); + assert_eq!(parsed.data.message.payload.slot_number, 64); + assert_eq!( + parsed.data.message.parent_beacon_block_root, + B256::repeat_byte(0xaa) + ); + } +} diff --git a/crates/witness-generator-spec-cli/src/serde_helpers.rs b/crates/witness-generator-spec-cli/src/serde_helpers.rs new file mode 100644 index 00000000..e6811b09 --- /dev/null +++ b/crates/witness-generator-spec-cli/src/serde_helpers.rs @@ -0,0 +1,119 @@ +//! Serde helpers for Beacon API and JSON-RPC quantity fields. + +use std::{fmt, str::FromStr}; + +use alloy_primitives::U256; +use serde::de::{self, Visitor}; + +pub(crate) fn de_u64<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + deserializer.deserialize_any(U64Visitor) +} + +pub(crate) fn de_u256<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + deserializer.deserialize_any(U256Visitor) +} + +struct U64Visitor; + +impl Visitor<'_> for U64Visitor { + type Value = u64; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a decimal or 0x-prefixed u64") + } + + fn visit_u64(self, value: u64) -> Result { + Ok(value) + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + parse_u64(value).map_err(E::custom) + } +} + +struct U256Visitor; + +impl Visitor<'_> for U256Visitor { + type Value = U256; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a decimal or 0x-prefixed uint256") + } + + fn visit_u64(self, value: u64) -> Result { + Ok(U256::from(value)) + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + parse_u256(value).map_err(E::custom) + } +} + +pub(crate) fn parse_u64(value: &str) -> anyhow::Result { + let value = value.trim(); + if let Some(hex) = value.strip_prefix("0x") { + return Ok(u64::from_str_radix(hex, 16)?); + } + Ok(value.parse()?) +} + +pub(crate) fn parse_u256(value: &str) -> anyhow::Result { + let value = value.trim(); + if let Some(hex) = value.strip_prefix("0x") { + return Ok(U256::from_str_radix(hex, 16)?); + } + Ok(U256::from_str(value)?) +} + +pub(crate) fn hex_quantity(value: u64) -> String { + format!("0x{value:x}") +} + +#[cfg(test)] +mod tests { + use serde::Deserialize; + + use super::*; + + #[derive(Debug, Deserialize)] + struct U64Value { + #[serde(deserialize_with = "de_u64")] + value: u64, + } + + #[derive(Debug, Deserialize)] + struct U256Value { + #[serde(deserialize_with = "de_u256")] + value: U256, + } + + #[test] + fn parses_decimal_and_hex_u64() { + let decimal: U64Value = serde_json::from_str(r#"{"value":"42"}"#).unwrap(); + let hex: U64Value = serde_json::from_str(r#"{"value":"0x2a"}"#).unwrap(); + + assert_eq!(decimal.value, 42); + assert_eq!(hex.value, 42); + } + + #[test] + fn parses_decimal_and_hex_u256() { + let decimal: U256Value = serde_json::from_str(r#"{"value":"42"}"#).unwrap(); + let hex: U256Value = serde_json::from_str(r#"{"value":"0x2a"}"#).unwrap(); + + assert_eq!(decimal.value, U256::from(42)); + assert_eq!(hex.value, U256::from(42)); + } +} diff --git a/crates/witness-generator-spec-cli/systemd/README.md b/crates/witness-generator-spec-cli/systemd/README.md new file mode 100644 index 00000000..28da08e2 --- /dev/null +++ b/crates/witness-generator-spec-cli/systemd/README.md @@ -0,0 +1,114 @@ +# witness-generator-spec-cli systemd units + +This directory contains example systemd units for running a stateless input +collector, periodically exporting complete local batches, and periodically +publishing those batches plus the generated public catalog to Cloudflare R2. + +The units assume: + +- Binary path: `/usr/local/bin/witness-generator-spec-cli` +- Runtime user and group: `stateless-inputs` +- Config path: `/etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml` +- Data root in the CLI config: `/data/stateless-inputs` +- Optional R2 credentials file: `/etc/witness-generator-spec-cli/r2.env` +- `flock` available at `/usr/bin/flock` to serialize export and publish jobs + +Adjust the unit files if your network name, config path, binary path, or data +root differs. + +## Install + +Build and install the CLI: + +```bash +cargo build --release -p witness-generator-spec-cli +sudo install -m 0755 target/release/witness-generator-spec-cli \ + /usr/local/bin/witness-generator-spec-cli +``` + +Create the service user and directories: + +```bash +sudo useradd --system --home /data/stateless-inputs \ + --shell /usr/sbin/nologin stateless-inputs +sudo install -d -o stateless-inputs -g stateless-inputs -m 0750 \ + /data/stateless-inputs +sudo install -d -o root -g stateless-inputs -m 0750 \ + /etc/witness-generator-spec-cli +``` + +Create the CLI config from the example and edit the RPC URLs, R2 bucket, and +Cloudflare account ID for your deployment: + +```bash +sudo install -m 0640 -o root -g stateless-inputs \ + crates/witness-generator-spec-cli/systemd/glamsterdam-devnet-5.toml.example \ + /etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml +sudoedit /etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml +``` + +Restrict the config file because RPC URLs can be private: + +```bash +sudo chown root:stateless-inputs \ + /etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml +sudo chmod 0640 /etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml +``` + +Install the systemd units: + +```bash +sudo install -m 0644 crates/witness-generator-spec-cli/systemd/*.service \ + crates/witness-generator-spec-cli/systemd/*.timer /etc/systemd/system/ +sudo systemctl daemon-reload +``` + +Start the live collector and the periodic exporter: + +```bash +sudo systemctl enable --now witness-collector.service +sudo systemctl enable --now witness-exporter.timer +``` + +## R2 publishing + +Install and configure the AWS CLI on the host. The `publish-r2` command shells +out to `aws s3 sync`, `aws s3 cp`, and `aws s3 rm` using the Cloudflare R2 +endpoint derived from the CLI TOML config. + +Create the R2 environment file from the example: + +```bash +sudo install -m 0640 -o root -g stateless-inputs \ + crates/witness-generator-spec-cli/systemd/r2.env.example \ + /etc/witness-generator-spec-cli/r2.env +sudoedit /etc/witness-generator-spec-cli/r2.env +``` + +Then start the publisher timer: + +```bash +sudo systemctl enable --now witness-publisher.timer +``` + +## Operations + +Run one export or publish immediately: + +```bash +sudo systemctl start witness-exporter.service +sudo systemctl start witness-publisher.service +``` + +Inspect services and timers: + +```bash +systemctl status witness-collector.service \ + witness-exporter.service witness-exporter.timer \ + witness-publisher.service witness-publisher.timer +journalctl -u witness-collector.service -f +systemctl list-timers 'witness-*' +``` + +If you change the CLI config `out_root`, update `ReadWritePaths` in the unit +files to match the writable data directory. diff --git a/crates/witness-generator-spec-cli/systemd/glamsterdam-devnet-5.toml.example b/crates/witness-generator-spec-cli/systemd/glamsterdam-devnet-5.toml.example new file mode 100644 index 00000000..419a6d58 --- /dev/null +++ b/crates/witness-generator-spec-cli/systemd/glamsterdam-devnet-5.toml.example @@ -0,0 +1,15 @@ +network = "glamsterdam-devnet-5" + +# Local Beacon API and execution JSON-RPC endpoints. +cl_url = "http://127.0.0.1:3500" +el_url = "http://127.0.0.1:8545" + +out_root = "/data/stateless-inputs" +poll_interval = "4s" +request_timeout = "30s" +batch_size = 100 + +[r2] +bucket = "stateless-inputs" +prefix = "devnets" +account_id = "" diff --git a/crates/witness-generator-spec-cli/systemd/r2.env.example b/crates/witness-generator-spec-cli/systemd/r2.env.example new file mode 100644 index 00000000..13f0c54d --- /dev/null +++ b/crates/witness-generator-spec-cli/systemd/r2.env.example @@ -0,0 +1,6 @@ +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=auto +AWS_EC2_METADATA_DISABLED=true +AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED +AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_REQUIRED diff --git a/crates/witness-generator-spec-cli/systemd/witness-collector.service b/crates/witness-generator-spec-cli/systemd/witness-collector.service new file mode 100644 index 00000000..dd581d2c --- /dev/null +++ b/crates/witness-generator-spec-cli/systemd/witness-collector.service @@ -0,0 +1,22 @@ +[Unit] +Description=Stateless input collector +Wants=network-online.target +After=network-online.target + +[Service] +Type=simple +User=stateless-inputs +Group=stateless-inputs +Environment=RUST_LOG=info +ExecStart=/usr/local/bin/witness-generator-spec-cli collect --config /etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml +Restart=always +RestartSec=10 + +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +ProtectHome=true +ReadWritePaths=/data/stateless-inputs + +[Install] +WantedBy=multi-user.target diff --git a/crates/witness-generator-spec-cli/systemd/witness-exporter.service b/crates/witness-generator-spec-cli/systemd/witness-exporter.service new file mode 100644 index 00000000..548fe788 --- /dev/null +++ b/crates/witness-generator-spec-cli/systemd/witness-exporter.service @@ -0,0 +1,15 @@ +[Unit] +Description=Export stateless input batches + +[Service] +Type=oneshot +User=stateless-inputs +Group=stateless-inputs +Environment=RUST_LOG=info +ExecStart=/usr/bin/flock /data/stateless-inputs/.export-publish.lock /usr/local/bin/witness-generator-spec-cli export --config /etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml + +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +ProtectHome=true +ReadWritePaths=/data/stateless-inputs diff --git a/crates/witness-generator-spec-cli/systemd/witness-exporter.timer b/crates/witness-generator-spec-cli/systemd/witness-exporter.timer new file mode 100644 index 00000000..2faa8494 --- /dev/null +++ b/crates/witness-generator-spec-cli/systemd/witness-exporter.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Run stateless input exporter periodically + +[Timer] +OnBootSec=5min +OnUnitActiveSec=10min +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/crates/witness-generator-spec-cli/systemd/witness-publisher.service b/crates/witness-generator-spec-cli/systemd/witness-publisher.service new file mode 100644 index 00000000..8dd5597e --- /dev/null +++ b/crates/witness-generator-spec-cli/systemd/witness-publisher.service @@ -0,0 +1,18 @@ +[Unit] +Description=Publish stateless input batches to Cloudflare R2 +Wants=network-online.target +After=network-online.target + +[Service] +Type=oneshot +User=stateless-inputs +Group=stateless-inputs +Environment=RUST_LOG=info +EnvironmentFile=/etc/witness-generator-spec-cli/r2.env +ExecStart=/usr/bin/flock /data/stateless-inputs/.export-publish.lock /usr/local/bin/witness-generator-spec-cli publish-r2 --config /etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml + +NoNewPrivileges=true +PrivateTmp=true +ProtectSystem=full +ProtectHome=true +ReadWritePaths=/data/stateless-inputs diff --git a/crates/witness-generator-spec-cli/systemd/witness-publisher.timer b/crates/witness-generator-spec-cli/systemd/witness-publisher.timer new file mode 100644 index 00000000..06b2030f --- /dev/null +++ b/crates/witness-generator-spec-cli/systemd/witness-publisher.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Run stateless input R2 publisher periodically + +[Timer] +OnBootSec=10min +OnUnitActiveSec=10min +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/crates/witness-generator/Cargo.toml b/crates/witness-generator/Cargo.toml index d892fee6..1a2fa285 100644 --- a/crates/witness-generator/Cargo.toml +++ b/crates/witness-generator/Cargo.toml @@ -22,6 +22,7 @@ serde.workspace = true serde_json.workspace = true erased-serde.workspace = true async-trait.workspace = true +futures.workspace = true jsonrpsee.workspace = true http = "1.0" thiserror.workspace = true diff --git a/crates/witness-generator/src/rpc_generator.rs b/crates/witness-generator/src/rpc_generator.rs index 054156aa..f937cb76 100644 --- a/crates/witness-generator/src/rpc_generator.rs +++ b/crates/witness-generator/src/rpc_generator.rs @@ -5,6 +5,7 @@ use alloy_eips::BlockNumberOrTag; use alloy_genesis::{ChainConfig, Genesis}; use alloy_rpc_types_eth::{Block, Header, Receipt, Transaction, TransactionRequest}; use async_trait::async_trait; +use futures::stream::StreamExt; use http::{HeaderName, HeaderValue}; use jsonrpsee::{ http_client::{HeaderMap, HttpClient, HttpClientBuilder}, @@ -17,6 +18,7 @@ use stateless::StatelessInput; use std::{ path::{Path, PathBuf}, str::FromStr, + time::{Duration, Instant}, }; use tokio_util::sync::CancellationToken; @@ -29,6 +31,7 @@ pub struct RpcBlocksAndWitnessesBuilder { block: Option, stop: Option, genesis: Option, + max_concurrency: Option, } impl RpcBlocksAndWitnessesBuilder { @@ -88,6 +91,15 @@ impl RpcBlocksAndWitnessesBuilder { self } + /// Sets the maximum number of blocks and witnesses fetched concurrently while catching up. + /// + /// # Arguments + /// * `max_concurrency` - Maximum number of in-flight block and witness fetches. Defaults to 1 (sequential). + pub const fn max_concurrency(mut self, max_concurrency: usize) -> Self { + self.max_concurrency = Some(max_concurrency); + self + } + fn load_chain_config_from_genesis(path: &Path) -> Result { if !path.exists() { return Err(WGError::GenesisPathNotFound(path.display().to_string())); @@ -157,6 +169,7 @@ impl RpcBlocksAndWitnessesBuilder { last_n_blocks: self.last_n_blocks, block: self.block, stop: self.stop, + max_concurrency: self.max_concurrency.unwrap_or(1).max(1), }) } } @@ -169,6 +182,7 @@ pub struct RpcFixtureGenerator { last_n_blocks: Option, block: Option, stop: Option, + max_concurrency: usize, } #[async_trait] @@ -209,6 +223,37 @@ impl FixtureGenerator for RpcFixtureGenerator { } impl RpcFixtureGenerator { + /// Fetches a block by number or tag. + /// + /// # Arguments + /// * `number` - The block number or tag to fetch + /// * `full` - Whether to include full transaction bodies + /// + /// # Errors + /// Returns an error if the RPC call fails or if the block cannot be found. + async fn fetch_block_by_number( + &self, + number: BlockNumberOrTag, + full: bool, + ) -> Result> { + EthApiClient::< + TransactionRequest, + Transaction, + Block, + Receipt, + Header, + TransactionSigned, + >::block_by_number(&self.client, number, full) + .await + .map_err(|e| WGError::RpcError(e.to_string()))? + .ok_or_else(|| { + number + .as_number() + .map(WGError::BlockNotFoundForNumber) + .unwrap_or(WGError::LatestBlockFetchError) + }) + } + /// Fetches the last N blocks and their execution witnesses. /// /// # Arguments @@ -221,43 +266,22 @@ impl RpcFixtureGenerator { return Ok(vec![]); } - let latest_block = EthApiClient::< - TransactionRequest, - Transaction, - Block, - Receipt, - Header, - TransactionSigned, - >::block_by_number(&self.client, BlockNumberOrTag::Latest, false) - .await - .map_err(|e| WGError::RpcError(e.to_string()))? - .ok_or(WGError::LatestBlockFetchError)?; + let latest_block = self + .fetch_block_by_number(BlockNumberOrTag::Latest, true) + .await?; let (block_num_start, block_num_end) = ( - std::cmp::max(0, latest_block.header.number - (last_n_blocks as u64 - 1)), + latest_block + .header + .number + .saturating_sub(last_n_blocks as u64 - 1), latest_block.header.number, ); - let mut hashes = Vec::with_capacity(last_n_blocks); - hashes.push((latest_block.header.number, latest_block.header.hash)); - for n in (block_num_start..block_num_end).rev() { - let block_hash = hashes.last().unwrap().1; - let block = EthApiClient::< - TransactionRequest, - Transaction, - Block, - Receipt, - Header, - TransactionSigned, - >::block_by_hash(&self.client, block_hash, true) - .await - .map_err(|e| WGError::RpcError(e.to_string()))? - .ok_or(WGError::BlockNotFoundForNumber(n))?; - hashes.push((n, block.header.parent_hash)); - } - - let mut blocks_and_witnesses = Vec::with_capacity(hashes.len()); - for (block_num, block_hash) in hashes { + let mut blocks = Vec::with_capacity(last_n_blocks); + blocks.push(latest_block); + for _ in (block_num_start..block_num_end).rev() { + let block_hash = blocks.last().unwrap().header.parent_hash; let block = EthApiClient::< TransactionRequest, Transaction, @@ -269,7 +293,13 @@ impl RpcFixtureGenerator { .await .map_err(|e| WGError::RpcError(e.to_string()))? .ok_or(WGError::BlockNotFoundForHash(block_hash.to_string()))?; + blocks.push(block); + } + let mut blocks_and_witnesses = Vec::with_capacity(blocks.len()); + for block in blocks { + let block_hash = block.header.hash; + let block_num = block.header.number; let witness = DebugApiClient::<()>::debug_execution_witness_by_block_hash( &self.client, block_hash, @@ -312,18 +342,9 @@ impl RpcFixtureGenerator { .map_err(|e| WGError::RpcError(e.to_string()))?; // Fetch the block details - let block = - EthApiClient::< - TransactionRequest, - Transaction, - Block, - Receipt, - Header, - TransactionSigned, - >::block_by_number(&self.client, BlockNumberOrTag::Number(block_num), true) - .await - .map_err(|e| WGError::RpcError(e.to_string()))? - .ok_or(WGError::BlockNotFoundForNumber(block_num))?; + let block = self + .fetch_block_by_number(BlockNumberOrTag::Number(block_num), true) + .await?; let bw = StatelessValidationFixture { name: format!("rpc_block_{block_num}"), @@ -340,31 +361,42 @@ impl RpcFixtureGenerator { /// Fetches blocks from a specific block number to the latest block and their execution witnesses. /// + /// The returned vector is the contiguous prefix of successfully fetched blocks + /// starting at `block_num`. On the first fetch failure the method stops and + /// returns the blocks gathered so far rather than the full range, so the caller + /// can resume from the block after the last returned one. A failure therefore + /// yields a shorter `Ok` result rather than an `Err`. + /// /// # Arguments /// * `block_num` - The starting block number to fetch /// /// # Returns - /// A vector of `BlockAndWitness` objects for all blocks in the range + /// A vector of `BlockAndWitness` objects forming the contiguous successful + /// prefix of the requested range /// /// # Errors /// - /// Returns an error if any RPC call fails or if blocks cannot be found. + /// Returns an error only if the latest block number cannot be resolved. async fn fetch_from_block(&self, block_num: u64) -> Result>> { - let latest_block = EthApiClient::< - TransactionRequest, - Transaction, - Block, - Receipt, - Header, - TransactionSigned, - >::block_by_number(&self.client, BlockNumberOrTag::Latest, false) - .await - .map_err(|e| WGError::RpcError(e.to_string()))? - .ok_or(WGError::LatestBlockFetchError)?; + let latest_block = self + .fetch_block_by_number(BlockNumberOrTag::Latest, false) + .await?; + + // Fetch up to `max_concurrency` blocks at a time, returning the contiguous + // prefix of successes so the caller can resume from the first gap. + let mut stream = futures::stream::iter(block_num..=latest_block.header.number) + .map(|n| async move { (n, self.fetch_specific_block(n).await) }) + .buffered(self.max_concurrency); let mut bws = Vec::new(); - for n in block_num..=latest_block.header.number { - bws.push(self.fetch_specific_block(n).await?); + while let Some((n, result)) = stream.next().await { + match result { + Ok(bw) => bws.push(bw), + Err(err) => { + error!("Fetch block {n} failed: {err}"); + break; + } + } } Ok(bws) @@ -387,17 +419,9 @@ impl RpcFixtureGenerator { /// Returns an error if the cancellation token is not set, if RPC calls fail, /// or if file writing fails. async fn fetch_live(&self, path: &Path) -> Result { - let latest_block = EthApiClient::< - TransactionRequest, - Transaction, - Block, - Receipt, - Header, - TransactionSigned, - >::block_by_number(&self.client, BlockNumberOrTag::Latest, false) - .await - .map_err(|e| WGError::RpcError(e.to_string()))? - .ok_or(WGError::LatestBlockFetchError)?; + let latest_block = self + .fetch_block_by_number(BlockNumberOrTag::Latest, false) + .await?; let mut count: usize = 0; let mut next_block_num = latest_block.header.number; @@ -407,7 +431,11 @@ impl RpcFixtureGenerator { .stop .as_ref() .ok_or(WGError::CancellationTokenRequired)?; + + const MIN_LOOP_INTERVAL: Duration = Duration::from_secs(6); loop { + let iter_start = Instant::now(); + tokio::select! { _ = stop_signal.cancelled() => { info!("Stopped listening for new blocks."); @@ -433,12 +461,15 @@ impl RpcFixtureGenerator { } } - tokio::select! { - _ = stop_signal.cancelled() => { - info!("Stopped listening for new blocks."); - break; - } - _ = tokio::time::sleep(std::time::Duration::from_secs(6)) => { + // Throttle the remaining time up to the minimum interval. + let remaining = MIN_LOOP_INTERVAL.saturating_sub(iter_start.elapsed()); + if !remaining.is_zero() { + tokio::select! { + _ = stop_signal.cancelled() => { + info!("Stopped listening for new blocks."); + break; + } + _ = tokio::time::sleep(remaining) => {} } } } diff --git a/docs/README.md b/docs/README.md index fe578f79..07010e6f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,6 +6,7 @@ - To understand exactly what JSON `ere-hosts` accepts, read [Benchmark Execution Inputs](benchmark-execution-inputs.md). - To understand exactly what files and JSON `ere-hosts` writes, read [Benchmark Execution Output](benchmark-execution-output.md). - To understand how legacy generated fixtures are produced, read [Fixture Generation](fixture-generation.md). +- To publish canonical stateless input batches as a public R2 dataset or validate the public R2 dataset locally with EEST, read [Stateless Input Publication](stateless-input-publication.md). ## Guides @@ -14,5 +15,6 @@ - [Benchmark Execution Inputs](benchmark-execution-inputs.md): input discovery, fixture filtering, legacy generated fixture schema, and direct EEST `blockchain_tests` schema. - [Benchmark Execution Output](benchmark-execution-output.md): metrics directory layout, `BenchmarkRun` JSON, hardware metadata, proof handling, and input dumps. - [Witness Generator CLI](witness-generator-cli.md): Docker usage and binary-local notes for `witness-generator-cli`. +- [Stateless Input Publication](stateless-input-publication.md): R2 public dataset catalog files, publish flow, download examples, and local EEST validation. The `zkevm-metrics` crate API documentation lives in [`crates/metrics/README.md`](../crates/metrics/README.md). The CLI metrics files written by `ere-hosts` are documented in [Benchmark Execution Output](benchmark-execution-output.md). diff --git a/docs/benchmark-execution-inputs.md b/docs/benchmark-execution-inputs.md index 4232c43a..6bf8c653 100644 --- a/docs/benchmark-execution-inputs.md +++ b/docs/benchmark-execution-inputs.md @@ -23,8 +23,6 @@ When the input is a directory, `ere-hosts` recursively loads `.json` files and s When the input folder contains an EEST `blockchain_tests/` subdirectory, only that subtree is used for `stateless-validator` inputs. EEST bundles that only contain `blockchain_tests_engine/`, `blockchain_tests_engine_x/`, or `blockchain_tests_sync/` are rejected because those fixture formats do not contain the canonical stateless validator bytes. -`empty-program` does not use fixture files. - ## Fixture Selection `--fixture ` filters selected fixtures by fixture-name prefix: @@ -45,7 +43,7 @@ For direct EEST fixtures, the prefix may match either the sanitized generated fi - Legacy generated fixtures with a top-level `stateless_input` field. - Direct EEST `blockchain_tests` fixtures whose executable blocks contain `statelessInputBytes` and `statelessOutputBytes`. -Use direct EEST inputs when you already have `blockchain_tests` files with canonical stateless bytes. Use legacy generated fixtures when generating from RPC, generating from raw inputs, or intentionally exercising the selected execution client's legacy host conversion path. +Use direct EEST inputs for Reth and Ethrex. Zesu accepts only inputs whose decoded canonical chain configuration selects `ProtocolFork::Amsterdam` (the Glamsterdam guest path). Legacy generated fixtures are accepted only when `--execution-client zilkworm` is selected. ## Legacy Generated Fixtures @@ -75,7 +73,7 @@ Fields: - `stateless_input`: A serialized `stateless::StatelessInput` containing `block`, `witness`, and `chain_config`. - `success`: Whether the fixture represents a valid block validation. -Generated repo fixtures are converted into the selected execution client's guest input format before execution. The expected public values are computed with the selected guest host code. Metrics metadata for these fixtures is `{"block_used_gas": }`. +Generated repo fixtures are converted into Zilkworm's unified-RLP guest input format before execution. Metrics metadata for these fixtures is `{"block_used_gas": }`. Selecting a legacy fixture with Reth, Ethrex, or Zesu returns a format error directing the operator to canonical fixtures. ## Direct EEST Blockchain Tests Fixtures @@ -113,6 +111,6 @@ Rules: - A block with non-empty `statelessInputBytes` must also have `statelessOutputBytes`. - `statelessInputBytes` and `statelessOutputBytes` are hex strings with or without `0x`; after removing the prefix they must contain an even number of hex digits. -Each accepted EEST block becomes one benchmark fixture. `ere-hosts` sends `statelessInputBytes` directly to the selected guest program without EL-specific host conversion. The expected public values are the SHA-256 digest of `statelessOutputBytes`, matching the current stateless validator guest binaries. +Each accepted EEST block becomes one benchmark fixture. `ere-hosts` sends `statelessInputBytes` directly to the selected guest program without EL-specific host conversion. The expected public values are the raw SSZ `statelessOutputBytes` emitted by the v0.13 stateless validator guests. The generated fixture name is derived from the original test name, source path, and block index. Output metadata preserves those original EEST fields; see [Benchmark Execution Output](benchmark-execution-output.md#metadata-by-workload). diff --git a/docs/benchmark-execution-output.md b/docs/benchmark-execution-output.md index 01e33911..8444260c 100644 --- a/docs/benchmark-execution-output.md +++ b/docs/benchmark-execution-output.md @@ -30,15 +30,6 @@ zkevm-metrics/ .json ``` -For `empty-program`, there is no execution-client subfolder: - -```text -zkevm-metrics/ - hardware.json - -/ - empty_program.json -``` - Each fixture metrics file is a single pretty-printed `BenchmarkRun` JSON object. The `zkevm-metrics` library helper `BenchmarkRun::to_json` serializes a list of runs, but the CLI output files under `zkevm-metrics/` contain one object per file. ## Hardware JSON @@ -152,7 +143,7 @@ The `metadata` field is workload-specific: - Legacy stateless-validator fixtures write `{"block_used_gas": }`. - Direct EEST stateless-validator fixtures write EEST provenance and block metadata. -- `empty-program` and standalone verification metrics write `null`. +- Standalone verification metrics write `null`. Direct EEST metadata has this shape: diff --git a/docs/benchmark-execution.md b/docs/benchmark-execution.md index f1d959a9..845da489 100644 --- a/docs/benchmark-execution.md +++ b/docs/benchmark-execution.md @@ -16,7 +16,8 @@ Prerequisites: - Docker is required because zkVM hosts are managed through `ere-dockerized`. - Fixture JSON files are expected in `zkevm-fixtures-input/` unless `--input-folder` is provided. -- `stateless-validator` accepts both generated repo fixtures and EEST `blockchain_tests` fixtures that contain `statelessInputBytes` and `statelessOutputBytes`. +- Reth and Ethrex consume EEST `blockchain_tests` fixtures containing canonical `statelessInputBytes` and `statelessOutputBytes`. Zesu accepts only Glamsterdam canonical inputs. Legacy generated fixtures remain available only for Zilkworm. +- The Ere Guests v0.13 release publishes Reth and Ethrex artifacts for OpenVM, SP1, and ZisK, plus a Zesu artifact for ZisK. ## Common Benchmark Commands @@ -30,16 +31,10 @@ cargo run -p ere-hosts --release -- --zkvms sp1 \ Run the stateless validator with Ethrex: ```bash -cargo run -p ere-hosts --release -- --zkvms risc0 \ +cargo run -p ere-hosts --release -- --zkvms openvm \ stateless-validator --execution-client ethrex ``` -Run the empty program: - -```bash -cargo run -p ere-hosts --release -- --zkvms sp1 empty-program -``` - Use a custom fixture folder: ```bash @@ -87,7 +82,7 @@ Override the timeout for the selected action only: ```bash cargo run -p ere-hosts --release -- --zkvms sp1 \ --timeout 90s \ - empty-program + stateless-validator --execution-client reth ``` ## Inputs And Outputs @@ -99,7 +94,7 @@ cargo run -p ere-hosts --release -- --zkvms sp1 \ Use the focused references when you need exact schemas or file layouts: -- [Benchmark Execution Inputs](benchmark-execution-inputs.md) describes input discovery, fixture filtering, and the two accepted `stateless-validator` JSON formats. +- [Benchmark Execution Inputs](benchmark-execution-inputs.md) describes input discovery, fixture filtering, and client compatibility for the accepted `stateless-validator` JSON formats. - [Benchmark Execution Output](benchmark-execution-output.md) describes metrics JSON, `hardware.json`, proof files, input dumps, and workload metadata. Dump the raw serialized guest inputs used for a run: diff --git a/docs/fixture-generation.md b/docs/fixture-generation.md index 0b81f737..f6a6b273 100644 --- a/docs/fixture-generation.md +++ b/docs/fixture-generation.md @@ -16,7 +16,7 @@ Use `witness-generator-cli` when you need legacy generated fixtures: - You want fixtures from live RPC blocks. - You have pre-collected raw JSON-RPC inputs. - You want to convert EEST cases into the legacy generated fixture shape. -- You need `ere-hosts` to run the selected execution client's legacy host conversion path instead of passing EEST stateless bytes directly to the guest. +- You need inputs for the Zilkworm benchmark path, which is the only execution client that retains legacy fixture support. For the exact schemas accepted by `ere-hosts`, see [Benchmark Execution Inputs](benchmark-execution-inputs.md). @@ -32,9 +32,9 @@ cargo run -p ere-hosts --release -- --zkvms sp1 \ When the input folder contains a `blockchain_tests/` subdirectory, `ere-hosts` uses only that subtree for stateless-validator inputs. -## Legacy Generated Fixtures +## Legacy Generated Fixtures For Zilkworm -`witness-generator-cli` writes legacy generated fixtures as JSON files. By default it writes them to `zkevm-fixtures-input/`; override that with `-o, --output-folder `. +`witness-generator-cli` writes legacy generated fixtures as JSON files for the Zilkworm benchmark path. By default it writes them to `zkevm-fixtures-input/`; override that with `-o, --output-folder `. Inspect the current CLI surface from the repo root: diff --git a/docs/stateless-input-publication.md b/docs/stateless-input-publication.md new file mode 100644 index 00000000..e9e03f23 --- /dev/null +++ b/docs/stateless-input-publication.md @@ -0,0 +1,158 @@ +# Stateless Input Publication + +This guide describes how to publish canonical stateless input batches as a public R2 dataset. + +## Public Dataset Shape + +R2 public buckets do not provide directory listing, so share the generated HTML page rather than the bucket or prefix root: + +```text +https:///devnets//index.html +``` + +The public dataset is batch-first. Users should download complete `.tar.zst` batch archives from: + +```text +exports/batches/-.tar.zst +``` + +Individual block artifacts remain described inside each batch archive's `manifest.json`, but they are not published as standalone public objects. + +## Generated Catalog Files + +Running `witness-generator-spec-cli export` rebuilds these files at the network root: + +- `index.html`: human-readable landing page with download examples and a batch table. +- `manifest.json`: dataset summary and paths to all public metadata files. +- `batches.jsonl`: one completed batch archive per line. +- `SHA256SUMS`: checksums for completed batch archives. + +The generated links are relative, so the same catalog works with an `r2.dev` development URL or a custom domain. + +## Operator Flow + +Collect live stateless inputs: + +```bash +cargo run -p witness-generator-spec-cli --release -- collect \ + --config /etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml +``` + +Export complete local block ranges and rebuild the public catalog: + +```bash +cargo run -p witness-generator-spec-cli --release -- export \ + --config /etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml +``` + +Publish batch archives and catalog files to R2: + +```bash +cargo run -p witness-generator-spec-cli --release -- publish-r2 \ + --config /etc/witness-generator-spec-cli/glamsterdam-devnet-5.toml +``` + +If `publish-r2` reports a missing public catalog file, run `export` first. + +Example systemd services and timers for this flow live in +[`crates/witness-generator-spec-cli/systemd`](../crates/witness-generator-spec-cli/systemd/README.md). + +## User Download Examples + +Download a batch archive from the generated HTML page: + +```bash +curl -LO https:///devnets//exports/batches/32500-32999.tar.zst +tar --zstd -xf 32500-32999.tar.zst +``` + +Verify checksums: + +```bash +curl -LO https:///devnets//SHA256SUMS +sha256sum -c SHA256SUMS +``` + +Inspect machine-readable metadata: + +```bash +curl -fsSL https:///devnets//manifest.json | jq +curl -fsSL https:///devnets//batches.jsonl | head +``` + +## Local EEST Validation + +Use [`scripts/validate-r2-stateless-inputs-with-eest.py`](../scripts/validate-r2-stateless-inputs-with-eest.py) +to validate published R2 batch archives against the EEST Amsterdam stateless +guest. The script downloads the selected batch archives, verifies the catalog +metadata, decompresses each `blocks/*.json.zst` artifact, checks the recorded +stateless input byte length and SHA-256 digest, and runs each stateless input +through EEST. + +Prerequisites: + +- Install [`uv`](https://docs.astral.sh/uv/). +- Check out `ethereum/execution-specs` next to this repository. +- Check out the EEST ref you want to validate against. + +```bash +cd /path/to/parent +git clone https://github.com/ethereum/execution-specs.git +cd execution-specs +git fetch --tags +git checkout tests-zkevm@v0.4.1 +``` + +From this repository root, run: + +```bash +CATALOG_URL="https://pub-5345007fbd06486bbb7cbbe9f3112c45.r2.dev/devnets/glamsterdam-devnet-5" +EEST_REF="tests-zkevm@v0.4.1" +EEST_DIR="../execution-specs" +SUMMARY_DIR="target/eest-r2-stateless-inputs" + +mkdir -p "$SUMMARY_DIR" + +uv run --project "$EEST_DIR" --with zstandard \ + python scripts/validate-r2-stateless-inputs-with-eest.py \ + --catalog-url "$CATALOG_URL" \ + --batch-count 70 \ + --summary-json "$SUMMARY_DIR/summary.json" \ + --summary-md "$SUMMARY_DIR/summary.md" \ + --eest-ref "$EEST_REF" \ + --eest-commit "$(git -C "$EEST_DIR" rev-parse HEAD)" + +cat "$SUMMARY_DIR/summary.md" +``` + +The `uv run --project "$EEST_DIR"` part matters: the validator imports EEST's +Python modules from the `execution-specs` checkout while adding `zstandard` for +the compressed R2 artifacts. `--eest-ref` and `--eest-commit` are recorded in +the summaries for provenance; the script does not use them to check out EEST. + +Useful selection options: + +- `--batch-count N`: validate the latest `N` complete batches from + `batches.jsonl`. +- `--block-number N`: validate the batch containing one block and only run the + matching block artifact. +- `--max-artifacts N`: stop after `N` matching artifacts, useful for a fast + smoke test. + +For a quick local smoke test: + +```bash +uv run --project "$EEST_DIR" --with zstandard \ + python scripts/validate-r2-stateless-inputs-with-eest.py \ + --catalog-url "$CATALOG_URL" \ + --batch-count 1 \ + --max-artifacts 1 \ + --summary-json "$SUMMARY_DIR/smoke.json" \ + --summary-md "$SUMMARY_DIR/smoke.md" \ + --eest-ref "$EEST_REF" \ + --eest-commit "$(git -C "$EEST_DIR" rev-parse HEAD)" +``` + +The JSON summary is intended for automation and contains full failure details. +The Markdown summary is intended for local inspection or GitHub Actions job +summaries. diff --git a/scripts/validate-r2-stateless-inputs-with-eest.py b/scripts/validate-r2-stateless-inputs-with-eest.py new file mode 100755 index 00000000..63fb6035 --- /dev/null +++ b/scripts/validate-r2-stateless-inputs-with-eest.py @@ -0,0 +1,893 @@ +#!/usr/bin/env python3 +"""Validate public R2 stateless input batches with the EEST spec guest.""" + +from __future__ import annotations + +import argparse +import hashlib +import io +import json +import sys +import tarfile +import tempfile +import time +import urllib.parse +import urllib.request +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +try: + import zstandard +except ImportError: + zstandard = None + +DEFAULT_CATALOG_URL = ( + "https://pub-5345007fbd06486bbb7cbbe9f3112c45.r2.dev/" + "devnets/glamsterdam-devnet-5" +) +REQUEST_TIMEOUT_SECONDS = 60 +DOWNLOAD_CHUNK_SIZE = 1024 * 1024 +FAILURE_MARKDOWN_LIMIT = 20 + + +class ValidationError(Exception): + """Raised when a downloaded artifact is structurally invalid.""" + + +@dataclass(frozen=True) +class BatchEntry: + """One public batch entry from batches.jsonl.""" + + network: str + batch_start_block: int + batch_end_block: int + batch_size: int + artifact_count: int + byte_length: int + sha256: str + path: str + + @classmethod + def from_json(cls, value: dict[str, Any]) -> "BatchEntry": + return cls( + network=str(value["network"]), + batch_start_block=int(value["batchStartBlock"]), + batch_end_block=int(value["batchEndBlock"]), + batch_size=int(value["batchSize"]), + artifact_count=int(value["artifactCount"]), + byte_length=int(value["byteLength"]), + sha256=str(value["sha256"]), + path=str(value["path"]), + ) + + def as_summary(self) -> dict[str, Any]: + return { + "network": self.network, + "batchStartBlock": self.batch_start_block, + "batchEndBlock": self.batch_end_block, + "batchSize": self.batch_size, + "artifactCount": self.artifact_count, + "byteLength": self.byte_length, + "sha256": self.sha256, + "path": self.path, + } + + +@dataclass(frozen=True) +class EestGuest: + """EEST Amsterdam guest functions.""" + + bytes_type: Any + run_stateless_guest: Any + deserialize_stateless_output: Any + deserialize_stateless_input: Any + + +def main() -> int: + args = parse_args() + started_at = time.monotonic() + try: + summary = run_validation(args, started_at) + exit_code = 1 if summary["totals"]["failures"] else 0 + except Exception as error: + summary = fatal_summary(args, started_at, error) + exit_code = 1 + + write_outputs(summary, args) + print(render_console_summary(summary), end="") + return exit_code + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Validate R2 stateless input batches with EEST", + ) + parser.add_argument( + "--catalog-url", + default=DEFAULT_CATALOG_URL, + help="Public catalog root URL, index.html URL, or manifest.json URL", + ) + parser.add_argument( + "--batch-count", + default=1, + type=positive_int, + help="Number of latest complete batches to validate", + ) + parser.add_argument( + "--max-artifacts", + type=positive_int, + help="Stop after validating this many matching artifacts", + ) + parser.add_argument( + "--block-number", + type=non_negative_int, + help="Validate artifacts for one block number; selects its batch", + ) + parser.add_argument( + "--summary-json", + required=True, + type=Path, + help="Path to write machine-readable validation summary", + ) + parser.add_argument( + "--summary-md", + required=True, + type=Path, + help="Path to write Markdown validation summary", + ) + parser.add_argument( + "--eest-ref", + default="unknown", + help="EEST ref used by the caller, recorded for provenance", + ) + parser.add_argument( + "--eest-commit", + default="unknown", + help="EEST commit used by the caller, recorded for provenance", + ) + return parser.parse_args() + + +def positive_int(value: str) -> int: + parsed = int(value) + if parsed <= 0: + raise argparse.ArgumentTypeError("must be greater than zero") + return parsed + + +def non_negative_int(value: str) -> int: + parsed = int(value) + if parsed < 0: + raise argparse.ArgumentTypeError("must be zero or greater") + return parsed + + +def run_validation(args: argparse.Namespace, started_at: float) -> dict[str, Any]: + catalog_base_url = normalize_catalog_url(args.catalog_url) + manifest_url = urllib.parse.urljoin(catalog_base_url, "manifest.json") + manifest = fetch_json(manifest_url) + batches_path = manifest.get("paths", {}).get("batches", "batches.jsonl") + batches_url = urllib.parse.urljoin(catalog_base_url, batches_path) + batches = fetch_batches(batches_url) + selected_batches = select_batches( + batches, + args.batch_count, + args.block_number, + ) + if zstandard is None: + raise RuntimeError("Python package 'zstandard' is required") + guest = load_eest_guest() + + print(f"Catalog: {catalog_base_url}") + print(f"EEST ref: {args.eest_ref}") + print(f"EEST commit: {args.eest_commit}") + print(f"Selected {len(selected_batches)} batch(es)") + if args.block_number is not None: + print(f"Block filter: {args.block_number}") + if args.max_artifacts is not None: + print(f"Artifact limit: {args.max_artifacts}") + + all_failures: list[dict[str, Any]] = [] + batch_summaries: list[dict[str, Any]] = [] + processed_batches: list[BatchEntry] = [] + total_artifacts = 0 + total_successes = 0 + remaining_artifacts = args.max_artifacts + + with tempfile.TemporaryDirectory(prefix="eest-r2-stateless-") as temp_dir: + temp_root = Path(temp_dir) + for batch in selected_batches: + batch_summary, failures = validate_batch( + catalog_base_url, + batch, + guest, + temp_root, + block_number=args.block_number, + max_artifacts=remaining_artifacts, + ) + processed_batches.append(batch) + batch_summaries.append(batch_summary) + all_failures.extend(failures) + total_artifacts += batch_summary["artifactsValidated"] + total_successes += batch_summary["successfulArtifacts"] + if remaining_artifacts is not None: + remaining_artifacts -= batch_summary["artifactsValidated"] + if remaining_artifacts <= 0: + break + + return { + "catalogUrl": catalog_base_url, + "manifestUrl": manifest_url, + "batchesUrl": batches_url, + "eest": { + "ref": args.eest_ref, + "commit": args.eest_commit, + }, + "selection": { + "batchCount": args.batch_count, + "maxArtifacts": args.max_artifacts, + "blockNumber": args.block_number, + }, + "selectedBatches": [batch.as_summary() for batch in processed_batches], + "batches": batch_summaries, + "failures": all_failures, + "totals": { + "selectedBatches": len(selected_batches), + "artifactsValidated": total_artifacts, + "successfulArtifacts": total_successes, + "failures": len(all_failures), + "durationSeconds": round(time.monotonic() - started_at, 3), + }, + } + + +def load_eest_guest() -> EestGuest: + try: + from ethereum.forks.amsterdam.stateless_guest import ( + deserialize_stateless_input, + run_stateless_guest, + ) + from ethereum.forks.amsterdam.stateless_host import ( + deserialize_stateless_output, + ) + from ethereum_types.bytes import Bytes + except Exception as error: + raise RuntimeError( + "failed to import EEST Amsterdam stateless guest modules" + ) from error + + return EestGuest( + bytes_type=Bytes, + run_stateless_guest=run_stateless_guest, + deserialize_stateless_output=deserialize_stateless_output, + deserialize_stateless_input=deserialize_stateless_input, + ) + + +def normalize_catalog_url(catalog_url: str) -> str: + catalog_url = catalog_url.strip() + if not catalog_url: + raise ValidationError("catalog URL must not be empty") + for suffix in ("/index.html", "/manifest.json", "/batches.jsonl"): + if catalog_url.endswith(suffix): + catalog_url = catalog_url[: -len(suffix)] + break + return catalog_url.rstrip("/") + "/" + + +def fetch_json(url: str) -> dict[str, Any]: + data = fetch_bytes(url) + try: + value = json.loads(data) + except json.JSONDecodeError as error: + raise ValidationError(f"failed to decode JSON from {url}") from error + if not isinstance(value, dict): + raise ValidationError(f"expected JSON object from {url}") + return value + + +def fetch_batches(url: str) -> list[BatchEntry]: + data = fetch_bytes(url) + batches = [] + for line_number, raw_line in enumerate(data.decode().splitlines(), start=1): + line = raw_line.strip() + if not line: + continue + try: + value = json.loads(line) + batches.append(BatchEntry.from_json(value)) + except Exception as error: + raise ValidationError( + f"failed to parse batch entry at {url}:{line_number}" + ) from error + if not batches: + raise ValidationError(f"no batches found in {url}") + return batches + + +def fetch_bytes(url: str) -> bytes: + request = urllib.request.Request(url, headers={"User-Agent": user_agent()}) + with urllib.request.urlopen( + request, + timeout=REQUEST_TIMEOUT_SECONDS, + ) as response: + return response.read() + + +def select_batches( + batches: list[BatchEntry], + batch_count: int, + block_number: int | None, +) -> list[BatchEntry]: + if block_number is not None: + containing_batches = [ + batch + for batch in batches + if batch.batch_start_block <= block_number <= batch.batch_end_block + ] + if not containing_batches: + raise ValidationError( + f"block {block_number} is not covered by any batch" + ) + return sorted( + containing_batches, + key=lambda batch: (batch.batch_end_block, batch.batch_start_block), + ) + + ordered = sorted( + batches, + key=lambda batch: (batch.batch_end_block, batch.batch_start_block), + ) + return ordered[-batch_count:] + + +def validate_batch( + catalog_base_url: str, + batch: BatchEntry, + guest: EestGuest, + temp_root: Path, + block_number: int | None, + max_artifacts: int | None, +) -> tuple[dict[str, Any], list[dict[str, Any]]]: + batch_url = urllib.parse.urljoin(catalog_base_url, batch.path) + archive_name = batch.path.rsplit("/", maxsplit=1)[-1] + archive_path = temp_root / archive_name + print( + "Validating " + f"{batch.path} ({batch.batch_start_block}-{batch.batch_end_block})" + ) + downloaded_sha256, downloaded_byte_length = download_file( + batch_url, + archive_path, + ) + expected_sha256 = normalize_sha256(batch.sha256) + failures: list[dict[str, Any]] = [] + + if downloaded_sha256 != expected_sha256: + failures.append( + batch_failure( + batch, + "archive", + ( + "batch SHA-256 mismatch: " + f"expected {expected_sha256}, got {downloaded_sha256}" + ), + ) + ) + if downloaded_byte_length != batch.byte_length: + failures.append( + batch_failure( + batch, + "archive", + ( + "batch byte length mismatch: " + f"expected {batch.byte_length}, got {downloaded_byte_length}" + ), + ) + ) + + artifact_count = 0 + successful_artifacts = 0 + batch_manifest: dict[str, Any] | None = None + stopped_early = False + full_batch_validation = block_number is None and max_artifacts is None + + with archive_path.open("rb") as archive_file: + reader = zstandard.ZstdDecompressor().stream_reader(archive_file) + with reader, tarfile.open(fileobj=reader, mode="r|") as archive: + for member in archive: + if not member.isfile(): + continue + member_name = member.name + extracted = archive.extractfile(member) + if extracted is None: + continue + if member_name == "manifest.json": + batch_manifest = read_json_member(extracted, member_name) + elif is_artifact_member(member_name): + if block_number is not None: + member_block_number = block_number_from_member_name( + member_name + ) + if ( + member_block_number is not None + and member_block_number != block_number + ): + continue + + artifact_count += 1 + artifact = None + try: + artifact = read_artifact_member(extracted, member_name) + if ( + block_number is not None + and artifact.get("blockNumber") != block_number + ): + artifact_count -= 1 + continue + validate_artifact(batch, member_name, artifact, guest) + successful_artifacts += 1 + except Exception as error: + failures.append( + artifact_failure(batch, member_name, error, artifact) + ) + + if max_artifacts is not None and artifact_count >= max_artifacts: + stopped_early = True + break + + if block_number is not None and artifact_count == 0: + failures.append( + batch_failure( + batch, + "archive", + f"blockNumber {block_number} not found in batch archive", + ) + ) + if full_batch_validation: + failures.extend( + validate_batch_manifest(batch, batch_manifest, artifact_count) + ) + + return ( + { + **batch.as_summary(), + "url": batch_url, + "downloadedByteLength": downloaded_byte_length, + "downloadedSha256": "0x" + downloaded_sha256, + "artifactsValidated": artifact_count, + "successfulArtifacts": successful_artifacts, + "batchManifestValidated": full_batch_validation, + "stoppedEarly": stopped_early, + "failures": len(failures), + }, + failures, + ) + + +def download_file(url: str, path: Path) -> tuple[str, int]: + request = urllib.request.Request(url, headers={"User-Agent": user_agent()}) + hasher = hashlib.sha256() + total_bytes = 0 + with urllib.request.urlopen( + request, + timeout=REQUEST_TIMEOUT_SECONDS, + ) as response: + with path.open("wb") as output: + while True: + chunk = response.read(DOWNLOAD_CHUNK_SIZE) + if not chunk: + break + output.write(chunk) + hasher.update(chunk) + total_bytes += len(chunk) + return hasher.hexdigest(), total_bytes + + +def is_artifact_member(member_name: str) -> bool: + return member_name.startswith("blocks/") and member_name.endswith(".json.zst") + + +def block_number_from_member_name(member_name: str) -> int | None: + filename = member_name.rsplit("/", maxsplit=1)[-1] + block_number, separator, _ = filename.partition("-") + if not separator: + return None + try: + return int(block_number) + except ValueError: + return None + + +def read_json_member(member_file: Any, member_name: str) -> dict[str, Any]: + try: + value = json.load(member_file) + except json.JSONDecodeError as error: + raise ValidationError(f"failed to decode {member_name} JSON") from error + if not isinstance(value, dict): + raise ValidationError(f"{member_name} must contain a JSON object") + return value + + +def read_artifact_member(member_file: Any, member_name: str) -> dict[str, Any]: + compressed = member_file.read() + try: + reader = zstandard.ZstdDecompressor().stream_reader( + io.BytesIO(compressed) + ) + with reader: + data = reader.read() + except zstandard.ZstdError as error: + raise ValidationError(f"failed to decompress {member_name}") from error + + try: + value = json.loads(data) + except json.JSONDecodeError as error: + raise ValidationError(f"failed to decode {member_name} JSON") from error + if not isinstance(value, dict): + raise ValidationError(f"{member_name} must contain a JSON object") + return value + + +def validate_artifact( + batch: BatchEntry, + archive_path: str, + artifact: dict[str, Any], + guest: EestGuest, +) -> None: + input_bytes = decode_hex_bytes( + "statelessInputBytes", + artifact.get("statelessInputBytes"), + ) + expected_length = int_required( + "statelessInputByteLength", + artifact.get("statelessInputByteLength"), + ) + if len(input_bytes) != expected_length: + raise ValidationError( + "statelessInputByteLength mismatch: " + f"expected {expected_length}, got {len(input_bytes)}" + ) + + expected_sha256 = normalize_sha256( + str_required( + "statelessInputSha256", + artifact.get("statelessInputSha256"), + ) + ) + actual_sha256 = hashlib.sha256(input_bytes).hexdigest() + if actual_sha256 != expected_sha256: + raise ValidationError( + "statelessInputSha256 mismatch: " + f"expected {expected_sha256}, got {actual_sha256}" + ) + + block_number = int_required("blockNumber", artifact.get("blockNumber")) + if block_number < batch.batch_start_block or block_number > batch.batch_end_block: + raise ValidationError( + "blockNumber outside selected batch range: " + f"{block_number} not in " + f"{batch.batch_start_block}-{batch.batch_end_block}" + ) + + output_bytes = guest.run_stateless_guest(guest.bytes_type(input_bytes)) + output = guest.deserialize_stateless_output(output_bytes) + if not bool(output.successful_validation): + output_root = bytes(output.new_payload_request_root).hex() + raise ValidationError( + "EEST stateless guest returned unsuccessful_validation " + f"(newPayloadRequestRoot=0x{output_root}; " + f"{stateless_input_diagnostics(input_bytes, guest)})" + ) + + +def stateless_input_diagnostics(input_bytes: bytes, guest: EestGuest) -> str: + try: + stateless_input = guest.deserialize_stateless_input( + guest.bytes_type(input_bytes) + ) + chain_config = stateless_input.chain_config + active_fork = chain_config.active_fork + payload = stateless_input.new_payload_request.execution_payload + return ( + f"chainId={int(chain_config.chain_id)}, " + f"activeFork={active_fork.fork}, " + f"payloadBlockNumber={int(payload.block_number)}, " + f"payloadBlockHash=0x{bytes(payload.block_hash).hex()}" + ) + except Exception as error: + return ( + "statelessInputDiagnostics=" + f"{type(error).__name__}: {error}" + ) + + +def validate_batch_manifest( + batch: BatchEntry, + manifest: dict[str, Any] | None, + artifact_count: int, +) -> list[dict[str, Any]]: + failures = [] + if manifest is None: + return [batch_failure(batch, "manifest.json", "missing batch manifest")] + + checks = [ + ("network", batch.network), + ("batchStartBlock", batch.batch_start_block), + ("batchEndBlock", batch.batch_end_block), + ("batchSize", batch.batch_size), + ("artifactCount", batch.artifact_count), + ] + for field_name, expected in checks: + actual = manifest.get(field_name) + if actual != expected: + failures.append( + batch_failure( + batch, + "manifest.json", + f"{field_name} mismatch: expected {expected}, got {actual}", + ) + ) + if artifact_count != batch.artifact_count: + failures.append( + batch_failure( + batch, + "manifest.json", + ( + "artifact count mismatch: " + f"expected {batch.artifact_count}, got {artifact_count}" + ), + ) + ) + return failures + + +def decode_hex_bytes(field_name: str, value: Any) -> bytes: + raw = str_required(field_name, value) + hex_data = raw[2:] if raw.startswith(("0x", "0X")) else raw + if len(hex_data) % 2: + raise ValidationError(f"{field_name} must have an even hex length") + try: + return bytes.fromhex(hex_data) + except ValueError as error: + raise ValidationError(f"{field_name} is not valid hex") from error + + +def str_required(field_name: str, value: Any) -> str: + if not isinstance(value, str) or not value: + raise ValidationError(f"{field_name} must be a non-empty string") + return value + + +def int_required(field_name: str, value: Any) -> int: + if isinstance(value, bool) or not isinstance(value, int): + raise ValidationError(f"{field_name} must be an integer") + return value + + +def normalize_sha256(value: str) -> str: + digest = value[2:] if value.startswith(("0x", "0X")) else value + digest = digest.lower() + if len(digest) != 64: + raise ValidationError(f"invalid SHA-256 length for {value}") + try: + bytes.fromhex(digest) + except ValueError as error: + raise ValidationError(f"invalid SHA-256 hex for {value}") from error + return digest + + +def artifact_failure( + batch: BatchEntry, + archive_path: str, + error: Exception, + artifact: dict[str, Any] | None = None, +) -> dict[str, Any]: + failure = batch_failure( + batch, + archive_path, + f"{type(error).__name__}: {error}", + ) + if artifact is not None: + failure["blockNumber"] = artifact.get("blockNumber") + failure["blockHash"] = artifact.get("blockHash") + return failure + + +def batch_failure( + batch: BatchEntry, + archive_path: str, + error: str, +) -> dict[str, Any]: + return { + "batchPath": batch.path, + "batchStartBlock": batch.batch_start_block, + "batchEndBlock": batch.batch_end_block, + "archivePath": archive_path, + "error": error, + } + + +def fatal_summary( + args: argparse.Namespace, + started_at: float, + error: Exception, +) -> dict[str, Any]: + return { + "catalogUrl": args.catalog_url, + "eest": { + "ref": args.eest_ref, + "commit": args.eest_commit, + }, + "selectedBatches": [], + "batches": [], + "failures": [], + "selection": { + "batchCount": args.batch_count, + "maxArtifacts": args.max_artifacts, + "blockNumber": args.block_number, + }, + "fatalError": { + "type": type(error).__name__, + "message": str(error), + }, + "totals": { + "selectedBatches": 0, + "artifactsValidated": 0, + "successfulArtifacts": 0, + "failures": 1, + "durationSeconds": round(time.monotonic() - started_at, 3), + }, + } + + +def write_outputs(summary: dict[str, Any], args: argparse.Namespace) -> None: + args.summary_json.parent.mkdir(parents=True, exist_ok=True) + args.summary_md.parent.mkdir(parents=True, exist_ok=True) + args.summary_json.write_text( + json.dumps(summary, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + args.summary_md.write_text(render_markdown_summary(summary), encoding="utf-8") + + +def render_console_summary(summary: dict[str, Any]) -> str: + totals = summary["totals"] + lines = [ + "", + "Validation summary:", + f" batches: {totals['selectedBatches']}", + f" artifacts validated: {totals['artifactsValidated']}", + f" successful artifacts: {totals['successfulArtifacts']}", + f" failures: {totals['failures']}", + f" duration seconds: {totals['durationSeconds']}", + ] + if "fatalError" in summary: + fatal_error = summary["fatalError"] + lines.append( + f" fatal error: {fatal_error['type']}: {fatal_error['message']}" + ) + return "\n".join(lines) + "\n" + + +def render_markdown_summary(summary: dict[str, Any]) -> str: + totals = summary["totals"] + eest = summary["eest"] + lines = [ + "# EEST R2 Stateless Input Validation", + "", + f"- Catalog: `{summary['catalogUrl']}`", + f"- EEST ref: `{eest['ref']}`", + f"- EEST commit: `{eest['commit']}`", + f"- Selected batches: `{totals['selectedBatches']}`", + f"- Artifacts validated: `{totals['artifactsValidated']}`", + f"- Successful artifacts: `{totals['successfulArtifacts']}`", + f"- Failures: `{totals['failures']}`", + f"- Duration seconds: `{totals['durationSeconds']}`", + "", + ] + selection = summary.get("selection", {}) + if selection: + lines.extend( + [ + "## Selection", + "", + f"- Batch count: `{selection.get('batchCount')}`", + f"- Max artifacts: `{selection.get('maxArtifacts')}`", + f"- Block number: `{selection.get('blockNumber')}`", + "", + ] + ) + + if "fatalError" in summary: + fatal_error = summary["fatalError"] + lines.extend( + [ + "## Fatal Error", + "", + f"`{fatal_error['type']}: {fatal_error['message']}`", + "", + ] + ) + + if summary["batches"]: + lines.extend( + [ + "## Batches", + "", + ( + "| Batch | Artifacts | Successful | Failures | " + "Downloaded bytes | SHA-256 |" + ), + "| --- | ---: | ---: | ---: | ---: | --- |", + ] + ) + for batch in summary["batches"]: + lines.append( + "| " + f"{markdown_cell(batch['path'])} | " + f"{batch['artifactsValidated']} | " + f"{batch['successfulArtifacts']} | " + f"{batch['failures']} | " + f"{batch['downloadedByteLength']} | " + f"`{short_hash(batch['downloadedSha256'])}` |" + ) + lines.append("") + + if summary["failures"]: + lines.extend(["## Failures", ""]) + lines.extend( + [ + "| Batch | Block | Block hash | Archive path | Error |", + "| --- | ---: | --- | --- | --- |", + ] + ) + for failure in summary["failures"][:FAILURE_MARKDOWN_LIMIT]: + lines.append( + "| " + f"{markdown_cell(failure['batchPath'])} | " + f"{markdown_cell(failure.get('blockNumber', ''))} | " + f"{markdown_cell(short_hash_or_empty(failure.get('blockHash')))} | " + f"{markdown_cell(failure['archivePath'])} | " + f"{markdown_cell(truncate(failure['error'], 220))} |" + ) + remaining = len(summary["failures"]) - FAILURE_MARKDOWN_LIMIT + if remaining > 0: + lines.append("") + lines.append( + f"Showing first {FAILURE_MARKDOWN_LIMIT} failures; " + f"{remaining} more are in the JSON artifact." + ) + lines.append("") + + return "\n".join(lines) + + +def markdown_cell(value: Any) -> str: + return str(value).replace("\n", " ").replace("|", "\\|") + + +def truncate(value: str, max_length: int) -> str: + if len(value) <= max_length: + return value + return value[: max_length - 3] + "..." + + +def short_hash(value: str) -> str: + digest = value[2:] if value.startswith("0x") else value + return "0x" + digest[:16] + "..." + + +def short_hash_or_empty(value: Any) -> str: + if not value: + return "" + return short_hash(str(value)) + + +def user_agent() -> str: + return "zkevm-benchmark-workload-eest-r2-validator/1.0" + + +if __name__ == "__main__": + sys.exit(main())