diff --git a/.cargo/audit.toml b/.cargo/audit.toml new file mode 100644 index 00000000000..97e464d1bf2 --- /dev/null +++ b/.cargo/audit.toml @@ -0,0 +1,5 @@ +[advisories] +# protobuf 2.28.0 (via prometheus 0.13.4) - crash due to uncontrolled recursion. +# Not exploitable in our context: protobuf is only used for Prometheus metrics +# serialization with trusted internal data, not for parsing untrusted input. +ignore = ["RUSTSEC-2024-0437"] diff --git a/.github/workflows/docker-reproducible.yml b/.github/workflows/docker-reproducible.yml index f3479e9468d..a4e67758b82 100644 --- a/.github/workflows/docker-reproducible.yml +++ b/.github/workflows/docker-reproducible.yml @@ -50,13 +50,13 @@ jobs: - arch: amd64 rust_target: x86_64-unknown-linux-gnu rust_image: >- - rust:1.88-bullseye@sha256:8e3c421122bf4cd3b2a866af41a4dd52d87ad9e315fd2cb5100e87a7187a9816 + rust:1.91-bullseye@sha256:ed6afcf912afc6aeddf0d1ff0dc6894c9b1c8f865964ef3f533e3ea77a64ffea platform: linux/amd64 runner: ubuntu-22.04 - arch: arm64 rust_target: aarch64-unknown-linux-gnu rust_image: >- - rust:1.88-bullseye@sha256:8b22455a7ce2adb1355067638284ee99d21cc516fab63a96c4514beaf370aa94 + rust:1.91-bullseye@sha256:2f06f086e3ceb2940b6f400f576aeec1abf6b6a7cbeb55a163ec2f9c0bbb1ed6 platform: linux/arm64 runner: ubuntu-22.04-arm runs-on: ${{ matrix.runner }} diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 7344a9367b7..25f7cbcac14 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -26,6 +26,10 @@ env: CARGO_INCREMENTAL: 0 # Enable portable to prevent issues with caching `blst` for the wrong CPU type TEST_FEATURES: portable + # Use Clang for C/C++ compilation. Required because leveldb-sys uses + # -Wthread-safety which is a Clang-only flag unsupported by GCC. + CC: clang + CXX: clang++ jobs: check-labels: runs-on: ubuntu-latest @@ -96,6 +100,8 @@ jobs: uses: foundry-rs/foundry-toolchain@v1 with: version: nightly-ca67d15f4abd46394b324c50e21e66f306a1162d + - name: Clear stale leveldb-sys cmake cache + run: rm -rf target/release/build/leveldb-sys-*/out/build 2>/dev/null || true - name: Run tests in release run: make test-release - name: Show cache stats diff --git a/.github/workflows/zkboost-tests.yml b/.github/workflows/zkboost-tests.yml new file mode 100644 index 00000000000..aaa15489d37 --- /dev/null +++ b/.github/workflows/zkboost-tests.yml @@ -0,0 +1,72 @@ +name: zkboost-tests + +on: + push: + branches: + - stable + - staging + - trying + - 'pr/*' + pull_request: + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + RUSTFLAGS: "-D warnings -C debuginfo=0" + CARGO_INCREMENTAL: 0 + TEST_FEATURES: portable + # Use Clang for C/C++ compilation. Required because leveldb-sys uses + # -Wthread-safety which is a Clang-only flag unsupported by GCC. + CC: clang + CXX: clang++ + +jobs: + check-labels: + runs-on: ubuntu-latest + name: Check for 'skip-ci' label + outputs: + skip_ci: ${{ steps.set-output.outputs.SKIP_CI }} + steps: + - name: check for skip-ci label + id: set-output + env: + LABELS: ${{ toJson(github.event.pull_request.labels) }} + run: | + SKIP_CI="false" + if [ -z "${LABELS}" ] || [ "${LABELS}" = "null" ]; then + LABELS="none"; + else + LABELS=$(echo ${LABELS} | jq -r '.[].name') + fi + for label in ${LABELS}; do + if [ "$label" = "skip-ci" ]; then + SKIP_CI="true" + break + fi + done + echo "skip_ci=$SKIP_CI" >> $GITHUB_OUTPUT + + zkboost-tests: + name: zkboost-tests + needs: [check-labels] + if: needs.check-labels.outputs.skip_ci != 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Install dependencies + run: sudo apt update && sudo apt install -y git gcc g++ make cmake pkg-config llvm-dev libclang-dev clang + - name: Get latest version of stable Rust + uses: moonrepo/setup-rust@v1 + with: + channel: stable + cache-target: release + bins: cargo-nextest + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Clear stale leveldb-sys cmake cache + run: rm -rf target/release/build/leveldb-sys-*/out/build 2>/dev/null || true + - name: Run proof_engine_zkboost integration tests + run: make test-zkboost diff --git a/Cargo.lock b/Cargo.lock index e53873dfc1c..5762b01e921 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,7 @@ name = "account_manager" version = "8.0.1" dependencies = [ "account_utils", - "bls", + "bls 0.2.0", "clap", "clap_utils", "directory", @@ -25,7 +25,7 @@ dependencies = [ "slot_clock", "tempfile", "tokio", - "types", + "types 0.2.1", "validator_dir", "zeroize", ] @@ -34,7 +34,7 @@ dependencies = [ name = "account_utils" version = "0.1.0" dependencies = [ - "bls", + "bls 0.2.0", "eth2_keystore", "eth2_wallet", "filesystem", @@ -44,11 +44,22 @@ dependencies = [ "serde", "serde_yaml", "tracing", - "types", + "types 0.2.1", "validator_dir", "zeroize", ] +[[package]] +name = "addchain" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e33f6a175ec6a9e0aca777567f9ff7c3deefc255660df887e7fa3585e9801d8" +dependencies = [ + "num-bigint 0.3.3", + "num-integer", + "num-traits", +] + [[package]] name = "adler2" version = "2.0.1" @@ -71,7 +82,7 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "cipher", "cpufeatures", ] @@ -96,7 +107,7 @@ version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "once_cell", "version_check", "zerocopy", @@ -119,31 +130,33 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.20" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bc32535569185cbcb6ad5fa64d989a47bccb9a08e27284b1f2a3ccf16e6d010" +checksum = "9247f0a399ef71aeb68f497b2b8fb348014f742b50d3b83b1e00dfe1b7d64b3d" dependencies = [ "alloy-primitives", + "alloy-rlp", "num_enum", + "serde", "strum", ] [[package]] name = "alloy-consensus" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e318e25fb719e747a7e8db1654170fc185024f3ed5b10f86c08d448a912f6e2" +checksum = "b0c0dc44157867da82c469c13186015b86abef209bf0e41625e4b68bac61d728" dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-serde", - "alloy-trie", + "alloy-trie 0.9.5", "alloy-tx-macros", "auto_impl", "borsh", "c-kzg", - "derive_more 2.0.1", + "derive_more 2.1.1", "either", "k256", "once_cell", @@ -152,14 +165,14 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-consensus-any" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "364380a845193a317bcb7a5398fc86cdb66c47ebe010771dde05f6869bf9e64a" +checksum = "ba4cdb42df3871cd6b346d6a938ec2ba69a9a0f49d1f82714bc5c48349268434" dependencies = [ "alloy-consensus", "alloy-eips", @@ -171,16 +184,16 @@ dependencies = [ [[package]] name = "alloy-dyn-abi" -version = "1.4.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdff496dd4e98a81f4861e66f7eaf5f2488971848bb42d9c892f871730245c8" +checksum = "cc2db5c583aaef0255aa63a4fe827f826090142528bba48d1bf4119b62780cad" dependencies = [ "alloy-json-abi", "alloy-primitives", "alloy-sol-type-parser", "alloy-sol-types", "itoa", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -193,7 +206,7 @@ dependencies = [ "alloy-rlp", "crc", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -213,42 +226,102 @@ name = "alloy-eip7702" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2919c5a56a1007492da313e7a3b6d45ef5edc5d33416fdec63c0d7a2702a0d20" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "borsh", + "k256", + "serde", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-eip7928" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" dependencies = [ "alloy-primitives", "alloy-rlp", "borsh", "serde", - "thiserror 2.0.17", ] [[package]] name = "alloy-eips" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c4d7c5839d9f3a467900c625416b24328450c65702eb3d8caff8813e4d1d33" +checksum = "b9f7ef09f21bd1e9cb8a686f168cb4a206646804567f0889eadb8dcc4c9288c8" dependencies = [ "alloy-eip2124", "alloy-eip2930", "alloy-eip7702", + "alloy-eip7928", "alloy-primitives", "alloy-rlp", "alloy-serde", "auto_impl", "borsh", "c-kzg", - "derive_more 2.0.1", + "derive_more 2.1.1", "either", "serde", "serde_with", "sha2", - "thiserror 2.0.17", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-evm" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b99ba7b74a87176f31ee1cd26768f7155b0eeff61ed925f59b13085ffe5f891" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-hardforks", + "alloy-primitives", + "alloy-sol-types", + "auto_impl", + "derive_more 2.1.1", + "revm", + "thiserror 2.0.18", +] + +[[package]] +name = "alloy-genesis" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c9cf3b99f46615fbf7dc1add0c96553abb7bf88fc9ec70dfbe7ad0b47ba7fe8" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "alloy-trie 0.9.5", + "borsh", + "serde", + "serde_with", +] + +[[package]] +name = "alloy-hardforks" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83ba208044232d14d4adbfa77e57d6329f51bc1acc21f5667bb7db72d88a0831" +dependencies = [ + "alloy-chains", + "alloy-eip2124", + "alloy-primitives", + "auto_impl", + "dyn-clone", ] [[package]] name = "alloy-json-abi" -version = "1.4.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5513d5e6bd1cba6bdcf5373470f559f320c05c8c59493b6e98912fbe6733943f" +checksum = "e9dbe713da0c737d9e5e387b0ba790eb98b14dd207fe53eef50e19a5a8ec3dac" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -258,24 +331,24 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f72cf87cda808e593381fb9f005ffa4d2475552b7a6c5ac33d087bf77d82abd0" +checksum = "ff42cd777eea61f370c0b10f2648a1c81e0b783066cd7269228aa993afd487f7" dependencies = [ "alloy-primitives", "alloy-sol-types", - "http 1.3.1", + "http 1.4.0", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-network" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12aeb37b6f2e61b93b1c3d34d01ee720207c76fe447e2a2c217e433ac75b17f5" +checksum = "8cbca04f9b410fdc51aaaf88433cbac761213905a65fe832058bcf6690585762" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -290,18 +363,18 @@ dependencies = [ "alloy-sol-types", "async-trait", "auto_impl", - "derive_more 2.0.1", + "derive_more 2.1.1", "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-network-primitives" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abd29ace62872083e30929cd9b282d82723196d196db589f3ceda67edcc05552" +checksum = "42d6d15e069a8b11f56bef2eccbad2a873c6dd4d4c81d04dda29710f5ea52f04" dependencies = [ "alloy-consensus", "alloy-eips", @@ -312,20 +385,20 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "1.4.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355bf68a433e0fd7f7d33d5a9fc2583fde70bf5c530f63b80845f8da5505cf28" +checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" dependencies = [ "alloy-rlp", "arbitrary", "bytes", - "cfg-if", + "cfg-if 1.0.4", "const-hex", - "derive_more 2.0.1", + "derive_more 2.1.1", "foldhash 0.2.0", - "getrandom 0.3.4", - "hashbrown 0.16.0", - "indexmap 2.12.0", + "getrandom 0.4.2", + "hashbrown 0.16.1", + "indexmap 2.13.0", "itoa", "k256", "keccak-asm", @@ -333,18 +406,18 @@ dependencies = [ "proptest", "proptest-derive", "rand 0.9.2", + "rapidhash", "ruint", "rustc-hash 2.1.1", "serde", "sha3", - "tiny-keccak", ] [[package]] name = "alloy-provider" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b710636d7126e08003b8217e24c09f0cca0b46d62f650a841736891b1ed1fc1" +checksum = "d181c8cc7cf4805d7e589bf4074d56d55064fa1a979f005a45a62b047616d870" dependencies = [ "alloy-chains", "alloy-consensus", @@ -366,13 +439,13 @@ dependencies = [ "either", "futures", "futures-utils-wasm", - "lru 0.13.0", + "lru 0.16.3", "parking_lot", "pin-project", "reqwest", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "url", @@ -381,9 +454,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f70d83b765fdc080dbcd4f4db70d8d23fe4761f2f02ebfa9146b833900634b4" +checksum = "e93e50f64a77ad9c5470bf2ad0ca02f228da70c792a8f06634801e202579f35e" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -392,20 +465,20 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" +checksum = "ce8849c74c9ca0f5a03da1c865e3eb6f768df816e67dd3721a398a8a7e398011" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "alloy-rpc-client" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0882e72d2c1c0c79dcf4ab60a67472d3f009a949f774d4c17d0bdb669cfde05" +checksum = "f2792758a93ae32a32e9047c843d536e1448044f78422d71bf7d7c05149e103f" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -418,7 +491,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower 0.5.3", "tracing", "url", "wasmtimer", @@ -426,20 +499,50 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a63fb40ed24e4c92505f488f9dd256e2afaed17faa1b7a221086ebba74f4122" +checksum = "dd720b63f82b457610f2eaaf1f32edf44efffe03ae25d537632e7d23e7929e1a" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", "alloy-serde", ] +[[package]] +name = "alloy-rpc-types-debug" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b21e1ad18ff1b31ff1030e046462ab8168cf8894e6778cd805c8bdfe2bd649" +dependencies = [ + "alloy-primitives", + "derive_more 2.1.1", + "serde", + "serde_with", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ac61f03f1edabccde1c687b5b25fff28f183afee64eaa2e767def3929e4457" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "derive_more 2.1.1", + "jsonwebtoken", + "rand 0.8.5", + "serde", + "strum", +] + [[package]] name = "alloy-rpc-types-eth" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9eae0c7c40da20684548cbc8577b6b7447f7bf4ddbac363df95e3da220e41e72" +checksum = "9b2dc411f13092f237d2bf6918caf80977fc2f51485f9b90cb2a2f956912c8c9" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -453,14 +556,14 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-serde" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0df1987ed0ff2d0159d76b52e7ddfc4e4fbddacc54d2fbee765e0d14d7c01b5" +checksum = "e2ce1e0dbf7720eee747700e300c99aac01b1a95bb93f493a01e78ee28bb1a37" dependencies = [ "alloy-primitives", "serde", @@ -469,9 +572,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff69deedee7232d7ce5330259025b868c5e6a52fa8dffda2c861fb3a5889b24" +checksum = "2425c6f314522c78e8198979c8cbf6769362be4da381d4152ea8eefce383535d" dependencies = [ "alloy-primitives", "async-trait", @@ -479,14 +582,14 @@ dependencies = [ "either", "elliptic-curve", "k256", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-signer-local" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72cfe0be3ec5a8c1a46b2e5a7047ed41121d360d97f4405bb7c1c784880c86cb" +checksum = "c3ecb71ee53d8d9c3fa7bac17542c8116ebc7a9726c91b1bf333ec3d04f5a789" dependencies = [ "alloy-consensus", "alloy-network", @@ -495,46 +598,46 @@ dependencies = [ "async-trait", "k256", "rand 0.8.5", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "alloy-sol-macro" -version = "1.4.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3ce480400051b5217f19d6e9a82d9010cdde20f1ae9c00d53591e4a1afbb312" +checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "alloy-sol-macro-expander" -version = "1.4.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028" +checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.12.0", + "indexmap 2.13.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.110", + "sha3", + "syn 2.0.117", "syn-solidity", - "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "1.4.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c" +checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" dependencies = [ "const-hex", "dunce", @@ -542,25 +645,25 @@ dependencies = [ "macro-string", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "1.4.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "954d1b2533b9b2c7959652df3076954ecb1122a28cc740aa84e7b0a49f6ac0a9" +checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" dependencies = [ "serde", - "winnow", + "winnow 0.7.15", ] [[package]] name = "alloy-sol-types" -version = "1.4.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70319350969a3af119da6fb3e9bddb1bce66c9ea933600cb297c8b1850ad2a3c" +checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -570,22 +673,22 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be98b07210d24acf5b793c99b759e9a696e4a2e67593aec0487ae3b3e1a2478c" +checksum = "fa186e560d523d196580c48bf00f1bf62e63041f28ecf276acc22f8b27bb9f53" dependencies = [ "alloy-json-rpc", "auto_impl", "base64 0.22.1", - "derive_more 2.0.1", + "derive_more 2.1.1", "futures", "futures-utils-wasm", "parking_lot", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", - "tower 0.5.2", + "tower 0.5.3", "tracing", "url", "wasmtimer", @@ -593,45 +696,61 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4198a1ee82e562cab85e7f3d5921aab725d9bd154b6ad5017f82df1695877c97" +checksum = "aa501ad58dd20acddbfebc65b52e60f05ebf97c52fa40d1b35e91f5e2da0ad0e" dependencies = [ "alloy-json-rpc", "alloy-transport", + "itertools 0.14.0", "reqwest", "serde_json", - "tower 0.5.2", + "tower 0.5.3", "tracing", "url", ] [[package]] name = "alloy-trie" -version = "0.9.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3412d52bb97c6c6cc27ccc28d4e6e8cf605469101193b50b0bd5813b1f990b5" +checksum = "983d99aa81f586cef9dae38443245e585840fcf0fc58b09aee0b1f27aed1d500" dependencies = [ "alloy-primitives", "alloy-rlp", "arrayvec", - "derive_more 2.0.1", - "nybbles", + "derive_more 2.1.1", + "nybbles 0.3.4", + "smallvec", + "tracing", +] + +[[package]] +name = "alloy-trie" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f14b5d9b2c2173980202c6ff470d96e7c5e202c65a9f67884ad565226df7fbb" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more 2.1.1", + "nybbles 0.4.8", "serde", "smallvec", + "thiserror 2.0.18", "tracing", ] [[package]] name = "alloy-tx-macros" -version = "1.1.3" +version = "1.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "333544408503f42d7d3792bfc0f7218b643d968a03d2c0ed383ae558fb4a76d0" +checksum = "6fa0c53e8c1e1ef4d01066b01c737fb62fc9397ab52c6e7bb5669f97d281b9bc" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -651,9 +770,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -666,15 +785,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -685,7 +804,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -696,14 +815,14 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arbitrary" @@ -716,9 +835,12 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.7.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" +checksum = "f9f3647c145568cec02c42054e07bdf9a5a698e15b466fb2341bfc393cd24aa5" +dependencies = [ + "rustversion", +] [[package]] name = "archery" @@ -729,6 +851,50 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "ark-bls12-381" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff 0.5.0", + "ark-std 0.5.0", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-poly", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.5", + "itertools 0.13.0", + "num-bigint 0.4.6", + "num-integer", + "num-traits", + "zeroize", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -740,7 +906,7 @@ dependencies = [ "ark-serialize 0.3.0", "ark-std 0.3.0", "derivative", - "num-bigint", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version 0.3.3", @@ -760,7 +926,7 @@ dependencies = [ "derivative", "digest 0.10.7", "itertools 0.10.5", - "num-bigint", + "num-bigint 0.4.6", "num-traits", "paste", "rustc_version 0.4.1", @@ -781,7 +947,7 @@ dependencies = [ "digest 0.10.7", "educe", "itertools 0.13.0", - "num-bigint", + "num-bigint 0.4.6", "num-traits", "paste", "zeroize", @@ -814,7 +980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -823,7 +989,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "quote", "syn 1.0.109", @@ -835,7 +1001,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "proc-macro2", "quote", @@ -848,11 +1014,26 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "ark-std 0.5.0", + "educe", + "fnv", + "hashbrown 0.15.5", ] [[package]] @@ -873,7 +1054,7 @@ checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" dependencies = [ "ark-std 0.4.0", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.6", ] [[package]] @@ -882,10 +1063,22 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" dependencies = [ + "ark-serialize-derive", "ark-std 0.5.0", "arrayvec", "digest 0.10.7", - "num-bigint", + "num-bigint 0.4.6", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", ] [[package]] @@ -935,9 +1128,6 @@ name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -dependencies = [ - "serde", -] [[package]] name = "asn1-rs" @@ -951,7 +1141,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", ] @@ -963,7 +1153,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", "synstructure", ] @@ -975,14 +1165,14 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "asn1_der" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" +checksum = "4858a9d740c5007a9069007c3b4e91152d0506f13c1b31dd49051fd537656156" [[package]] name = "assert-json-diff" @@ -1024,13 +1214,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.4", "concurrent-queue", "futures-io", "futures-lite", "parking", "polling", - "rustix 1.1.2", + "rustix 1.1.4", "slab", "windows-sys 0.61.2", ] @@ -1054,7 +1244,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -1065,7 +1255,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -1094,11 +1284,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9" dependencies = [ "base64 0.22.1", - "http 1.3.1", + "http 1.4.0", "log", "url", ] +[[package]] +name = "aurora-engine-modexp" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518bc5745a6264b5fd7b09dffb9667e400ee9e2bbe18555fac75e1fe9afa0df9" +dependencies = [ + "hex", + "num", +] + [[package]] name = "auto_impl" version = "1.3.0" @@ -1107,7 +1307,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -1116,6 +1316,28 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bffc006df10ac2a68c83692d734a465f8ee6c5b384d8545a636f81d858f4bf" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4321e568ed89bb5a7d291a7f37997c2c0df89809d7b6d12062c81ddb54aa782e" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "axum" version = "0.7.9" @@ -1123,24 +1345,68 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.4.5", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", + "hyper 1.8.1", + "hyper-util", "itoa", - "matchit", + "matchit 0.7.3", "memchr", "mime", "percent-encoding", "pin-project-lite", "rustversion", "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower 0.5.3", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +dependencies = [ + "axum-core 0.5.6", + "axum-macros", + "base64 0.22.1", + "bytes", + "form_urlencoded", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-util", + "itoa", + "matchit 0.8.4", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sha1", "sync_wrapper", - "tower 0.5.2", + "tokio", + "tokio-tungstenite", + "tower 0.5.3", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -1152,7 +1418,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "mime", @@ -1161,25 +1427,79 @@ dependencies = [ "sync_wrapper", "tower-layer", "tower-service", + "tracing", ] [[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - -[[package]] -name = "base16ct" -version = "0.2.0" +name = "axum-core" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] [[package]] -name = "base256emoji" -version = "1.0.2" +name = "axum-extra" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +checksum = "9963ff19f40c6102c76756ef0a46004c0d58957d87259fc9208ff8441c12ab96" +dependencies = [ + "axum 0.8.8", + "axum-core 0.5.6", + "bytes", + "futures-util", + "headers 0.4.1", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "serde_core", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" dependencies = [ "const-str", "match-lookup", @@ -1205,9 +1525,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.8.0" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "beacon_chain" @@ -1215,31 +1535,31 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "bitvec", - "bls", + "bls 0.2.0", "criterion", "educe", "eth2", "eth2_network_config", - "ethereum_hashing", + "ethereum_hashing 0.8.0", "ethereum_serde_utils", - "ethereum_ssz", - "ethereum_ssz_derive", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", "execution_layer", - "fixed_bytes", + "fixed_bytes 0.1.0", "fork_choice", "futures", "genesis", "hex", - "int_to_bytes", + "int_to_bytes 0.2.0", "itertools 0.10.5", - "kzg", + "kzg 0.1.0", "lighthouse_tracing", "lighthouse_version", "logging", "lru 0.12.5", "maplit", - "merkle_proof", - "metrics", + "merkle_proof 0.2.0", + "metrics 0.2.0", "milhouse", "mockall", "mockall_double", @@ -1257,7 +1577,7 @@ dependencies = [ "slasher", "slot_clock", "smallvec", - "ssz_types", + "ssz_types 0.14.0", "state_processing", "store", "strum", @@ -1267,10 +1587,10 @@ dependencies = [ "tokio", "tokio-stream", "tracing", - "tree_hash", - "tree_hash_derive", + "tree_hash 0.12.1", + "tree_hash_derive 0.12.1", "typenum", - "types", + "types 0.2.1", "zstd", ] @@ -1280,7 +1600,7 @@ version = "8.0.1" dependencies = [ "account_utils", "beacon_chain", - "bls", + "bls 0.2.0", "clap", "clap_utils", "client", @@ -1304,14 +1624,14 @@ dependencies = [ "strum", "task_executor", "tracing", - "types", + "types 0.2.1", ] [[package]] name = "beacon_node_fallback" version = "0.1.0" dependencies = [ - "bls", + "bls 0.2.0", "clap", "eth2", "futures", @@ -1323,7 +1643,7 @@ dependencies = [ "task_executor", "tokio", "tracing", - "types", + "types 0.2.1", "validator_metrics", "validator_test_rig", ] @@ -1337,7 +1657,7 @@ dependencies = [ "itertools 0.10.5", "lighthouse_network", "logging", - "metrics", + "metrics 0.2.0", "num_cpus", "parking_lot", "serde", @@ -1347,7 +1667,7 @@ dependencies = [ "tokio", "tokio-util", "tracing", - "types", + "types 0.2.1", ] [[package]] @@ -1359,13 +1679,23 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "serde", + "unty", +] + [[package]] name = "bindgen" version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cexpr", "clang-sys", "itertools 0.12.1", @@ -1378,7 +1708,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.110", + "syn 2.0.117", "which", ] @@ -1399,15 +1729,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitcoin-io" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" +checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953" [[package]] name = "bitcoin_hashes" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b" dependencies = [ "bitcoin-io", "hex-conservative", @@ -1421,9 +1751,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +dependencies = [ + "serde_core", +] [[package]] name = "bitvec" @@ -1433,6 +1766,7 @@ checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", + "serde", "tap", "wyz", ] @@ -1446,6 +1780,31 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "blake2b_simd" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79834656f71332577234b50bfc009996f7449e0c056884e6a02492ded0ca2f3" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + +[[package]] +name = "blake3" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2468ef7d57b3fb7e16b576e8377cdbde2320c60e1491e961d11da40fc4f02a2d" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if 1.0.4", + "constant_time_eq", + "cpufeatures", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -1471,18 +1830,63 @@ dependencies = [ "alloy-primitives", "arbitrary", "blst", - "ethereum_hashing", + "ethereum_hashing 0.8.0", + "ethereum_serde_utils", + "ethereum_ssz 0.10.1", + "fixed_bytes 0.1.0", + "hex", + "rand 0.9.2", + "safe_arith", + "serde", + "tree_hash 0.12.1", + "zeroize", +] + +[[package]] +name = "bls" +version = "0.2.0" +source = "git+https://github.com/sigp/lighthouse?branch=unstable#a965bfdf77a0b1a3cb2471b9df787edbe99779e8" +dependencies = [ + "alloy-primitives", + "blst", + "ethereum_hashing 0.8.0", "ethereum_serde_utils", - "ethereum_ssz", - "fixed_bytes", + "ethereum_ssz 0.10.1", + "fixed_bytes 0.1.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", "hex", "rand 0.9.2", "safe_arith", "serde", - "tree_hash", + "tree_hash 0.12.1", "zeroize", ] +[[package]] +name = "bls12_381" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3c196a77437e7cc2fb515ce413a6401291578b5afc8ecb29a3c7ab957f05941" +dependencies = [ + "ff 0.12.1", + "group 0.12.1", + "pairing 0.22.0", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "bls12_381" +version = "0.8.0" +source = "git+https://github.com/lambdaclass/bls12_381?branch=expose-fp-struct#219174187bd78154cec35b0809799fc2c991a579" +dependencies = [ + "digest 0.10.7", + "ff 0.13.1", + "group 0.13.0", + "pairing 0.23.0", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "blst" version = "0.3.16" @@ -1503,9 +1907,9 @@ checksum = "7a8a8ed6fefbeef4a8c7b460e4110e12c5e22a5b7cf32621aae6ad650c4dcf29" dependencies = [ "blst", "byte-slice-cast", - "ff", - "group", - "pairing", + "ff 0.13.1", + "group 0.13.0", + "pairing 0.23.0", "rand_core 0.6.4", "serde", "subtle", @@ -1520,7 +1924,7 @@ dependencies = [ "clap", "clap_utils", "eth2_network_config", - "ethereum_ssz", + "ethereum_ssz 0.10.1", "hex", "lighthouse_network", "log", @@ -1530,30 +1934,31 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", - "types", + "types 0.2.1", ] [[package]] name = "borsh" -version = "1.5.7" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad8646f98db542e39fc66e68a20b2144f6a732636df7c2354e74645faaa433ce" +checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" dependencies = [ "borsh-derive", + "bytes", "cfg_aliases", ] [[package]] name = "borsh-derive" -version = "1.5.7" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd1d3c0c2f5833f22386f252fe8ed005c7f59fdcddeef025c01b4c3b9fd9ac3" +checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -1575,10 +1980,10 @@ dependencies = [ name = "builder_client" version = "0.1.0" dependencies = [ - "bls", + "bls 0.2.0", "context_deserialize", "eth2", - "ethereum_ssz", + "ethereum_ssz 0.10.1", "lighthouse_version", "mockito", "reqwest", @@ -1590,9 +1995,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byte-slice-cast" @@ -1600,6 +2005,35 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7575182f7272186991736b70173b0ea045398f984bf5ebbb3804736ce1330c9d" +[[package]] +name = "bytecheck" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0caa33a2c0edca0419d15ac723dff03f1956f7978329b1e3b5fdaaaed9d3ca8b" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "rancor", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + [[package]] name = "byteorder" version = "1.5.0" @@ -1608,18 +2042,18 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" dependencies = [ "serde", ] [[package]] name = "c-kzg" -version = "2.1.5" +version = "2.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e00bf4b112b07b505472dbefd19e37e53307e2bfed5a79e0cc161d58ccd0e687" +checksum = "6648ed1e4ea8e8a1a4a2c78e1cda29a3fd500bc622899c340d8525ea9a76b24a" dependencies = [ "blst", "cc", @@ -1632,9 +2066,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" dependencies = [ "serde_core", ] @@ -1659,7 +2093,7 @@ dependencies = [ "semver 1.0.27", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -1670,9 +2104,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.46" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "jobserver", @@ -1689,6 +2123,12 @@ dependencies = [ "nom", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.4" @@ -1707,7 +2147,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "cipher", "cpufeatures", ] @@ -1727,9 +2167,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -1790,9 +2230,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.53" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", "clap_derive", @@ -1800,34 +2240,34 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.53" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", "terminal_size", ] [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "clap_utils" @@ -1837,12 +2277,12 @@ dependencies = [ "clap", "dirs", "eth2_network_config", - "ethereum_ssz", + "ethereum_ssz 0.10.1", "hex", "serde", "serde_json", "serde_yaml", - "types", + "types 0.2.1", ] [[package]] @@ -1856,16 +2296,16 @@ dependencies = [ "environment", "eth2", "eth2_config", - "ethereum_ssz", + "ethereum_ssz 0.10.1", "execution_layer", "futures", "genesis", "http_api", "http_metrics", - "kzg", + "kzg 0.1.0", "lighthouse_network", "logging", - "metrics", + "metrics 0.2.0", "monitoring_api", "network", "operation_pool", @@ -1885,38 +2325,38 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", - "types", + "types 0.2.1", ] [[package]] name = "cmake" -version = "0.1.54" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" dependencies = [ "cc", ] [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "colored" -version = "3.0.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" +checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "compare_fields" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05162add7c8618791829528194a271dca93f69194d35b19db1ca7fbfb8275278" +checksum = "f6f45d0b4d61b582303179fb7a1a142bc9d647b7583db3b0d5f25a21d286fab9" dependencies = [ "compare_fields_derive", "itertools 0.14.0", @@ -1924,12 +2364,21 @@ dependencies = [ [[package]] name = "compare_fields_derive" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f5ee468b2e568b668e2a686112935e7bbe9a81bf4fa6b9f6fc3410ea45fb7ce" +checksum = "92ff1dbbda10d495b2c92749c002b2025e0be98f42d1741ecc9ff820d2f04dce" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.117", +] + +[[package]] +name = "concat-kdf" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d72c1252426a83be2092dd5884a5f6e3b8e7180f6891b6263d2c21b92ec8816" +dependencies = [ + "digest 0.10.7", ] [[package]] @@ -1938,7 +2387,7 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.8.21", ] [[package]] @@ -1961,8 +2410,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6539aa9c6a4cd31f4b1c040f860a1eac9aa80e7df6b05d506a6e7179936d6a01" dependencies = [ "console-api", - "crossbeam-channel", - "crossbeam-utils", + "crossbeam-channel 0.5.15", + "crossbeam-utils 0.8.21", "futures-task", "hdrhistogram", "humantime", @@ -1982,11 +2431,11 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.17.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" +checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "cpufeatures", "proptest", "serde_core", @@ -2024,11 +2473,17 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + [[package]] name = "context_deserialize" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5f9ea0a0ae2de4943f5ca71590b6dbd0b952475f0a0cafb30a470cec78c8b9" +checksum = "4c523eea4af094b5970c321f4604abc42c5549d3cbae332e98325403fbbdbf70" dependencies = [ "context_deserialize_derive", "serde", @@ -2036,12 +2491,12 @@ dependencies = [ [[package]] name = "context_deserialize_derive" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c57b2db1e4e3ed804dcc49894a144b68fe6c754b8f545eb1dda7ad3c7dbe7e6" +checksum = "3b7bf98c48ffa511b14bb3c76202c24a8742cea1efa9570391c5d41373419a09" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] @@ -2050,6 +2505,24 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -2096,9 +2569,9 @@ dependencies = [ [[package]] name = "crc" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] @@ -2115,7 +2588,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", ] [[package]] @@ -2160,13 +2633,61 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "crossbeam" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e" +dependencies = [ + "cfg-if 0.1.10", + "crossbeam-channel 0.4.4", + "crossbeam-deque 0.7.4", + "crossbeam-epoch 0.8.2", + "crossbeam-queue 0.2.3", + "crossbeam-utils 0.7.2", +] + +[[package]] +name = "crossbeam" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +dependencies = [ + "crossbeam-channel 0.5.15", + "crossbeam-deque 0.8.6", + "crossbeam-epoch 0.9.18", + "crossbeam-queue 0.3.12", + "crossbeam-utils 0.8.21", +] + +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.8.21", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed" +dependencies = [ + "crossbeam-epoch 0.8.2", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] @@ -2175,8 +2696,23 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", + "crossbeam-epoch 0.9.18", + "crossbeam-utils 0.8.21", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "lazy_static", + "maybe-uninit", + "memoffset 0.5.6", + "scopeguard", ] [[package]] @@ -2185,7 +2721,38 @@ version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.8.21", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +dependencies = [ + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils 0.8.21", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "lazy_static", ] [[package]] @@ -2234,12 +2801,12 @@ dependencies = [ [[package]] name = "ctrlc" -version = "3.5.1" +version = "3.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73736a89c4aff73035ba2ed2e565061954da00d4970fc9ac25dcc85a2a20d790" +checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" dependencies = [ "dispatch2", - "nix 0.30.1", + "nix 0.31.2", "windows-sys 0.61.2", ] @@ -2249,7 +2816,7 @@ version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "cpufeatures", "curve25519-dalek-derive", "digest 0.10.7", @@ -2267,17 +2834,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", -] - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", + "syn 2.0.117", ] [[package]] @@ -2301,17 +2858,13 @@ dependencies = [ ] [[package]] -name = "darling_core" -version = "0.13.4" +name = "darling" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim 0.10.0", - "syn 1.0.109", + "darling_core 0.23.0", + "darling_macro 0.23.0", ] [[package]] @@ -2324,8 +2877,8 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim 0.11.1", - "syn 2.0.110", + "strsim", + "syn 2.0.117", ] [[package]] @@ -2339,19 +2892,21 @@ dependencies = [ "proc-macro2", "quote", "serde", - "strsim 0.11.1", - "syn 2.0.110", + "strsim", + "syn 2.0.117", ] [[package]] -name = "darling_macro" -version = "0.13.4" +name = "darling_core" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" dependencies = [ - "darling_core 0.13.4", + "ident_case", + "proc-macro2", "quote", - "syn 1.0.109", + "strsim", + "syn 2.0.117", ] [[package]] @@ -2362,7 +2917,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -2373,7 +2928,18 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.110", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core 0.23.0", + "quote", + "syn 2.0.117", ] [[package]] @@ -2402,8 +2968,8 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ - "cfg-if", - "crossbeam-utils", + "cfg-if 1.0.4", + "crossbeam-utils 0.8.21", "hashbrown 0.14.5", "lock_api", "once_cell", @@ -2412,15 +2978,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] name = "data-encoding-macro" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47ce6c96ea0102f01122a185683611bd5ac8d99e62bc59dd12e6bda344ee673d" +checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -2428,12 +2994,12 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" +checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" dependencies = [ "data-encoding", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -2450,7 +3016,19 @@ dependencies = [ "store", "strum", "tracing", - "types", + "types 0.2.1", +] + +[[package]] +name = "datatest-stable" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833306ca7eec4d95844e65f0d7502db43888c5c1006c6c517e8cf51a27d15431" +dependencies = [ + "camino", + "fancy-regex", + "libtest-mimic", + "walkdir", ] [[package]] @@ -2477,14 +3055,14 @@ dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", - "bls", - "ethereum_ssz", + "bls 0.2.0", + "ethereum_ssz 0.10.1", "hex", "reqwest", "serde_json", "sha2", - "tree_hash", - "types", + "tree_hash 0.12.1", + "types 0.2.1", ] [[package]] @@ -2494,6 +3072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -2506,16 +3085,16 @@ dependencies = [ "asn1-rs", "displaydoc", "nom", - "num-bigint", + "num-bigint 0.4.6", "num-traits", "rusticata-macros", ] [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", "serde_core", @@ -2532,6 +3111,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive-where" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "derive_arbitrary" version = "1.4.2" @@ -2540,7 +3130,38 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.117", ] [[package]] @@ -2549,31 +3170,55 @@ version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ - "convert_case", + "convert_case 0.4.0", "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "derive_more" -version = "2.0.1" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl 1.0.0", +] + +[[package]] +name = "derive_more" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" dependencies = [ - "derive_more-impl", + "derive_more-impl 2.1.1", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "convert_case 0.6.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "unicode-xid", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case 0.10.0", "proc-macro2", "quote", - "syn 2.0.110", + "rustc_version 0.4.1", + "syn 2.0.117", "unicode-xid", ] @@ -2662,11 +3307,11 @@ dependencies = [ [[package]] name = "dispatch2" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "block2", "libc", "objc2", @@ -2680,7 +3325,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -2688,7 +3333,7 @@ name = "doppelganger_service" version = "0.1.0" dependencies = [ "beacon_node_fallback", - "bls", + "bls 0.2.0", "environment", "eth2", "futures", @@ -2698,7 +3343,7 @@ dependencies = [ "task_executor", "tokio", "tracing", - "types", + "types 0.2.1", "validator_store", ] @@ -2710,9 +3355,9 @@ checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" [[package]] name = "dtoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" [[package]] name = "dunce" @@ -2775,7 +3420,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -2784,18 +3429,18 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "beacon_chain", - "bls", + "bls 0.2.0", "compare_fields", "context_deserialize", "educe", "eth2_network_config", - "ethereum_ssz", - "ethereum_ssz_derive", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", "execution_layer", "fork_choice", "fs2", "hex", - "kzg", + "kzg 0.1.0", "logging", "milhouse", "rayon", @@ -2804,13 +3449,13 @@ dependencies = [ "serde_repr", "serde_yaml", "snap", - "ssz_types", + "ssz_types 0.14.0", "state_processing", - "swap_or_not_shuffle", - "tree_hash", - "tree_hash_derive", + "swap_or_not_shuffle 0.2.0", + "tree_hash 0.12.1", + "tree_hash_derive 0.12.1", "typenum", - "types", + "types 0.2.1", ] [[package]] @@ -2837,13 +3482,13 @@ name = "eip_3076" version = "0.1.0" dependencies = [ "arbitrary", - "bls", + "bls 0.2.0", "ethereum_serde_utils", - "fixed_bytes", + "fixed_bytes 0.1.0", "serde", "serde_json", "tempfile", - "types", + "types 0.2.1", ] [[package]] @@ -2863,9 +3508,9 @@ checksum = "05c599a59deba6188afd9f783507e4d89efc997f0fa340a758f0d0992b322416" dependencies = [ "blst", "blstrs", - "ff", - "group", - "pairing", + "ff 0.13.1", + "group 0.13.0", + "pairing 0.23.0", "subtle", ] @@ -2941,6 +3586,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "elf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -2950,9 +3601,10 @@ dependencies = [ "base16ct", "crypto-bigint", "digest 0.10.7", - "ff", + "ff 0.13.1", "generic-array", - "group", + "group 0.13.0", + "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", @@ -2967,7 +3619,7 @@ version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", ] [[package]] @@ -2998,7 +3650,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -3018,7 +3670,7 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -3040,7 +3692,16 @@ dependencies = [ "tracing-appender", "tracing-log", "tracing-subscriber", - "types", + "types 0.2.1", +] + +[[package]] +name = "envy" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47e0157f2cb54f5ae1bd371b30a2ae4311e1c028f575cd4e81de7353215965" +dependencies = [ + "serde", ] [[package]] @@ -3049,6 +3710,54 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "ere-io" +version = "0.3.0" +source = "git+https://github.com/eth-act/ere?tag=v0.3.0#ffc0e230cf4387fb22eb755f0fb323b38294520f" +dependencies = [ + "bincode 2.0.1", + "rkyv", + "serde", +] + +[[package]] +name = "ere-platform-trait" +version = "0.3.0" +source = "git+https://github.com/eth-act/ere?tag=v0.3.0#ffc0e230cf4387fb22eb755f0fb323b38294520f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "ere-server" +version = "0.3.0" +source = "git+https://github.com/eth-act/ere?tag=v0.3.0#ffc0e230cf4387fb22eb755f0fb323b38294520f" +dependencies = [ + "anyhow", + "bincode 2.0.1", + "ere-zkvm-interface", + "prost", + "serde", + "thiserror 2.0.18", + "tokio", + "twirp", +] + +[[package]] +name = "ere-zkvm-interface" +version = "0.3.0" +source = "git+https://github.com/eth-act/ere?tag=v0.3.0#ffc0e230cf4387fb22eb755f0fb323b38294520f" +dependencies = [ + "anyhow", + "auto_impl", + "bincode 2.0.1", + "clap", + "indexmap 2.13.0", + "serde", + "strum", + "thiserror 2.0.18", +] + [[package]] name = "errno" version = "0.3.14" @@ -3056,21 +3765,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] +[[package]] +name = "escape8259" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" + [[package]] name = "eth2" version = "0.1.0" dependencies = [ - "bls", + "bls 0.2.0", "context_deserialize", "educe", "eip_3076", "eth2_keystore", "ethereum_serde_utils", - "ethereum_ssz", - "ethereum_ssz_derive", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", "futures", "futures-util", "mediatype", @@ -3082,11 +3797,11 @@ dependencies = [ "sensitive_url", "serde", "serde_json", - "ssz_types", + "ssz_types 0.14.0", "superstruct", - "test_random_derive", + "test_random_derive 0.2.0", "tokio", - "types", + "types 0.2.1", "zeroize", ] @@ -3095,7 +3810,7 @@ name = "eth2_config" version = "0.2.0" dependencies = [ "paste", - "types", + "types 0.2.1", ] [[package]] @@ -3103,10 +3818,23 @@ name = "eth2_interop_keypairs" version = "0.2.0" dependencies = [ "base64 0.13.1", - "bls", - "ethereum_hashing", + "bls 0.2.0", + "ethereum_hashing 0.8.0", + "hex", + "num-bigint 0.4.6", + "serde", + "serde_yaml", +] + +[[package]] +name = "eth2_interop_keypairs" +version = "0.2.0" +source = "git+https://github.com/sigp/lighthouse?branch=unstable#a965bfdf77a0b1a3cb2471b9df787edbe99779e8" +dependencies = [ + "bls 0.2.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", + "ethereum_hashing 0.8.0", "hex", - "num-bigint", + "num-bigint 0.4.6", "serde", "serde_yaml", ] @@ -3115,7 +3843,7 @@ dependencies = [ name = "eth2_key_derivation" version = "0.1.0" dependencies = [ - "bls", + "bls 0.2.0", "hex", "num-bigint-dig", "ring", @@ -3128,7 +3856,7 @@ name = "eth2_keystore" version = "0.1.0" dependencies = [ "aes", - "bls", + "bls 0.2.0", "cipher", "ctr", "eth2_key_derivation", @@ -3154,9 +3882,9 @@ dependencies = [ "bytes", "discv5", "eth2_config", - "ethereum_ssz", - "fixed_bytes", - "kzg", + "ethereum_ssz 0.10.1", + "fixed_bytes 0.1.0", + "kzg 0.1.0", "pretty_reqwest_error", "reqwest", "sensitive_url", @@ -3165,7 +3893,7 @@ dependencies = [ "tempfile", "tokio", "tracing", - "types", + "types 0.2.1", "url", "zip", ] @@ -3195,6 +3923,44 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ethbloom" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c321610643004cf908ec0f5f2aa0d8f1f8e14b540562a2887a1111ff1ecbf7b" +dependencies = [ + "crunchy", + "fixed-hash", + "impl-rlp", + "impl-serde", + "tiny-keccak", +] + +[[package]] +name = "ethereum-types" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab15ed80916029f878e0267c3a9f92b67df55e79af370bf66199059ae2b4ee3" +dependencies = [ + "ethbloom", + "fixed-hash", + "impl-rlp", + "impl-serde", + "primitive-types 0.13.1", + "uint 0.10.0", +] + +[[package]] +name = "ethereum_hashing" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c853bd72c9e5787f8aafc3df2907c2ed03cff3150c3acd94e2e53a98ab70a8ab" +dependencies = [ + "cpufeatures", + "ring", + "sha2", +] + [[package]] name = "ethereum_hashing" version = "0.8.0" @@ -3221,15 +3987,30 @@ dependencies = [ [[package]] name = "ethereum_ssz" -version = "0.10.0" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dcddb2554d19cde19b099fadddde576929d7a4d0c1cd3512d1fd95cf174375c" +dependencies = [ + "alloy-primitives", + "ethereum_serde_utils", + "itertools 0.13.0", + "serde", + "serde_derive", + "smallvec", + "typenum", +] + +[[package]] +name = "ethereum_ssz" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8cd8c4f47dfb947dbfe3cdf2945ae1da808dbedc592668658e827a12659ba1" +checksum = "2128a84f7a3850d54ee343334e3392cca61f9f6aa9441eec481b9394b43c238b" dependencies = [ "alloy-primitives", "arbitrary", "context_deserialize", "ethereum_serde_utils", - "itertools 0.13.0", + "itertools 0.14.0", "serde", "serde_derive", "smallvec", @@ -3238,123 +4019,449 @@ dependencies = [ [[package]] name = "ethereum_ssz_derive" -version = "0.10.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78d247bc40823c365a62e572441a8f8b12df03f171713f06bc76180fcd56ab71" +checksum = "a657b6b3b7e153637dc6bdc6566ad9279d9ee11a15b12cfb24a2e04360637e9f" dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] -name = "event-listener" -version = "2.5.3" +name = "ethereum_ssz_derive" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +checksum = "cd596f91cff004fc8d02be44c21c0f9b93140a04b66027ae052f5f8e05b48eba" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] [[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +name = "ethrex-blockchain" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", + "bytes", + "ethrex-common", + "ethrex-crypto", + "ethrex-metrics", + "ethrex-rlp", + "ethrex-storage", + "ethrex-trie", + "ethrex-vm", + "hex", + "rustc-hash 2.1.1", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tracing", ] [[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +name = "ethrex-common" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" dependencies = [ - "event-listener 5.4.1", - "pin-project-lite", + "bytes", + "crc32fast", + "ethereum-types", + "ethrex-crypto", + "ethrex-rlp", + "ethrex-trie", + "hex", + "hex-literal", + "k256", + "kzg-rs", + "lazy_static", + "libc", + "once_cell", + "rayon", + "rkyv", + "rustc-hash 2.1.1", + "serde", + "serde_json", + "sha2", + "sha3", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "url", ] [[package]] -name = "eventsource-stream" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +name = "ethrex-crypto" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" dependencies = [ - "futures-core", - "nom", - "pin-project-lite", + "c-kzg", + "kzg-rs", + "thiserror 2.0.18", + "tiny-keccak", ] [[package]] -name = "execution_engine_integration" -version = "0.1.0" +name = "ethrex-l2-common" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" dependencies = [ - "alloy-network", - "alloy-primitives", - "alloy-provider", - "alloy-rpc-types-eth", - "alloy-signer-local", - "async-channel 1.9.0", - "bls", - "deposit_contract", - "execution_layer", - "fixed_bytes", - "fork_choice", - "futures", + "bytes", + "ethereum-types", + "ethrex-common", + "ethrex-crypto", + "ethrex-rlp", + "ethrex-storage", + "ethrex-trie", + "ethrex-vm", "hex", - "logging", - "network_utils", - "reqwest", - "sensitive_url", - "serde_json", - "task_executor", - "tempfile", - "tokio", - "typenum", - "types", + "k256", + "lambdaworks-crypto", + "rkyv", + "serde", + "serde_with", + "sha3", + "thiserror 2.0.18", + "tracing", ] [[package]] -name = "execution_layer" -version = "0.1.0" +name = "ethrex-levm" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" dependencies = [ - "alloy-consensus", - "alloy-primitives", - "alloy-rlp", - "alloy-rpc-types-eth", - "anyhow", + "ark-bn254", + "ark-ec", + "ark-ff 0.5.0", + "bitvec", + "bls12_381 0.8.0", + "bytes", + "datatest-stable", + "derive_more 1.0.0", + "ethrex-common", + "ethrex-crypto", + "ethrex-rlp", + "k256", + "lambdaworks-math", + "lazy_static", + "malachite", + "p256", + "ripemd", + "rustc-hash 2.1.1", + "serde", + "serde_json", + "sha2", + "sha3", + "strum", + "thiserror 2.0.18", + "walkdir", +] + +[[package]] +name = "ethrex-metrics" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" +dependencies = [ + "axum 0.8.8", + "ethrex-common", + "prometheus 0.13.4", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "ethrex-p2p" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" +dependencies = [ + "aes", + "async-trait", + "bytes", + "concat-kdf", + "crossbeam 0.8.4", + "ctr", + "ethereum-types", + "ethrex-blockchain", + "ethrex-common", + "ethrex-crypto", + "ethrex-rlp", + "ethrex-storage", + "ethrex-threadpool", + "ethrex-trie", + "futures", + "hex", + "hmac", + "indexmap 2.13.0", + "lazy_static", + "prometheus 0.14.0", + "rand 0.8.5", + "rayon", + "rustc-hash 2.1.1", + "secp256k1", + "serde", + "serde_json", + "sha2", + "snap", + "spawned-concurrency", + "spawned-rt", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "ethrex-rlp" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" +dependencies = [ + "bytes", + "ethereum-types", + "hex", + "lazy_static", + "snap", + "thiserror 2.0.18", + "tinyvec", +] + +[[package]] +name = "ethrex-rpc" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" +dependencies = [ + "axum 0.8.8", + "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", + "rand 0.8.5", + "reqwest", + "secp256k1", + "serde", + "serde_json", + "sha2", + "spawned-concurrency", + "spawned-rt", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "tower-http", + "tracing", + "tracing-subscriber", + "uuid 1.22.0", +] + +[[package]] +name = "ethrex-storage" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "ethereum-types", + "ethrex-common", + "ethrex-crypto", + "ethrex-rlp", + "ethrex-trie", + "hex", + "lru 0.16.3", + "qfilter", + "rayon", + "rustc-hash 2.1.1", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tracing", +] + +[[package]] +name = "ethrex-threadpool" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" +dependencies = [ + "crossbeam 0.8.4", +] + +[[package]] +name = "ethrex-trie" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" +dependencies = [ + "anyhow", + "bytes", + "crossbeam 0.8.4", + "digest 0.10.7", + "ethereum-types", + "ethrex-crypto", + "ethrex-rlp", + "ethrex-threadpool", + "hex", + "lazy_static", + "rkyv", + "rustc-hash 2.1.1", + "serde", + "serde_json", + "smallvec", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "ethrex-vm" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" +dependencies = [ + "bincode 1.3.3", + "bytes", + "derive_more 1.0.0", + "dyn-clone", + "ethereum-types", + "ethrex-common", + "ethrex-crypto", + "ethrex-levm", + "ethrex-rlp", + "ethrex-trie", + "lazy_static", + "rkyv", + "serde", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.1", + "pin-project-lite", +] + +[[package]] +name = "eventsource-stream" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74fef4569247a5f429d9156b9d0a2599914385dd189c539334c625d8099d90ab" +dependencies = [ + "futures-core", + "nom", + "pin-project-lite", +] + +[[package]] +name = "execution_engine_integration" +version = "0.1.0" +dependencies = [ + "alloy-network", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-types-eth", + "alloy-signer-local", + "async-channel 1.9.0", + "bls 0.2.0", + "deposit_contract", + "execution_layer", + "fixed_bytes 0.1.0", + "fork_choice", + "futures", + "hex", + "logging", + "network_utils", + "reqwest", + "sensitive_url", + "serde_json", + "task_executor", + "tempfile", + "tokio", + "typenum", + "types 0.2.1", +] + +[[package]] +name = "execution_layer" +version = "0.1.0" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "anyhow", "arc-swap", + "async-stream", "async-trait", - "bls", + "bls 0.2.0", "builder_client", "bytes", "eth2", "ethereum_serde_utils", - "ethereum_ssz", - "fixed_bytes", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "fixed_bytes 0.1.0", "fork_choice", + "futures", "hash-db", "hash256-std-hasher", "hex", "jsonwebtoken", "keccak-hash", - "kzg", + "kzg 0.1.0", "lighthouse_version", "logging", "lru 0.12.5", - "metrics", + "metrics 0.2.0", "parking_lot", "pretty_reqwest_error", "rand 0.9.2", "reqwest", + "reqwest-eventsource", "sensitive_url", "serde", "serde_json", "sha2", "slot_clock", - "ssz_types", + "ssz_types 0.14.0", "state_processing", + "store", "strum", "superstruct", "task_executor", @@ -3362,11 +4469,11 @@ dependencies = [ "tokio", "tokio-stream", "tracing", - "tree_hash", - "tree_hash_derive", + "tree_hash 0.12.1", + "tree_hash_derive 0.12.1", "triehash", "typenum", - "types", + "types 0.2.1", "warp", "zeroize", ] @@ -3383,6 +4490,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fancy-regex" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -3421,6 +4539,17 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "ff" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d013fc25338cc558c5c2cfbad646908fb23591e2404481826742b651c9af7160" +dependencies = [ + "bitvec", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "ff" version = "0.13.1" @@ -3428,10 +4557,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" dependencies = [ "bitvec", + "byteorder", + "ff_derive", "rand_core 0.6.4", "subtle", ] +[[package]] +name = "ff_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f10d12652036b0e99197587c6ba87a8fc3031986499973c030d8b44fcc151b60" +dependencies = [ + "addchain", + "num-bigint 0.3.3", + "num-integer", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "ffi-opaque" version = "2.0.1" @@ -3450,7 +4596,7 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" dependencies = [ - "memoffset", + "memoffset 0.9.1", "rustc_version 0.4.1", ] @@ -3464,9 +4610,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.5" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fixed-hash" @@ -3480,9 +4626,38 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixed-map" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ed19add84e8cb9e8cc5f7074de0324247149ffef0b851e215fb0edc50c229b" +dependencies = [ + "fixed-map-derive", +] + +[[package]] +name = "fixed-map-derive" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dc7a9cb3326bafb80642c5ce99b39a2c0702d4bfa8ee8a3e773791a6cbe2407" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "fixed_bytes" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "safe_arith", +] + [[package]] name = "fixed_bytes" version = "0.1.0" +source = "git+https://github.com/sigp/lighthouse?branch=unstable#a965bfdf77a0b1a3cb2471b9df787edbe99779e8" dependencies = [ "alloy-primitives", "safe_arith", @@ -3490,14 +4665,14 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", - "libz-rs-sys", "libz-sys", "miniz_oxide", + "zlib-rs", ] [[package]] @@ -3538,18 +4713,18 @@ name = "fork_choice" version = "0.1.0" dependencies = [ "beacon_chain", - "ethereum_ssz", - "ethereum_ssz_derive", - "fixed_bytes", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "fixed_bytes 0.1.0", "logging", - "metrics", + "metrics 0.2.0", "proto_array", "state_processing", "store", "superstruct", "tokio", "tracing", - "types", + "types 0.2.1", ] [[package]] @@ -3577,6 +4752,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "funty" version = "2.0.0" @@ -3585,9 +4766,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -3610,9 +4791,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -3620,27 +4801,26 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", "futures-util", - "num_cpus", ] [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -3654,13 +4834,13 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -3670,21 +4850,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f2f12607f92c69b12ed746fabf9ca4f5c482cba46679c1a75b874ed7c26adb" dependencies = [ "futures-io", - "rustls 0.23.35", + "rustls 0.23.37", "rustls-pki-types", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-timer" @@ -3694,9 +4874,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -3706,7 +4886,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -3716,6 +4895,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" +[[package]] +name = "gcd" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" + [[package]] name = "generic-array" version = "0.14.7" @@ -3731,25 +4916,25 @@ dependencies = [ name = "genesis" version = "0.2.0" dependencies = [ - "bls", - "ethereum_hashing", - "ethereum_ssz", - "int_to_bytes", - "merkle_proof", + "bls 0.2.0", + "ethereum_hashing 0.8.0", + "ethereum_ssz 0.10.1", + "int_to_bytes 0.2.0", + "merkle_proof 0.2.0", "rayon", "state_processing", "tracing", - "tree_hash", - "types", + "tree_hash 0.12.1", + "types 0.2.1", ] [[package]] name = "getrandom" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "js-sys", "libc", "wasi", @@ -3762,14 +4947,39 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if 1.0.4", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "getset" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf0fc11e47561d47397154977bc219f4cf809b2974facc3ccb3b89e2436f912" +dependencies = [ + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "ghash" version = "0.5.1" @@ -3790,12 +5000,24 @@ checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" name = "graffiti_file" version = "0.1.0" dependencies = [ - "bls", + "bls 0.2.0", "hex", "serde", "tempfile", "tracing", - "types", + "types 0.2.1", +] + +[[package]] +name = "group" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfbfb3a6cfbd390d5c9564ab283a0349b9b9fcd46a706c1eb10e0db70bfbac7" +dependencies = [ + "ff 0.12.1", + "memuse", + "rand_core 0.6.4", + "subtle", ] [[package]] @@ -3804,13 +5026,46 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ - "ff", + "ff 0.13.1", "rand 0.8.5", "rand_core 0.6.4", "rand_xorshift 0.3.0", "subtle", ] +[[package]] +name = "guest" +version = "0.5.0" +source = "git+https://github.com/eth-act/ere-guests?tag=v0.6.0#64c94bb3da631101a6cb2f276c89392cb7c3426f" +dependencies = [ + "ere-io", + "ere-platform-trait", + "sha2", +] + +[[package]] +name = "guest_program" +version = "9.0.0" +source = "git+https://github.com/lambdaclass/ethrex.git?tag=v9.0.0#e88175e2d49f1192cc9f2fdeae6fde1392d0759d" +dependencies = [ + "bincode 1.3.3", + "bytes", + "ethrex-blockchain", + "ethrex-common", + "ethrex-crypto", + "ethrex-l2-common", + "ethrex-rlp", + "ethrex-storage", + "ethrex-trie", + "ethrex-vm", + "hex", + "rkyv", + "serde", + "serde_json", + "serde_with", + "thiserror 2.0.18", +] + [[package]] name = "h2" version = "0.3.27" @@ -3823,7 +5078,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.12.0", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -3832,17 +5087,17 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.3.1", - "indexmap 2.12.0", + "http 1.4.0", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -3855,11 +5110,34 @@ version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "crunchy", "zerocopy", ] +[[package]] +name = "halo2" +version = "0.1.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a23c779b38253fe1538102da44ad5bd5378495a61d2c4ee18d64eaa61ae5995" +dependencies = [ + "halo2_proofs", +] + +[[package]] +name = "halo2_proofs" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e925780549adee8364c7f2b685c753f6f3df23bde520c67416e93bf615933760" +dependencies = [ + "blake2b_simd", + "ff 0.12.1", + "group 0.12.1", + "pasta_curves 0.4.1", + "rand_core 0.6.4", + "rayon", +] + [[package]] name = "hash-db" version = "0.15.2" @@ -3904,12 +5182,15 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" dependencies = [ + "allocator-api2", + "equivalent", "foldhash 0.2.0", "serde", + "serde_core", ] [[package]] @@ -3960,13 +5241,28 @@ checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" dependencies = [ "base64 0.21.7", "bytes", - "headers-core", + "headers-core 0.2.0", "http 0.2.12", "httpdate", "mime", "sha1", ] +[[package]] +name = "headers" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3314d5adb5d94bcdf56771f2e50dbbc80bb4bdf88967526706205ac9eff24eb" +dependencies = [ + "base64 0.22.1", + "bytes", + "headers-core 0.3.0", + "http 1.4.0", + "httpdate", + "mime", + "sha1", +] + [[package]] name = "headers-core" version = "0.2.0" @@ -3976,13 +5272,22 @@ dependencies = [ "http 0.2.12", ] +[[package]] +name = "headers-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" +dependencies = [ + "http 1.4.0", +] + [[package]] name = "health_metrics" version = "0.1.0" dependencies = [ "eth2", - "metrics", - "procfs", + "metrics 0.2.0", + "procfs 0.18.0", "psutil", ] @@ -4006,13 +5311,19 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" dependencies = [ "arrayvec", ] +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hex_fmt" version = "0.3.0" @@ -4026,7 +5337,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8a6fe56c0038198998a6f217ca4e7ef3a5e51f46163bd6dd60b5c71ca6c6502" dependencies = [ "async-trait", - "cfg-if", + "cfg-if 1.0.4", "data-encoding", "enum-as-inner", "futures-channel", @@ -4038,7 +5349,7 @@ dependencies = [ "rand 0.9.2", "ring", "socket2 0.5.10", - "thiserror 2.0.17", + "thiserror 2.0.18", "tinyvec", "tokio", "tracing", @@ -4051,7 +5362,7 @@ version = "0.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc62a9a99b0bfb44d2ab95a7208ac952d31060efc16241c87eaf36406fecf87a" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "futures-util", "hickory-proto", "ipconfig", @@ -4061,7 +5372,7 @@ dependencies = [ "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", ] @@ -4106,12 +5417,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -4133,7 +5443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -4144,7 +5454,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "pin-project-lite", ] @@ -4155,7 +5465,7 @@ version = "0.1.0" dependencies = [ "beacon_chain", "beacon_processor", - "bls", + "bls 0.2.0", "bs58 0.4.0", "bytes", "context_deserialize", @@ -4163,9 +5473,9 @@ dependencies = [ "either", "eth2", "ethereum_serde_utils", - "ethereum_ssz", + "ethereum_ssz 0.10.1", "execution_layer", - "fixed_bytes", + "fixed_bytes 0.1.0", "futures", "genesis", "health_metrics", @@ -4175,7 +5485,7 @@ dependencies = [ "lighthouse_version", "logging", "lru 0.12.5", - "metrics", + "metrics 0.2.0", "network", "network_utils", "operation_pool", @@ -4195,8 +5505,8 @@ dependencies = [ "tokio", "tokio-stream", "tracing", - "tree_hash", - "types", + "tree_hash 0.12.1", + "types 0.2.1", "warp", "warp_utils", ] @@ -4211,7 +5521,7 @@ dependencies = [ "lighthouse_version", "logging", "malloc_utils", - "metrics", + "metrics 0.2.0", "network_utils", "reqwest", "serde", @@ -4219,7 +5529,7 @@ dependencies = [ "store", "tokio", "tracing", - "types", + "types 0.2.1", "warp", "warp_utils", ] @@ -4276,8 +5586,8 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2 0.4.12", - "http 1.3.1", + "h2 0.4.13", + "http 1.4.0", "http-body 1.0.1", "httparse", "httpdate", @@ -4295,10 +5605,11 @@ version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", + "http 1.4.0", "hyper 1.8.1", "hyper-util", - "rustls 0.23.35", + "rustls 0.23.37", + "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls 0.26.4", @@ -4337,33 +5648,34 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "hyper 1.8.1", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.1", + "socket2 0.6.3", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -4371,7 +5683,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.2", + "windows-core", ] [[package]] @@ -4431,9 +5743,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -4445,9 +5757,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -4464,6 +5776,12 @@ 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" @@ -4493,19 +5811,19 @@ dependencies = [ [[package]] name = "if-addrs" -version = "0.10.2" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a" +checksum = "c0a05c691e1fae256cf7013d99dad472dc52d5543322761f83ec8d47eab40d2b" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] name = "if-watch" -version = "3.2.1" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf9d64cfcf380606e64f9a0bcf493616b65331199f984151a6fa11a7b3cde38" +checksum = "71c02a5161c313f0cbdbadc511611893584a10a7b6153cb554bdf83ddce99ec2" dependencies = [ "async-io", "core-foundation 0.9.4", @@ -4534,7 +5852,7 @@ dependencies = [ "attohttpc", "bytes", "futures", - "http 1.3.1", + "http 1.4.0", "http-body-util", "hyper 1.8.1", "hyper-util", @@ -4554,6 +5872,33 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "impl-codec" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d40b9d5e17727407e55028eafc22b2dc68781786e6d7eb8a21103f5058e3a14" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-rlp" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ed8ad1f3877f7e775b8cbf30ed1bd3209a95401817f19a0eb4402d13f8cf90" +dependencies = [ + "rlp 0.6.1", +] + +[[package]] +name = "impl-serde" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a143eada6a1ec4aefa5049037a26a6d597bfd64f8c026d07b77133e02b7dd0b" +dependencies = [ + "serde", +] + [[package]] name = "impl-trait-for-tuples" version = "0.2.3" @@ -4562,7 +5907,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -4578,13 +5923,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.12.0" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.16.0", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -4594,12 +5939,12 @@ name = "initialized_validators" version = "0.1.0" dependencies = [ "account_utils", - "bincode", - "bls", + "bincode 1.3.3", + "bls 0.2.0", "eth2_keystore", "filesystem", "lockfile", - "metrics", + "metrics 0.2.0", "parking_lot", "rand 0.9.2", "reqwest", @@ -4608,7 +5953,7 @@ dependencies = [ "signing_method", "tokio", "tracing", - "types", + "types 0.2.1", "url", "validator_dir", "validator_metrics", @@ -4633,6 +5978,14 @@ dependencies = [ "yaml-rust2", ] +[[package]] +name = "int_to_bytes" +version = "0.2.0" +source = "git+https://github.com/sigp/lighthouse?branch=unstable#a965bfdf77a0b1a3cb2471b9df787edbe99779e8" +dependencies = [ + "bytes", +] + [[package]] name = "integer-sqrt" version = "0.1.5" @@ -4656,15 +6009,15 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ "memchr", "serde", @@ -4678,7 +6031,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4725,9 +6078,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "jobserver" @@ -4741,9 +6094,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -4764,13 +6117,27 @@ dependencies = [ "simple_asn1", ] +[[package]] +name = "jubjub" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a575df5f985fe1cd5b2b05664ff6accfc46559032b954529fd225a2168d27b0f" +dependencies = [ + "bitvec", + "bls12_381 0.7.1", + "ff 0.12.1", + "group 0.12.1", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "k256" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "ecdsa", "elliptic-curve", "once_cell", @@ -4781,18 +6148,18 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" dependencies = [ "cpufeatures", ] [[package]] name = "keccak-asm" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +checksum = "b646a74e746cd25045aa0fd42f4f7f78aa6d119380182c7e63a5593c4ab8df6f" dependencies = [ "digest 0.10.7", "sha3-asm", @@ -4804,7 +6171,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b286e6b663fb926e1eeb68528e69cb70ed46c6d65871a21b2215ae8154c6d3c" dependencies = [ - "primitive-types", + "primitive-types 0.12.2", "tiny-keccak", ] @@ -4816,17 +6183,79 @@ dependencies = [ "c-kzg", "criterion", "educe", - "ethereum_hashing", + "ethereum_hashing 0.8.0", + "ethereum_serde_utils", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "hex", + "rayon", + "rust_eth_kzg", + "serde", + "serde_json", + "tracing", + "tree_hash 0.12.1", +] + +[[package]] +name = "kzg" +version = "0.1.0" +source = "git+https://github.com/sigp/lighthouse?branch=unstable#a965bfdf77a0b1a3cb2471b9df787edbe99779e8" +dependencies = [ + "educe", + "ethereum_hashing 0.8.0", "ethereum_serde_utils", - "ethereum_ssz", - "ethereum_ssz_derive", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", "hex", "rayon", "rust_eth_kzg", "serde", "serde_json", "tracing", - "tree_hash", + "tree_hash 0.12.1", +] + +[[package]] +name = "kzg-rs" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8b4f55c3dedcfaa8668de1dfc8469e7a32d441c28edf225ed1f566fb32977d" +dependencies = [ + "ff 0.13.1", + "hex", + "serde_arrays", + "sha2", + "sp1_bls12_381", + "spin", +] + +[[package]] +name = "lambdaworks-crypto" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58b1a1c1102a5a7fbbda117b79fb3a01e033459c738a3c1642269603484fd1c1" +dependencies = [ + "lambdaworks-math", + "rand 0.8.5", + "rand_chacha 0.3.1", + "serde", + "sha2", + "sha3", +] + +[[package]] +name = "lambdaworks-math" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "018a95aa873eb49896a858dee0d925c33f3978d073c64b08dd4f2c9b35a017c6" +dependencies = [ + "getrandom 0.2.17", + "num-bigint 0.4.6", + "num-traits", + "rand 0.8.5", + "rayon", + "serde", + "serde_json", ] [[package]] @@ -4850,7 +6279,7 @@ version = "8.0.1" dependencies = [ "account_utils", "beacon_chain", - "bls", + "bls 0.2.0", "clap", "clap_utils", "deposit_contract", @@ -4858,10 +6287,10 @@ dependencies = [ "eth2", "eth2_network_config", "eth2_wallet", - "ethereum_hashing", - "ethereum_ssz", + "ethereum_hashing 0.8.0", + "ethereum_ssz 0.10.1", "execution_layer", - "fixed_bytes", + "fixed_bytes 0.1.0", "hex", "lighthouse_network", "lighthouse_version", @@ -4877,11 +6306,17 @@ dependencies = [ "store", "tracing", "tracing-subscriber", - "tree_hash", - "types", + "tree_hash 0.12.1", + "types 0.2.1", "validator_dir", ] +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "leveldb" version = "0.8.6" @@ -4907,9 +6342,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libloading" @@ -4917,15 +6352,15 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "windows-link", ] [[package]] name = "libm" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libmdbx" @@ -4952,7 +6387,7 @@ dependencies = [ "either", "futures", "futures-timer", - "getrandom 0.2.16", + "getrandom 0.2.17", "libp2p-allow-block-list", "libp2p-connection-limits", "libp2p-core", @@ -4971,7 +6406,7 @@ dependencies = [ "multiaddr", "pin-project", "rw-stream-sink", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -4998,9 +6433,9 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.43.1" +version = "0.43.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d28e2d2def7c344170f5c6450c0dbe3dfef655610dbfde2f6ac28a527abbe36" +checksum = "249128cd37a2199aff30a7675dffa51caf073b51aa612d2f544b19932b9aebca" dependencies = [ "either", "fnv", @@ -5015,7 +6450,7 @@ dependencies = [ "quick-protobuf", "rand 0.8.5", "rw-stream-sink", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "unsigned-varint 0.8.0", "web-time", @@ -5051,7 +6486,7 @@ dependencies = [ "fnv", "futures", "futures-timer", - "getrandom 0.2.16", + "getrandom 0.2.17", "hashlink 0.10.0", "hex_fmt", "libp2p-core", @@ -5084,15 +6519,15 @@ dependencies = [ "quick-protobuf", "quick-protobuf-codec", "smallvec", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] [[package]] name = "libp2p-identity" -version = "0.2.12" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3104e13b51e4711ff5738caa1fb54467c8604c2e94d607e27745bcf709068774" +checksum = "f0c7892c221730ba55f7196e98b0b8ba5e04b4155651736036628e9f73ed6fc3" dependencies = [ "asn1_der", "bs58 0.5.1", @@ -5103,7 +6538,7 @@ dependencies = [ "quick-protobuf", "rand 0.8.5", "sha2", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "zeroize", ] @@ -5179,7 +6614,7 @@ dependencies = [ "rand 0.8.5", "snow", "static_assertions", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "x25519-dalek", "zeroize", @@ -5216,27 +6651,27 @@ dependencies = [ "quinn", "rand 0.8.5", "ring", - "rustls 0.23.35", + "rustls 0.23.37", "socket2 0.5.10", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", ] [[package]] name = "libp2p-swarm" -version = "0.47.0" +version = "0.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aa762e5215919a34e31c35d4b18bf2e18566ecab7f8a3d39535f4a3068f8b62" +checksum = "ce88c6c4bf746c8482480345ea3edfd08301f49e026889d1cbccfa1808a9ed9e" dependencies = [ "either", "fnv", "futures", "futures-timer", + "hashlink 0.10.0", "libp2p-core", "libp2p-identity", "libp2p-swarm-derive", - "lru 0.12.5", "multistream-select", "rand 0.8.5", "smallvec", @@ -5253,21 +6688,21 @@ checksum = "dd297cf53f0cb3dee4d2620bb319ae47ef27c702684309f682bdb7e55a18ae9c" dependencies = [ "heck", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "libp2p-tcp" -version = "0.44.0" +version = "0.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65b4e030c52c46c8d01559b2b8ca9b7c4185f10576016853129ca1fe5cd1a644" +checksum = "fb6585b9309699f58704ec9ab0bb102eca7a3777170fa91a8678d73ca9cafa93" dependencies = [ "futures", "futures-timer", "if-watch", "libc", "libp2p-core", - "socket2 0.5.10", + "socket2 0.6.3", "tokio", "tracing", ] @@ -5284,9 +6719,9 @@ dependencies = [ "libp2p-identity", "rcgen", "ring", - "rustls 0.23.35", - "rustls-webpki 0.103.8", - "thiserror 2.0.17", + "rustls 0.23.37", + "rustls-webpki 0.103.9", + "thiserror 2.0.18", "x509-parser", "yasna", ] @@ -5315,19 +6750,18 @@ dependencies = [ "either", "futures", "libp2p-core", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "yamux 0.12.1", - "yamux 0.13.8", + "yamux 0.13.10", ] [[package]] name = "libredox" -version = "0.1.10" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" dependencies = [ - "bitflags 2.10.0", "libc", ] @@ -5343,19 +6777,22 @@ dependencies = [ ] [[package]] -name = "libz-rs-sys" -version = "0.5.4" +name = "libtest-mimic" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15413ef615ad868d4d65dce091cb233b229419c7c0c4bcaa746c0901c49ff39c" +checksum = "14e6ba06f0ade6e504aff834d7c34298e5155c6baca353cc6a4aaff2f9fd7f33" dependencies = [ - "zlib-rs", + "anstream", + "anstyle", + "clap", + "escape8259", ] [[package]] name = "libz-sys" -version = "1.1.23" +version = "1.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +checksum = "d52f4c29e2a68ac30c9087e1b772dc9f44a2b66ed44edf2266cf2be9b03dafc1" dependencies = [ "cc", "pkg-config", @@ -5371,7 +6808,7 @@ dependencies = [ "beacon_node", "beacon_node_fallback", "beacon_processor", - "bls", + "bls 0.2.0", "boot_node", "clap", "clap_utils", @@ -5381,7 +6818,7 @@ dependencies = [ "environment", "eth2", "eth2_network_config", - "ethereum_hashing", + "ethereum_hashing 0.8.0", "futures", "initialized_validators", "lighthouse_network", @@ -5389,7 +6826,7 @@ dependencies = [ "lighthouse_version", "logging", "malloc_utils", - "metrics", + "metrics 0.2.0", "network_utils", "opentelemetry", "opentelemetry-otlp", @@ -5406,7 +6843,7 @@ dependencies = [ "tracing", "tracing-opentelemetry", "tracing-subscriber", - "types", + "types 0.2.1", "validator_client", "validator_dir", "validator_manager", @@ -5420,7 +6857,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "async-channel 1.9.0", - "bls", + "bls 0.2.0", "bytes", "delay_map", "directory", @@ -5428,9 +6865,9 @@ dependencies = [ "discv5", "either", "eth2", - "ethereum_ssz", - "ethereum_ssz_derive", - "fixed_bytes", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "fixed_bytes 0.1.0", "fnv", "futures", "hex", @@ -5443,7 +6880,7 @@ dependencies = [ "logging", "lru 0.12.5", "lru_cache", - "metrics", + "metrics 0.2.0", "network_utils", "parking_lot", "prometheus-client", @@ -5454,7 +6891,7 @@ dependencies = [ "sha2", "smallvec", "snap", - "ssz_types", + "ssz_types 0.14.0", "strum", "superstruct", "task_executor", @@ -5464,7 +6901,7 @@ dependencies = [ "tracing", "tracing-subscriber", "typenum", - "types", + "types 0.2.1", "unsigned-varint 0.8.0", ] @@ -5478,7 +6915,7 @@ version = "0.1.0" dependencies = [ "account_utils", "beacon_node_fallback", - "bls", + "bls 0.2.0", "doppelganger_service", "either", "environment", @@ -5494,7 +6931,7 @@ dependencies = [ "task_executor", "tokio", "tracing", - "types", + "types 0.2.1", "validator_metrics", "validator_store", ] @@ -5514,9 +6951,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -5547,14 +6984,13 @@ dependencies = [ [[package]] name = "local-ip-address" -version = "0.6.5" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "656b3b27f8893f7bbf9485148ff9a65f019e3f33bd5cdc87c83cab16b3fd9ec8" +checksum = "79ef8c257c92ade496781a32a581d43e3d512cf8ce714ecf04ea80f93ed0ff4a" dependencies = [ "libc", "neli", - "thiserror 2.0.17", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5576,9 +7012,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "logging" @@ -5586,7 +7022,7 @@ version = "0.2.0" dependencies = [ "chrono", "logroller", - "metrics", + "metrics 0.2.0", "serde", "serde_json", "tokio", @@ -5621,11 +7057,11 @@ dependencies = [ [[package]] name = "lru" -version = "0.13.0" +version = "0.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" +checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" dependencies = [ - "hashbrown 0.15.5", + "hashbrown 0.16.1", ] [[package]] @@ -5659,35 +7095,81 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] -name = "malloc_utils" -version = "0.1.0" +name = "malachite" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec410515e231332b14cd986a475d1c3323bcfa4c7efc038bfa1d5b410b1c57e4" dependencies = [ - "libc", - "metrics", - "parking_lot", - "tikv-jemalloc-ctl", - "tikv-jemallocator", + "malachite-base", + "malachite-nz", + "malachite-q", ] [[package]] -name = "maplit" -version = "1.0.2" +name = "malachite-base" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" +checksum = "c738d3789301e957a8f7519318fcbb1b92bb95863b28f6938ae5a05be6259f34" +dependencies = [ + "hashbrown 0.15.5", + "itertools 0.14.0", + "libm", + "ryu", +] [[package]] -name = "match-lookup" -version = "0.1.1" +name = "malachite-nz" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" +checksum = "1707c9a1fa36ce21749b35972bfad17bbf34cf5a7c96897c0491da321e387d3b" +dependencies = [ + "itertools 0.14.0", + "libm", + "malachite-base", + "wide", +] + +[[package]] +name = "malachite-q" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d764801aa4e96bbb69b389dcd03b50075345131cd63ca2e380bca71cc37a3675" +dependencies = [ + "itertools 0.14.0", + "malachite-base", + "malachite-nz", +] + +[[package]] +name = "malloc_utils" +version = "0.1.0" +dependencies = [ + "libc", + "metrics 0.2.0", + "parking_lot", + "tikv-jemalloc-ctl", + "tikv-jemallocator", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "match-lookup" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] @@ -5711,6 +7193,18 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + [[package]] name = "mdbx-sys" version = "0.11.6-4" @@ -5730,9 +7224,18 @@ checksum = "33746aadcb41349ec291e7f2f0a3aa6834d1d7c58066fb4b01f68efc4c4b7631" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memoffset" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +dependencies = [ + "autocfg", +] [[package]] name = "memoffset" @@ -5743,45 +7246,109 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memuse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" + [[package]] name = "merkle_proof" version = "0.2.0" dependencies = [ "alloy-primitives", - "ethereum_hashing", - "fixed_bytes", + "ethereum_hashing 0.8.0", + "fixed_bytes 0.1.0", "proptest", "safe_arith", ] +[[package]] +name = "merkle_proof" +version = "0.2.0" +source = "git+https://github.com/sigp/lighthouse?branch=unstable#a965bfdf77a0b1a3cb2471b9df787edbe99779e8" +dependencies = [ + "alloy-primitives", + "ethereum_hashing 0.8.0", + "fixed_bytes 0.1.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", + "safe_arith", +] + [[package]] name = "metastruct" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d74f54f231f9a18d77393ecc5cc7ab96709b2a61ee326c2b2b291009b0cc5a07" +checksum = "969a1be9bd80794bdf93b23ab552c2ec6f3e83b33164824553fd996cdad513b8" dependencies = [ "metastruct_macro", ] [[package]] name = "metastruct_macro" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7225f3a4dfbec47a0c6a730a874185fda840d365d7bbd6ba199dd81796d5" +checksum = "de9164f767d73a507c19205868c84da411dc7795f4bdabf497d3dd93cfef9930" dependencies = [ - "darling 0.13.4", - "itertools 0.10.5", + "darling 0.23.0", + "itertools 0.14.0", "proc-macro2", "quote", "smallvec", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] name = "metrics" version = "0.2.0" dependencies = [ - "prometheus", + "prometheus 0.13.4", +] + +[[package]] +name = "metrics" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8" +dependencies = [ + "ahash", + "portable-atomic", +] + +[[package]] +name = "metrics-exporter-prometheus" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd7399781913e5393588a8d8c6a2867bf85fb38eaf2502fdce465aad2dc6f034" +dependencies = [ + "base64 0.22.1", + "http-body-util", + "hyper 1.8.1", + "hyper-rustls", + "hyper-util", + "indexmap 2.13.0", + "ipnet", + "metrics 0.24.3", + "metrics-util", + "quanta", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "metrics-util" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8496cc523d1f94c1385dd8f0f0c2c480b2b8aeccb5b7e4485ad6365523ae376" +dependencies = [ + "crossbeam-epoch 0.9.18", + "crossbeam-utils 0.8.21", + "hashbrown 0.15.5", + "metrics 0.24.3", + "quanta", + "rand 0.9.2", + "rand_xoshiro", + "sketches-ddsketch", ] [[package]] @@ -5794,15 +7361,15 @@ dependencies = [ "arbitrary", "context_deserialize", "educe", - "ethereum_hashing", - "ethereum_ssz", - "ethereum_ssz_derive", + "ethereum_hashing 0.8.0", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", "itertools 0.13.0", "parking_lot", "rayon", "serde", "smallvec", - "tree_hash", + "tree_hash 0.12.1", "triomphe", "typenum", "vec_map", @@ -5842,9 +7409,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi", @@ -5863,7 +7430,7 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "downcast", "fragile", "mockall_derive", @@ -5877,10 +7444,10 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -5889,28 +7456,29 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1ca96e5ac35256ae3e13536edd39b172b88f41615e1d7b653c8ad24524113e8" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "mockito" -version = "1.7.0" +version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7760e0e418d9b7e5777c0374009ca4c93861b9066f18cb334a20ce50ab63aa48" +checksum = "90820618712cab19cfc46b274c6c22546a82affcb3c3bdf0f29e3db8e1bb92c0" dependencies = [ "assert-json-diff", "bytes", "colored", - "futures-util", - "http 1.3.1", + "futures-core", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "hyper 1.8.1", "hyper-util", "log", + "pin-project-lite", "rand 0.9.2", "regex", "serde_json", @@ -5919,22 +7487,42 @@ dependencies = [ "tokio", ] +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "moka" -version = "0.12.11" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8261cd88c312e0004c1d51baad2980c66528dfdb2bee62003e643a4d8f86b077" +checksum = "85f8024e1c8e71c778968af91d43700ce1d11b219d127d79fb2934153b82b42b" dependencies = [ - "crossbeam-channel", - "crossbeam-epoch", - "crossbeam-utils", + "crossbeam-channel 0.5.15", + "crossbeam-epoch 0.9.18", + "crossbeam-utils 0.8.21", "equivalent", "parking_lot", "portable-atomic", - "rustc_version 0.4.1", "smallvec", "tagptr", - "uuid 1.18.1", + "uuid 1.22.0", ] [[package]] @@ -5944,7 +7532,7 @@ dependencies = [ "eth2", "health_metrics", "lighthouse_version", - "metrics", + "metrics 0.2.0", "regex", "reqwest", "sensitive_url", @@ -5962,6 +7550,17 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" +[[package]] +name = "mpt" +version = "0.1.0" +source = "git+https://github.com/eth-act/zkvm-ethereum-mpt.git?rev=a1e44638c49c4e16751a0b915593fce98ab6bdef#a1e44638c49c4e16751a0b915593fce98ab6bdef" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-trie 0.8.1", + "arrayvec", +] + [[package]] name = "multiaddr" version = "0.18.2" @@ -6017,11 +7616,31 @@ dependencies = [ "unsigned-varint 0.7.2", ] +[[package]] +name = "munge" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e17401f259eba956ca16491461b6e8f72913a0a114e39736ce404410f915a0c" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", @@ -6029,95 +7648,83 @@ dependencies = [ "openssl-probe", "openssl-sys", "schannel", - "security-framework 2.11.1", + "security-framework", "security-framework-sys", "tempfile", ] [[package]] name = "neli" -version = "0.6.5" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93062a0dce6da2517ea35f301dfc88184ce18d3601ec786a727a87bf535deca9" +checksum = "22f9786d56d972959e1408b6a93be6af13b9c1392036c5c1fafa08a1b0c6ee87" dependencies = [ + "bitflags 2.11.0", "byteorder", + "derive_builder", + "getset", "libc", "log", "neli-proc-macros", + "parking_lot", ] [[package]] name = "neli-proc-macros" -version = "0.1.4" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8034b7fbb6f9455b2a96c19e6edf8dc9fc34c70449938d8ee3b4df363f61fe" +checksum = "05d8d08c6e98f20a62417478ebf7be8e1425ec9acecc6f63e22da633f6b71609" dependencies = [ "either", "proc-macro2", "quote", "serde", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] name = "netlink-packet-core" -version = "0.7.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +checksum = "3463cbb78394cb0141e2c926b93fc2197e473394b761986eca3b9da2c63ae0f4" dependencies = [ - "anyhow", - "byteorder", - "netlink-packet-utils", + "paste", ] [[package]] name = "netlink-packet-route" -version = "0.17.1" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053998cea5a306971f88580d0829e90f270f940befd7cf928da179d4187a5a66" +checksum = "4ce3636fa715e988114552619582b530481fd5ef176a1e5c1bf024077c2c9445" dependencies = [ - "anyhow", - "bitflags 1.3.2", - "byteorder", + "bitflags 2.11.0", "libc", + "log", "netlink-packet-core", - "netlink-packet-utils", -] - -[[package]] -name = "netlink-packet-utils" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" -dependencies = [ - "anyhow", - "byteorder", - "paste", - "thiserror 1.0.69", ] [[package]] name = "netlink-proto" -version = "0.11.5" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72452e012c2f8d612410d89eea01e2d9b56205274abb35d53f60200b2ec41d60" +checksum = "b65d130ee111430e47eed7896ea43ca693c387f097dd97376bffafbf25812128" dependencies = [ "bytes", "futures", "log", "netlink-packet-core", "netlink-sys", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "netlink-sys" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" +checksum = "cd6c30ed10fa69cc491d491b85cc971f6bdeb8e7367b7cde2ee6cc878d583fae" dependencies = [ "bytes", - "futures", + "futures-util", "libc", "log", "tokio", @@ -6133,14 +7740,14 @@ dependencies = [ "async-channel 1.9.0", "beacon_chain", "beacon_processor", - "bls", + "bls 0.2.0", "delay_map", "educe", "eth2", "eth2_network_config", - "ethereum_ssz", + "ethereum_ssz 0.10.1", "execution_layer", - "fixed_bytes", + "fixed_bytes 0.1.0", "fnv", "futures", "genesis", @@ -6148,14 +7755,14 @@ dependencies = [ "igd-next", "itertools 0.10.5", "k256", - "kzg", + "kzg 0.1.0", "libp2p-gossipsub", "lighthouse_network", "lighthouse_tracing", "logging", "lru_cache", "matches", - "metrics", + "metrics 0.2.0", "operation_pool", "parking_lot", "rand 0.8.5", @@ -6165,7 +7772,7 @@ dependencies = [ "serde_json", "slot_clock", "smallvec", - "ssz_types", + "ssz_types 0.14.0", "store", "strum", "task_executor", @@ -6174,7 +7781,7 @@ dependencies = [ "tracing", "tracing-subscriber", "typenum", - "types", + "types 0.2.1", ] [[package]] @@ -6185,7 +7792,7 @@ dependencies = [ "hex", "libp2p-identity", "lru_cache", - "metrics", + "metrics 0.2.0", "multiaddr", "parking_lot", "serde", @@ -6199,29 +7806,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ "bitflags 1.3.2", - "cfg-if", + "cfg-if 1.0.4", "libc", ] [[package]] name = "nix" -version = "0.26.4" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 1.3.2", - "cfg-if", + "bitflags 2.11.0", + "cfg-if 1.0.4", + "cfg_aliases", "libc", ] [[package]] name = "nix" -version = "0.30.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" dependencies = [ - "bitflags 2.10.0", - "cfg-if", + "bitflags 2.11.0", + "cfg-if 1.0.4", "cfg_aliases", "libc", ] @@ -6232,23 +7840,28 @@ version = "0.2.0" dependencies = [ "beacon_node", "beacon_node_fallback", - "bls", + "bls 0.2.0", + "bytes", "environment", "eth2", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", "execution_layer", + "futures", "hex", - "mockito", "parking_lot", "reqwest", "sensitive_url", + "serde", "serde_json", - "ssz_types", + "ssz_types 0.14.0", "task_executor", "tempfile", "tokio", + "tokio-stream", "tracing", - "tree_hash", - "types", + "tree_hash 0.12.1", + "types 0.2.1", "validator_client", "validator_dir", "validator_store", @@ -6272,9 +7885,9 @@ dependencies = [ [[package]] name = "ntapi" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" dependencies = [ "winapi", ] @@ -6285,7 +7898,32 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint 0.4.6", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", ] [[package]] @@ -6315,11 +7953,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" [[package]] name = "num-integer" @@ -6341,6 +7988,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint 0.4.6", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -6363,9 +8021,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", "rustversion", @@ -6373,23 +8031,33 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "nybbles" -version = "0.4.6" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983bb634df7248924ee0c4c3a749609b5abcb082c28fffe3254b3eb3602b307" +dependencies = [ + "const-hex", + "smallvec", +] + +[[package]] +name = "nybbles" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4b5ecbd0beec843101bffe848217f770e8b8da81d8355b7d6e226f2199b3dc" +checksum = "0d49ff0c0d00d4a502b39df9af3a525e1efeb14b9dabb5bb83335284c1309210" dependencies = [ "alloy-rlp", - "cfg-if", + "cfg-if 1.0.4", "proptest", "ruint", "serde", @@ -6398,9 +8066,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ "objc2-encode", ] @@ -6422,9 +8090,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" dependencies = [ "critical-section", "portable-atomic", @@ -6449,6 +8117,23 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "op-alloy-consensus" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736381a95471d23e267263cfcee9e1d96d30b9754a94a2819148f83379de8a86" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "derive_more 2.1.1", + "serde", + "serde_with", + "thiserror 2.0.18", +] + [[package]] name = "opaque-debug" version = "0.3.1" @@ -6457,12 +8142,12 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.75" +version = "0.10.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" dependencies = [ - "bitflags 2.10.0", - "cfg-if", + "bitflags 2.11.0", + "cfg-if 1.0.4", "foreign-types", "libc", "once_cell", @@ -6478,29 +8163,29 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "openssl-probe" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-src" -version = "300.5.4+3.5.4" +version = "300.5.5+3.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" +checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" dependencies = [ "cc", "libc", @@ -6519,7 +8204,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", ] @@ -6531,7 +8216,7 @@ checksum = "50f6639e842a97dbea8886e3439710ae463120091e2e064518ba8e716e6ac36d" dependencies = [ "async-trait", "bytes", - "http 1.3.1", + "http 1.4.0", "opentelemetry", "reqwest", ] @@ -6542,14 +8227,14 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbee664a43e07615731afc539ca60c6d9f1a9425e25ca09c57bc36c87c55852b" dependencies = [ - "http 1.3.1", + "http 1.4.0", "opentelemetry", "opentelemetry-http", "opentelemetry-proto", "opentelemetry_sdk", "prost", "reqwest", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tonic 0.13.1", "tracing", @@ -6580,7 +8265,7 @@ dependencies = [ "percent-encoding", "rand 0.9.2", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -6589,14 +8274,14 @@ version = "0.2.0" dependencies = [ "beacon_chain", "bitvec", - "bls", + "bls 0.2.0", "educe", - "ethereum_ssz", - "ethereum_ssz_derive", - "fixed_bytes", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "fixed_bytes 0.1.0", "itertools 0.10.5", "maplit", - "metrics", + "metrics 0.2.0", "parking_lot", "rand 0.9.2", "rayon", @@ -6606,16 +8291,178 @@ dependencies = [ "superstruct", "tokio", "typenum", - "types", + "types 0.2.1", ] [[package]] -name = "pairing" -version = "0.23.0" +name = "p256" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" dependencies = [ - "group", + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p3-bn254-fr" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9abf208fbfe540d6e2a6caaa2a9a345b1c8cb23ffdcdfcc6987244525d4fc821" +dependencies = [ + "ff 0.13.1", + "num-bigint 0.4.6", + "p3-field", + "p3-poseidon2", + "p3-symmetric", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "p3-challenger" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42b725b453bbb35117a1abf0ddfd900b0676063d6e4231e0fa6bb0d76018d8ad" +dependencies = [ + "p3-field", + "p3-maybe-rayon", + "p3-symmetric", + "p3-util", + "serde", + "tracing", +] + +[[package]] +name = "p3-dft" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56a1f81101bff744b7ebba7f4497e917a2c6716d6e62736e4a56e555a2d98cb7" +dependencies = [ + "p3-field", + "p3-matrix", + "p3-maybe-rayon", + "p3-util", + "tracing", +] + +[[package]] +name = "p3-field" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36459d4acb03d08097d713f336c7393990bb489ab19920d4f68658c7a5c10968" +dependencies = [ + "itertools 0.12.1", + "num-bigint 0.4.6", + "num-traits", + "p3-util", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "p3-koala-bear" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1f52bcb6be38bdc8fa6b38b3434d4eedd511f361d4249fd798c6a5ef817b40" +dependencies = [ + "num-bigint 0.4.6", + "p3-field", + "p3-mds", + "p3-poseidon2", + "p3-symmetric", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "p3-matrix" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5583e9cd136a4095a25c41a9edfdcce2dfae58ef01639317813bdbbd5b55c583" +dependencies = [ + "itertools 0.12.1", + "p3-field", + "p3-maybe-rayon", + "p3-util", + "rand 0.8.5", + "serde", + "tracing", +] + +[[package]] +name = "p3-maybe-rayon" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e524d47a49fb4265611303339c4ef970d892817b006cc330dad18afb91e411b1" + +[[package]] +name = "p3-mds" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f6cb8edcb276033d43769a3725570c340d2ed6f35c3cca4cddeee07718fa376" +dependencies = [ + "itertools 0.12.1", + "p3-dft", + "p3-field", + "p3-matrix", + "p3-symmetric", + "p3-util", + "rand 0.8.5", +] + +[[package]] +name = "p3-poseidon2" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a26197df2097b98ab7038d59a01e1fe1a0f545e7e04aa9436b2454b1836654f" +dependencies = [ + "gcd", + "p3-field", + "p3-mds", + "p3-symmetric", + "rand 0.8.5", + "serde", +] + +[[package]] +name = "p3-symmetric" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a1d3b5202096bca57cde912fbbb9cbaedaf5ac7c42a924c7166b98709d64d21" +dependencies = [ + "itertools 0.12.1", + "p3-field", + "serde", +] + +[[package]] +name = "p3-util" +version = "0.3.2-succinct" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec5f0388aa6d935ca3a17444086120f393f0b2f0816010b5ff95998c1c4095e3" +dependencies = [ + "serde", +] + +[[package]] +name = "pairing" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135590d8bdba2b31346f9cd1fb2a912329f5135e832a4f422942eb6ead8b6b3b" +dependencies = [ + "group 0.12.1", +] + +[[package]] +name = "pairing" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81fec4625e73cf41ef4bb6846cafa6d44736525f442ba45e407c4a000a13996f" +dependencies = [ + "group 0.13.0", ] [[package]] @@ -6643,7 +8490,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -6668,13 +8515,43 @@ version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "libc", "redox_syscall", "smallvec", "windows-link", ] +[[package]] +name = "pasta_curves" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc65faf8e7313b4b1fbaa9f7ca917a0eed499a9663be71477f87993604341d8" +dependencies = [ + "blake2b_simd", + "ff 0.12.1", + "group 0.12.1", + "lazy_static", + "rand 0.8.5", + "static_assertions", + "subtle", +] + +[[package]] +name = "pasta_curves" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e57598f73cc7e1b2ac63c79c517b31a0877cd7c402cdcaa311b5208de7a095" +dependencies = [ + "blake2b_simd", + "ff 0.13.1", + "group 0.13.0", + "lazy_static", + "rand 0.8.5", + "static_assertions", + "subtle", +] + [[package]] name = "paste" version = "1.0.15" @@ -6701,6 +8578,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -6709,39 +8595,82 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.3" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", "ucd-trie", ] +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pin-utils" @@ -6805,11 +8734,11 @@ version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.1.2", + "rustix 1.1.4", "windows-sys 0.61.2", ] @@ -6830,7 +8759,7 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "cpufeatures", "opaque-debug", "universal-hash", @@ -6838,9 +8767,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.11.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "potential_utf" @@ -6868,9 +8797,9 @@ dependencies = [ [[package]] name = "predicates" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe" dependencies = [ "anstyle", "predicates-core", @@ -6878,15 +8807,15 @@ dependencies = [ [[package]] name = "predicates-core" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" +checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144" [[package]] name = "predicates-tree" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2" dependencies = [ "predicates-core", "termtree", @@ -6907,7 +8836,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.110", + "syn 2.0.117", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", ] [[package]] @@ -6917,17 +8855,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", - "impl-codec", + "impl-codec 0.6.0", "uint 0.9.5", ] +[[package]] +name = "primitive-types" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15600a7d856470b7d278b3fe0e311fe28c2526348549f8ef2ff7db3299c87f5" +dependencies = [ + "fixed-hash", + "impl-codec 0.7.1", + "impl-rlp", + "impl-serde", + "uint 0.10.0", +] + [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit", + "toml_edit 0.25.5+spec-1.1.0", ] [[package]] @@ -6949,27 +8900,50 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] +[[package]] +name = "procfs" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "731e0d9356b0c25f16f33b5be79b1c57b562f141ebfcdb0ad8ac2c13a24293b4" +dependencies = [ + "bitflags 2.11.0", + "hex", + "lazy_static", + "procfs-core 0.16.0", + "rustix 0.38.44", +] + [[package]] name = "procfs" version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" dependencies = [ - "bitflags 2.10.0", - "procfs-core", - "rustix 1.1.2", + "bitflags 2.11.0", + "procfs-core 0.18.0", + "rustix 1.1.4", +] + +[[package]] +name = "procfs-core" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" +dependencies = [ + "bitflags 2.11.0", + "hex", ] [[package]] @@ -6978,7 +8952,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "hex", ] @@ -6988,14 +8962,32 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "fnv", "lazy_static", + "libc", "memchr", "parking_lot", + "procfs 0.16.0", + "protobuf 2.28.0", "thiserror 1.0.69", ] +[[package]] +name = "prometheus" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ca5326d8d0b950a9acd87e6a3f94745394f62e4dae1b1ee22b2bc0c394af43a" +dependencies = [ + "cfg-if 1.0.4", + "fnv", + "lazy_static", + "memchr", + "parking_lot", + "protobuf 3.7.2", + "thiserror 2.0.18", +] + [[package]] name = "prometheus-client" version = "0.23.1" @@ -7016,7 +9008,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -7030,15 +9022,40 @@ dependencies = [ "tracing", ] +[[package]] +name = "proof_engine_zkboost_test" +version = "0.1.0" +dependencies = [ + "anyhow", + "axum 0.7.9", + "bytes", + "execution_layer", + "futures", + "metrics-exporter-prometheus", + "reqwest", + "sensitive_url", + "serde", + "serde_json", + "strum", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", + "types 0.2.1", + "url", + "zkboost-server", + "zkboost-types", +] + [[package]] name = "proptest" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +checksum = "37566cb3fdacef14c0737f9546df7cfeadbfbc9fef10991038bf5015d0c80532" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.10.0", + "bitflags 2.11.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -7051,13 +9068,13 @@ dependencies = [ [[package]] name = "proptest-derive" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" +checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -7080,7 +9097,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -7096,14 +9113,40 @@ dependencies = [ name = "proto_array" version = "0.2.0" dependencies = [ - "ethereum_ssz", - "ethereum_ssz_derive", - "fixed_bytes", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "fixed_bytes 0.1.0", "safe_arith", "serde", "serde_yaml", "superstruct", - "types", + "types 0.2.1", +] + +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + +[[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]] @@ -7112,7 +9155,7 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e617cc9058daa5e1fe5a0d23ed745773a5ee354111dad1ec0235b0cc16b6730" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "darwin-libproc", "derive_more 0.99.20", "glob", @@ -7126,37 +9169,80 @@ dependencies = [ ] [[package]] -name = "quick-error" -version = "1.2.3" +name = "ptr_meta" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quick-protobuf" -version = "0.8.1" -source = "git+https://github.com/sigp/quick-protobuf.git?rev=681f413312404ab6e51f0b46f39b0075c6f4ebfd#681f413312404ab6e51f0b46f39b0075c6f4ebfd" +checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79" dependencies = [ - "byteorder", + "ptr_meta_derive", ] [[package]] -name = "quick-protobuf-codec" +name = "ptr_meta_derive" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a0580ab32b169745d7a39db2ba969226ca16738931be152a3209b409de2474" +checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" dependencies = [ - "asynchronous-codec", - "bytes", - "quick-protobuf", - "thiserror 1.0.69", - "unsigned-varint 0.8.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "qfilter" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "746341cd2357c9a4df2d951522b4a8dd1ef553e543119899ad7bf87e938c8fbe" +dependencies = [ + "xxhash-rust", +] + +[[package]] +name = "quanta" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3ab5a9d756f0d97bdc89019bd2e4ea098cf9cde50ee7564dde6b81ccc8f06c7" +dependencies = [ + "crossbeam-utils 0.8.21", + "libc", + "once_cell", + "raw-cpuid", + "wasi", + "web-sys", + "winapi", +] + +[[package]] +name = "quick-error" +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 = "git+https://github.com/sigp/quick-protobuf.git?rev=681f413312404ab6e51f0b46f39b0075c6f4ebfd#681f413312404ab6e51f0b46f39b0075c6f4ebfd" +dependencies = [ + "byteorder", +] + +[[package]] +name = "quick-protobuf-codec" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15a0580ab32b169745d7a39db2ba969226ca16738931be152a3209b409de2474" +dependencies = [ + "asynchronous-codec", + "bytes", + "quick-protobuf", + "thiserror 1.0.69", + "unsigned-varint 0.8.0", ] [[package]] name = "quinn" version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +source = "git+https://github.com/sigp/quinn?rev=59af87979c8411864c1cb68613222f54ed2930a7#59af87979c8411864c1cb68613222f54ed2930a7" dependencies = [ "bytes", "cfg_aliases", @@ -7165,9 +9251,9 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.1.1", - "rustls 0.23.35", - "socket2 0.6.1", - "thiserror 2.0.17", + "rustls 0.23.37", + "socket2 0.6.3", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -7176,8 +9262,7 @@ dependencies = [ [[package]] name = "quinn-proto" version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +source = "git+https://github.com/sigp/quinn?rev=59af87979c8411864c1cb68613222f54ed2930a7#59af87979c8411864c1cb68613222f54ed2930a7" dependencies = [ "bytes", "getrandom 0.3.4", @@ -7185,10 +9270,10 @@ dependencies = [ "rand 0.9.2", "ring", "rustc-hash 2.1.1", - "rustls 0.23.35", + "rustls 0.23.37", "rustls-pki-types", "slab", - "thiserror 2.0.17", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -7197,22 +9282,21 @@ dependencies = [ [[package]] name = "quinn-udp" version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +source = "git+https://github.com/sigp/quinn?rev=59af87979c8411864c1cb68613222f54ed2930a7#59af87979c8411864c1cb68613222f54ed2930a7" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.1", + "socket2 0.6.3", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -7223,6 +9307,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "r2d2" version = "0.8.10" @@ -7250,6 +9340,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rancor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a063ea72381527c2a0561da9c80000ef822bdd7c3241b1cc1b12100e3df081ee" +dependencies = [ + "ptr_meta", +] + [[package]] name = "rand" version = "0.8.5" @@ -7269,7 +9368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", - "rand_core 0.9.3", + "rand_core 0.9.5", "serde", ] @@ -7290,7 +9389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.3", + "rand_core 0.9.5", ] [[package]] @@ -7299,14 +9398,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", ] [[package]] name = "rand_core" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ "getrandom 0.3.4", "serde", @@ -7327,7 +9426,34 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" dependencies = [ - "rand_core 0.9.3", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_xoshiro" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f703f4665700daf5512dcca5f43afa6af89f09db47fb56be587f80636bda2d41" +dependencies = [ + "rand_core 0.9.5", +] + +[[package]] +name = "rapidhash" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" +dependencies = [ + "rustversion", +] + +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.11.0", ] [[package]] @@ -7346,8 +9472,8 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ - "crossbeam-deque", - "crossbeam-utils", + "crossbeam-deque 0.8.6", + "crossbeam-utils 0.8.21", ] [[package]] @@ -7378,7 +9504,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -7387,7 +9513,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "libredox", "thiserror 1.0.69", ] @@ -7398,115 +9524,715 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ - "ref-cast-impl", + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rend" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadadef317c2f20755a64d7fdc48f9e7178ee6b0e1f7fce33fa60f1d68a276e6" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.4.13", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.8.1", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.37", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-rustls 0.26.4", + "tokio-util", + "tower 0.5.3", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "reqwest-eventsource" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632c55746dbb44275691640e7b40c907c16a2dc1a5842aa98aaec90da6ec6bde" +dependencies = [ + "eventsource-stream", + "futures-core", + "futures-timer", + "mime", + "nom", + "pin-project-lite", + "reqwest", + "thiserror 1.0.69", +] + +[[package]] +name = "resolv-conf" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" + +[[package]] +name = "reth-chainspec" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-genesis", + "alloy-primitives", + "alloy-trie 0.9.5", + "auto_impl", + "derive_more 2.1.1", + "reth-ethereum-forks", + "reth-network-peers", + "reth-primitives-traits", + "serde_json", +] + +[[package]] +name = "reth-codecs" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-trie 0.9.5", + "bytes", + "modular-bitfield", + "op-alloy-consensus", + "reth-codecs-derive", + "reth-zstd-compressors", + "serde", +] + +[[package]] +name = "reth-codecs-derive" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "reth-consensus" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "auto_impl", + "reth-execution-types", + "reth-primitives-traits", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-consensus-common" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "reth-chainspec", + "reth-consensus", + "reth-primitives-traits", +] + +[[package]] +name = "reth-db-models" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "reth-primitives-traits", +] + +[[package]] +name = "reth-errors" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "reth-consensus", + "reth-execution-errors", + "reth-storage-errors", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-ethereum-consensus" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "reth-chainspec", + "reth-consensus", + "reth-consensus-common", + "reth-execution-types", + "reth-primitives-traits", + "tracing", +] + +[[package]] +name = "reth-ethereum-forks" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-eip2124", + "alloy-hardforks", + "alloy-primitives", + "auto_impl", + "once_cell", +] + +[[package]] +name = "reth-ethereum-primitives" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-serde", + "reth-codecs", + "reth-primitives-traits", + "serde", + "serde_with", +] + +[[package]] +name = "reth-evm" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "auto_impl", + "derive_more 2.1.1", + "futures-util", + "reth-execution-errors", + "reth-execution-types", + "reth-primitives-traits", + "reth-storage-api", + "reth-storage-errors", + "reth-trie-common", + "revm", +] + +[[package]] +name = "reth-evm-ethereum" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "alloy-rpc-types-engine", + "reth-chainspec", + "reth-ethereum-forks", + "reth-ethereum-primitives", + "reth-evm", + "reth-execution-types", + "reth-primitives-traits", + "reth-storage-errors", + "revm", +] + +[[package]] +name = "reth-execution-errors" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-evm", + "alloy-primitives", + "alloy-rlp", + "nybbles 0.4.8", + "reth-storage-errors", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-execution-types" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-evm", + "alloy-primitives", + "derive_more 2.1.1", + "reth-ethereum-primitives", + "reth-primitives-traits", + "reth-trie-common", + "revm", +] + +[[package]] +name = "reth-network-peers" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde_with", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "reth-payload-validator" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-rpc-types-engine", + "reth-primitives-traits", +] + +[[package]] +name = "reth-primitives-traits" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-eth", + "alloy-trie 0.9.5", + "auto_impl", + "bytes", + "derive_more 2.1.1", + "once_cell", + "op-alloy-consensus", + "reth-codecs", + "revm-bytecode", + "revm-primitives", + "revm-state", + "secp256k1", + "serde", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-prune-types" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-primitives", + "derive_more 2.1.1", + "strum", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-revm" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-primitives", + "reth-primitives-traits", + "reth-storage-api", + "reth-storage-errors", + "revm", +] + +[[package]] +name = "reth-stages-types" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-primitives", + "reth-trie-common", +] + +[[package]] +name = "reth-stateless" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-debug", + "alloy-trie 0.9.5", + "itertools 0.14.0", + "k256", + "reth-chainspec", + "reth-consensus", + "reth-errors", + "reth-ethereum-consensus", + "reth-ethereum-primitives", + "reth-evm", + "reth-primitives-traits", + "reth-revm", + "reth-trie-common", + "reth-trie-sparse", + "serde", + "serde_with", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-static-file-types" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-primitives", + "derive_more 2.1.1", + "fixed-map", + "serde", + "strum", +] + +[[package]] +name = "reth-storage-api" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "auto_impl", + "reth-chainspec", + "reth-db-models", + "reth-ethereum-primitives", + "reth-execution-types", + "reth-primitives-traits", + "reth-prune-types", + "reth-stages-types", + "reth-storage-errors", + "reth-trie-common", + "revm-database", +] + +[[package]] +name = "reth-storage-errors" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "derive_more 2.1.1", + "reth-primitives-traits", + "reth-prune-types", + "reth-static-file-types", + "revm-database-interface", + "revm-state", + "thiserror 2.0.18", +] + +[[package]] +name = "reth-trie-common" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rlp", + "alloy-trie 0.9.5", + "derive_more 2.1.1", + "itertools 0.14.0", + "nybbles 0.4.8", + "reth-primitives-traits", + "revm-database", +] + +[[package]] +name = "reth-trie-sparse" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-trie 0.9.5", + "auto_impl", + "reth-execution-errors", + "reth-primitives-traits", + "reth-trie-common", + "smallvec", + "tracing", +] + +[[package]] +name = "reth-zstd-compressors" +version = "1.10.2" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.10.2#8e3b5e6a99439561b73c5dd31bd3eced2e994d60" +dependencies = [ + "zstd", +] + +[[package]] +name = "revm" +version = "34.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2aabdebaa535b3575231a88d72b642897ae8106cf6b0d12eafc6bfdf50abfc7" +dependencies = [ + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database", + "revm-database-interface", + "revm-handler", + "revm-inspector", + "revm-interpreter", + "revm-precompile", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-bytecode" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d1e5c1eaa44d39d537f668bc5c3409dc01e5c8be954da6c83370bbdf006457" +dependencies = [ + "bitvec", + "phf", + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-context" +version = "13.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "892ff3e6a566cf8d72ffb627fdced3becebbd9ba64089c25975b9b028af326a5" +dependencies = [ + "bitvec", + "cfg-if 1.0.4", + "derive-where", + "revm-bytecode", + "revm-context-interface", + "revm-database-interface", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-context-interface" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57f61cc6d23678c4840af895b19f8acfbbd546142ec8028b6526c53cc1c16c98" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "auto_impl", + "either", + "revm-database-interface", + "revm-primitives", + "revm-state", +] + +[[package]] +name = "revm-database" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529528d0b05fe646be86223032c3e77aa8b05caa2a35447d538c55965956a511" +dependencies = [ + "revm-bytecode", + "revm-database-interface", + "revm-primitives", + "revm-state", ] [[package]] -name = "ref-cast-impl" -version = "1.0.25" +name = "revm-database-interface" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "b7bf93ac5b91347c057610c0d96e923db8c62807e03f036762d03e981feddc1d" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.110", + "auto_impl", + "either", + "revm-primitives", + "revm-state", + "thiserror 2.0.18", ] [[package]] -name = "regex" -version = "1.12.2" +name = "revm-handler" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "0cd0e43e815a85eded249df886c4badec869195e70cdd808a13cfca2794622d2" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", + "auto_impl", + "derive-where", + "revm-bytecode", + "revm-context", + "revm-context-interface", + "revm-database-interface", + "revm-interpreter", + "revm-precompile", + "revm-primitives", + "revm-state", ] [[package]] -name = "regex-automata" -version = "0.4.13" +name = "revm-inspector" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "4f3ccad59db91ef93696536a0dbaf2f6f17cfe20d4d8843ae118edb7e97947ef" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "auto_impl", + "either", + "revm-context", + "revm-database-interface", + "revm-handler", + "revm-interpreter", + "revm-primitives", + "revm-state", ] [[package]] -name = "regex-syntax" -version = "0.8.8" +name = "revm-interpreter" +version = "32.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "11406408597bc249392d39295831c4b641b3a6f5c471a7c41104a7a1e3564c07" +dependencies = [ + "revm-bytecode", + "revm-context-interface", + "revm-primitives", + "revm-state", +] [[package]] -name = "reqwest" -version = "0.12.24" +name = "revm-precompile" +version = "32.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +checksum = "e2ec11f45deec71e4945e1809736bb20d454285f9167ab53c5159dae1deb603f" dependencies = [ - "base64 0.22.1", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "http-body-util", - "hyper 1.8.1", - "hyper-rustls", - "hyper-tls", - "hyper-util", - "js-sys", - "log", - "native-tls", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls 0.23.35", - "rustls-pki-types", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "tokio", - "tokio-native-tls", - "tokio-rustls 0.26.4", - "tokio-util", - "tower 0.5.2", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "wasm-streams", - "web-sys", - "webpki-roots", + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff 0.5.0", + "ark-serialize 0.5.0", + "arrayref", + "aurora-engine-modexp", + "cfg-if 1.0.4", + "k256", + "p256", + "revm-primitives", + "ripemd", + "sha2", ] [[package]] -name = "reqwest-eventsource" -version = "0.6.0" +name = "revm-primitives" +version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632c55746dbb44275691640e7b40c907c16a2dc1a5842aa98aaec90da6ec6bde" +checksum = "4bcfb5ce6cf18b118932bcdb7da05cd9c250f2cb9f64131396b55f3fe3537c35" dependencies = [ - "eventsource-stream", - "futures-core", - "futures-timer", - "mime", - "nom", - "pin-project-lite", - "reqwest", - "thiserror 1.0.69", + "alloy-primitives", + "num_enum", + "once_cell", + "serde", ] [[package]] -name = "resolv-conf" -version = "0.7.6" +name = "revm-state" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e061d1b48cb8d38042de4ae0a7a6401009d6143dc80d2e2d6f31f0bdd6470c7" +checksum = "311720d4f0f239b041375e7ddafdbd20032a33b7bae718562ea188e188ed9fd3" +dependencies = [ + "alloy-eip7928", + "bitflags 2.11.0", + "revm-bytecode", + "revm-primitives", + "serde", +] [[package]] name = "rfc6979" @@ -7525,13 +10251,52 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", - "cfg-if", - "getrandom 0.2.16", + "cfg-if 1.0.4", + "getrandom 0.2.17", "libc", "untrusted", "windows-sys 0.52.0", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "rkyv" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a30e631b7f4a03dee9056b8ef6982e8ba371dd5bedb74d3ec86df4499132c70" +dependencies = [ + "bytecheck", + "bytes", + "hashbrown 0.16.1", + "indexmap 2.13.0", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", + "uuid 1.22.0", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8100bb34c0a1d0f907143db3149e6b4eea3c33b9ee8b189720168e818303986f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "rlp" version = "0.5.2" @@ -7542,6 +10307,16 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rlp" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa24e92bb2a83198bb76d661a71df9f7076b8c420b8696e4d3d97d50d94479e3" +dependencies = [ + "bytes", + "rustc-hex", +] + [[package]] name = "rpassword" version = "5.0.1" @@ -7563,18 +10338,18 @@ dependencies = [ [[package]] name = "rtnetlink" -version = "0.13.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a552eb82d19f38c3beed3f786bd23aa434ceb9ac43ab44419ca6d67a7e186c0" +checksum = "4b960d5d873a75b5be9761b1e73b146f52dddcd27bac75263f40fba686d4d7b5" dependencies = [ - "futures", + "futures-channel", + "futures-util", "log", "netlink-packet-core", "netlink-packet-route", - "netlink-packet-utils", "netlink-proto", "netlink-sys", - "nix 0.26.4", + "nix 0.30.1", "thiserror 1.0.69", "tokio", ] @@ -7593,15 +10368,15 @@ dependencies = [ "bytes", "fastrlp 0.3.1", "fastrlp 0.4.0", - "num-bigint", + "num-bigint 0.4.6", "num-integer", "num-traits", "parity-scale-codec", - "primitive-types", + "primitive-types 0.12.2", "proptest", "rand 0.8.5", "rand 0.9.2", - "rlp", + "rlp 0.5.2", "ruint-macro", "serde_core", "valuable", @@ -7696,24 +10471,24 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys 0.4.15", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", - "linux-raw-sys 0.11.0", - "windows-sys 0.52.0", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", ] [[package]] @@ -7732,29 +10507,30 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.35" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.8", + "rustls-webpki 0.103.9", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.5.1", + "security-framework", ] [[package]] @@ -7768,9 +10544,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ "web-time", "zeroize", @@ -7789,10 +10565,11 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.8" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -7829,9 +10606,18 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.20" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "safe_arch" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323" +dependencies = [ + "bytemuck", +] [[package]] name = "safe_arith" @@ -7859,9 +10645,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -7889,9 +10675,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9558e172d4e8533736ba97870c4b2cd63f84b382a3d6eb063da41b91cce17289" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -7960,24 +10746,11 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework" -version = "3.5.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -7986,9 +10759,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -8052,6 +10825,15 @@ dependencies = [ "serde_urlencoded", ] +[[package]] +name = "serde_arrays" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a16b99c5ea4fe3daccd14853ad260ec00ea043b2708d1fd1da3106dcd8d9df" +dependencies = [ + "serde", +] + [[package]] name = "serde_core" version = "1.0.228" @@ -8069,18 +10851,29 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", - "ryu", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457" +dependencies = [ + "itoa", "serde", "serde_core", ] @@ -8093,7 +10886,16 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", +] + +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", ] [[package]] @@ -8110,17 +10912,17 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10574371d41b0d9b2cff89418eda27da52bcaff2cc8741db26382a77c29131f1" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.12.0", + "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.1.0", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -8129,14 +10931,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.0" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a72d8216842fdd57820dc78d840bef99248e35fb2554ff923319e60f2d686b" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ - "darling 0.21.3", + "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -8145,7 +10947,7 @@ version = "0.9.34+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ - "indexmap 2.12.0", + "indexmap 2.13.0", "itoa", "ryu", "serde", @@ -8168,7 +10970,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "cpufeatures", "digest 0.10.7", ] @@ -8179,7 +10981,7 @@ version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "cpufeatures", "digest 0.10.7", ] @@ -8196,12 +10998,12 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +checksum = "b31139435f327c93c6038ed350ae4588e2c70a13d50599509fee6349967ba35a" dependencies = [ "cc", - "cfg-if", + "cfg-if 1.0.4", ] [[package]] @@ -8221,10 +11023,11 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.6" +version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ + "errno", "libc", ] @@ -8242,7 +11045,7 @@ dependencies = [ name = "signing_method" version = "0.1.0" dependencies = [ - "bls", + "bls 0.2.0", "eth2_keystore", "ethereum_serde_utils", "lockfile", @@ -8251,16 +11054,22 @@ dependencies = [ "serde", "task_executor", "tracing", - "types", + "types 0.2.1", "url", "validator_metrics", ] [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "simdutf8" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" [[package]] name = "similar" @@ -8270,13 +11079,13 @@ checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" [[package]] name = "simple_asn1" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" dependencies = [ - "num-bigint", + "num-bigint 0.4.6", "num-traits", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", ] @@ -8291,7 +11100,7 @@ dependencies = [ "eth2", "execution_layer", "futures", - "kzg", + "kzg 0.1.0", "lighthouse_network", "logging", "node_test_rig", @@ -8305,96 +11114,190 @@ dependencies = [ "tracing", "tracing-subscriber", "typenum", - "types", + "types 0.2.1", "validator_http_api", ] +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + +[[package]] +name = "sketches-ddsketch" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" + [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "slasher" version = "0.1.0" dependencies = [ - "bincode", - "bls", + "bincode 1.3.3", + "bls 0.2.0", "byteorder", "educe", - "ethereum_ssz", - "ethereum_ssz_derive", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", "filesystem", - "fixed_bytes", + "fixed_bytes 0.1.0", "flate2", "libmdbx", "lmdb-rkv", "lmdb-rkv-sys", "lru 0.12.5", "maplit", - "metrics", + "metrics 0.2.0", "parking_lot", "rand 0.9.2", "rayon", "redb", "safe_arith", "serde", - "ssz_types", + "ssz_types 0.14.0", "strum", "tempfile", "tracing", - "tree_hash", - "tree_hash_derive", + "tree_hash 0.12.1", + "tree_hash_derive 0.12.1", "typenum", - "types", + "types 0.2.1", +] + +[[package]] +name = "slasher_service" +version = "0.1.0" +dependencies = [ + "beacon_chain", + "directory", + "lighthouse_network", + "network", + "slasher", + "slot_clock", + "state_processing", + "task_executor", + "tokio", + "tracing", + "types 0.2.1", +] + +[[package]] +name = "slashing_protection" +version = "0.1.0" +dependencies = [ + "arbitrary", + "bls 0.2.0", + "eip_3076", + "ethereum_serde_utils", + "filesystem", + "fixed_bytes 0.1.0", + "r2d2", + "r2d2_sqlite", + "rayon", + "rusqlite", + "serde", + "serde_json", + "tempfile", + "tracing", + "types 0.2.1", +] + +[[package]] +name = "slop-algebra" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "691beea96fd18d4881f9ca1cb4e58194dac6366f24956a2fdae00c8ee382a0c9" +dependencies = [ + "itertools 0.14.0", + "p3-field", + "serde", +] + +[[package]] +name = "slop-bn254" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1852499c245f7f3dec23408b4930b3ea7570ae914b9c31f12950ac539d85ee" +dependencies = [ + "ff 0.13.1", + "p3-bn254-fr", + "serde", + "slop-algebra", + "slop-challenger", + "slop-poseidon2", + "slop-symmetric", + "zkhash", +] + +[[package]] +name = "slop-challenger" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4349af93602f3876a3eda948a74d9d16d774c401dfe25f41a45ffd84f230bc1" +dependencies = [ + "futures", + "p3-challenger", + "serde", + "slop-algebra", + "slop-symmetric", +] + +[[package]] +name = "slop-koala-bear" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "574784c044d11cf9d8238dc18bce9b897bc34d0fb1daaceafd75ebb400084016" +dependencies = [ + "lazy_static", + "p3-koala-bear", + "serde", + "slop-algebra", + "slop-challenger", + "slop-poseidon2", + "slop-symmetric", +] + +[[package]] +name = "slop-poseidon2" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5af617970b63e8d7199204bc02996745b6c35c39f2b513a118c62c7b1a0b2f1b" +dependencies = [ + "p3-poseidon2", ] [[package]] -name = "slasher_service" -version = "0.1.0" +name = "slop-primitives" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58d82c53508f3ebff8acdabb5db2584f37686257a2549a17c977cf30cd9e24e6" dependencies = [ - "beacon_chain", - "directory", - "lighthouse_network", - "network", - "slasher", - "slot_clock", - "state_processing", - "task_executor", - "tokio", - "tracing", - "types", + "slop-algebra", ] [[package]] -name = "slashing_protection" -version = "0.1.0" +name = "slop-symmetric" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15acfa7f567ffa4f36de134492632a397c33fa6af2e48894e50978b52eeeb871" dependencies = [ - "arbitrary", - "bls", - "eip_3076", - "ethereum_serde_utils", - "filesystem", - "fixed_bytes", - "r2d2", - "r2d2_sqlite", - "rayon", - "rusqlite", - "serde", - "serde_json", - "tempfile", - "tracing", - "types", + "p3-symmetric", ] [[package]] name = "slot_clock" version = "0.2.0" dependencies = [ - "metrics", + "metrics 0.2.0", "parking_lot", - "types", + "types 0.2.1", ] [[package]] @@ -8442,12 +11345,104 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "sp1-lib" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "517e820776910468611149dda66791bdb700c1b7d68b96f0ea2e604f00ad8771" +dependencies = [ + "bincode 1.3.3", + "serde", + "sp1-primitives", +] + +[[package]] +name = "sp1-primitives" +version = "6.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f395525b4fc46d37136f45be264c81718a67f4409c14c547ff491a263e019e7" +dependencies = [ + "bincode 1.3.3", + "blake3", + "elf", + "hex", + "itertools 0.14.0", + "lazy_static", + "num-bigint 0.4.6", + "serde", + "sha2", + "slop-algebra", + "slop-bn254", + "slop-challenger", + "slop-koala-bear", + "slop-poseidon2", + "slop-primitives", + "slop-symmetric", +] + +[[package]] +name = "sp1_bls12_381" +version = "0.8.0-sp1-6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23e41cd36168cc2e51e5d3e35ff0c34b204d945769a65591a76286d04b51e43" +dependencies = [ + "cfg-if 1.0.4", + "ff 0.13.1", + "group 0.13.0", + "pairing 0.23.0", + "rand_core 0.6.4", + "sp1-lib", + "subtle", +] + +[[package]] +name = "sparsestate" +version = "0.1.0" +source = "git+https://github.com/eth-act/zkvm-ethereum-mpt.git?rev=a1e44638c49c4e16751a0b915593fce98ab6bdef#a1e44638c49c4e16751a0b915593fce98ab6bdef" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-trie 0.9.5", + "mpt", + "reth-errors", + "reth-revm", + "reth-stateless", + "reth-trie-common", +] + +[[package]] +name = "spawned-concurrency" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3ec6b3c003075f7d1c4c6475308243e853c9a78149b84b1f8b64d5bed49d49" +dependencies = [ + "futures", + "pin-project-lite", + "spawned-rt", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "spawned-rt" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca60c56b1c60b94dd314edce5ea1a98b6037cca3b44d73828e647bad4dae46c" +dependencies = [ + "crossbeam 0.7.3", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", + "tracing-subscriber", ] [[package]] @@ -8466,6 +11461,22 @@ dependencies = [ "der", ] +[[package]] +name = "ssz_types" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b55bedc9a18ed2860a46d6beb4f4082416ee1d60be0cc364cebdcdddc7afd4" +dependencies = [ + "ethereum_serde_utils", + "ethereum_ssz 0.9.1", + "itertools 0.13.0", + "serde", + "serde_derive", + "smallvec", + "tree_hash 0.10.0", + "typenum", +] + [[package]] name = "ssz_types" version = "0.14.0" @@ -8476,12 +11487,12 @@ dependencies = [ "context_deserialize", "educe", "ethereum_serde_utils", - "ethereum_ssz", + "ethereum_ssz 0.10.1", "itertools 0.14.0", "serde", "serde_derive", "smallvec", - "tree_hash", + "tree_hash 0.12.1", "typenum", ] @@ -8497,29 +11508,29 @@ version = "0.2.0" dependencies = [ "arbitrary", "beacon_chain", - "bls", + "bls 0.2.0", "educe", - "ethereum_hashing", - "ethereum_ssz", - "ethereum_ssz_derive", - "fixed_bytes", - "int_to_bytes", + "ethereum_hashing 0.8.0", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "fixed_bytes 0.1.0", + "int_to_bytes 0.2.0", "integer-sqrt", "itertools 0.10.5", - "merkle_proof", - "metrics", + "merkle_proof 0.2.0", + "metrics 0.2.0", "milhouse", "rand 0.9.2", "rayon", "safe_arith", "smallvec", - "ssz_types", - "test_random_derive", + "ssz_types 0.14.0", + "test_random_derive 0.2.0", "tokio", "tracing", - "tree_hash", + "tree_hash 0.12.1", "typenum", - "types", + "types 0.2.1", ] [[package]] @@ -8527,12 +11538,90 @@ name = "state_transition_vectors" version = "0.1.0" dependencies = [ "beacon_chain", - "bls", - "ethereum_ssz", - "fixed_bytes", + "bls 0.2.0", + "ethereum_ssz 0.10.1", + "fixed_bytes 0.1.0", "state_processing", "tokio", - "types", + "types 0.2.1", +] + +[[package]] +name = "stateless-validator-common" +version = "0.5.0" +source = "git+https://github.com/eth-act/ere-guests?tag=v0.6.0#64c94bb3da631101a6cb2f276c89392cb7c3426f" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "anyhow", + "ethereum_ssz 0.9.1", + "ethereum_ssz_derive 0.9.1", + "rkyv", + "serde", + "serde_with", + "sha2", + "ssz_types 0.11.0", + "tree_hash 0.10.0", + "tree_hash_derive 0.10.0", + "typenum", +] + +[[package]] +name = "stateless-validator-ethrex" +version = "0.5.0" +source = "git+https://github.com/eth-act/ere-guests?tag=v0.6.0#64c94bb3da631101a6cb2f276c89392cb7c3426f" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-rlp", + "anyhow", + "bytes", + "ere-io", + "ere-zkvm-interface", + "ethrex-common", + "ethrex-rlp", + "ethrex-rpc", + "ethrex-vm", + "guest", + "guest_program", + "reth-stateless", + "rkyv", + "stateless-validator-common", + "stateless-validator-reth", +] + +[[package]] +name = "stateless-validator-reth" +version = "0.5.0" +source = "git+https://github.com/eth-act/ere-guests?tag=v0.6.0#64c94bb3da631101a6cb2f276c89392cb7c3426f" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types-engine", + "anyhow", + "ere-io", + "ere-zkvm-interface", + "ethereum_ssz 0.9.1", + "guest", + "once_cell", + "reth-chainspec", + "reth-ethereum-primitives", + "reth-evm-ethereum", + "reth-payload-validator", + "reth-primitives-traits", + "reth-stateless", + "serde", + "serde_with", + "sha2", + "sparsestate", + "ssz_types 0.11.0", + "stateless-validator-common", + "tree_hash 0.10.0", + "tree_hash_derive 0.10.0", ] [[package]] @@ -8546,18 +11635,18 @@ name = "store" version = "0.2.0" dependencies = [ "beacon_chain", - "bls", + "bls 0.2.0", "criterion", "db-key", "directory", - "ethereum_ssz", - "ethereum_ssz_derive", - "fixed_bytes", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "fixed_bytes 0.1.0", "itertools 0.10.5", "leveldb", "logging", "lru 0.12.5", - "metrics", + "metrics 0.2.0", "milhouse", "parking_lot", "rand 0.9.2", @@ -8565,7 +11654,7 @@ dependencies = [ "safe_arith", "serde", "smallvec", - "ssz_types", + "ssz_types 0.14.0", "state_processing", "strum", "superstruct", @@ -8573,17 +11662,11 @@ dependencies = [ "tracing", "tracing-subscriber", "typenum", - "types", + "types 0.2.1", "xdelta3", "zstd", ] -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -8608,7 +11691,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -8619,16 +11702,16 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "superstruct" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b986e4a629907f20a2c2a639a75bc22a8b5d99b444e0d83c395f4cb309022bf" +checksum = "bae4a9ccd7882533c1f210e400763ec6ee64c390fc12248c238276281863719e" dependencies = [ - "darling 0.20.11", - "itertools 0.13.0", + "darling 0.23.0", + "itertools 0.14.0", "proc-macro2", "quote", "smallvec", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -8637,8 +11720,18 @@ version = "0.2.0" dependencies = [ "alloy-primitives", "criterion", - "ethereum_hashing", - "fixed_bytes", + "ethereum_hashing 0.8.0", + "fixed_bytes 0.1.0", +] + +[[package]] +name = "swap_or_not_shuffle" +version = "0.2.0" +source = "git+https://github.com/sigp/lighthouse?branch=unstable#a965bfdf77a0b1a3cb2471b9df787edbe99779e8" +dependencies = [ + "alloy-primitives", + "ethereum_hashing 0.8.0", + "fixed_bytes 0.1.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", ] [[package]] @@ -8654,9 +11747,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.110" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -8665,14 +11758,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.4.1" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3" +checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -8692,7 +11785,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -8701,7 +11794,7 @@ version = "0.26.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c18a6156d1f27a9592ee18c1a846ca8dd5c258b7179fc193ae87c74ebb666f5" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "core-foundation-sys", "libc", "ntapi", @@ -8712,11 +11805,11 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.6.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -8736,12 +11829,12 @@ name = "system_health" version = "0.1.0" dependencies = [ "lighthouse_network", - "metrics", + "metrics 0.2.0", "network_utils", "parking_lot", "serde", "sysinfo", - "types", + "types 0.2.1", ] [[package]] @@ -8769,7 +11862,7 @@ version = "0.1.0" dependencies = [ "async-channel 1.9.0", "futures", - "metrics", + "metrics 0.2.0", "num_cpus", "rayon", "tokio", @@ -8778,15 +11871,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.23.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", - "rustix 1.1.2", - "windows-sys 0.52.0", + "rustix 1.1.4", + "windows-sys 0.61.2", ] [[package]] @@ -8795,7 +11888,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ - "rustix 1.1.2", + "rustix 1.1.4", "windows-sys 0.60.2", ] @@ -8810,7 +11903,16 @@ name = "test_random_derive" version = "0.2.0" dependencies = [ "quote", - "syn 2.0.110", + "syn 2.0.117", +] + +[[package]] +name = "test_random_derive" +version = "0.2.0" +source = "git+https://github.com/sigp/lighthouse?branch=unstable#a965bfdf77a0b1a3cb2471b9df787edbe99779e8" +dependencies = [ + "quote", + "syn 2.0.117", ] [[package]] @@ -8824,11 +11926,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -8839,18 +11941,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -8859,7 +11961,7 @@ version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", ] [[package]] @@ -8904,30 +12006,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.44" +version = "0.3.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", - "serde", + "serde_core", "time-core", "time-macros", ] [[package]] name = "time-core" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "time-macros" -version = "0.2.24" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" dependencies = [ "num-conv", "time-core", @@ -8992,9 +12094,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -9007,9 +12109,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.48.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ "bytes", "libc", @@ -9017,7 +12119,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.1", + "socket2 0.6.3", "tokio-macros", "tracing", "windows-sys 0.61.2", @@ -9025,13 +12127,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -9061,15 +12163,15 @@ version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "rustls 0.23.35", + "rustls 0.23.37", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", @@ -9077,16 +12179,29 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" -version = "0.7.17" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", "futures-io", "futures-sink", + "futures-util", "pin-project-lite", "slab", "tokio", @@ -9094,34 +12209,64 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.0.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.24.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "01f2eadbbc6b377a847be05f60791ef1058d9f696ecb51d2c07fe911d8569d8e" dependencies = [ + "indexmap 2.13.0", "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", ] [[package]] name = "toml_edit" -version = "0.23.7" +version = "0.25.5+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" +checksum = "8ca1a40644a28bce036923f6a431df0b34236949d111cc07cb6dca830c9ef2e1" dependencies = [ - "indexmap 2.12.0", - "toml_datetime", + "indexmap 2.13.0", + "toml_datetime 1.0.1+spec-1.1.0", "toml_parser", - "winnow", + "winnow 1.0.0", ] [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.0.10+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420" dependencies = [ - "winnow", + "winnow 1.0.0", ] +[[package]] +name = "toml_writer" +version = "1.0.7+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d" + [[package]] name = "tonic" version = "0.12.3" @@ -9130,11 +12275,11 @@ checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", - "axum", + "axum 0.7.9", "base64 0.22.1", "bytes", - "h2 0.4.12", - "http 1.3.1", + "h2 0.4.13", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "hyper 1.8.1", @@ -9161,7 +12306,7 @@ dependencies = [ "async-trait", "base64 0.22.1", "bytes", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "hyper 1.8.1", @@ -9174,7 +12319,7 @@ dependencies = [ "tokio", "tokio-rustls 0.26.4", "tokio-stream", - "tower 0.5.2", + "tower 0.5.3", "tower-layer", "tower-service", "tracing", @@ -9202,13 +12347,13 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", - "indexmap 2.12.0", + "indexmap 2.13.0", "pin-project-lite", "slab", "sync_wrapper", @@ -9221,20 +12366,22 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", + "http-body-util", "iri-string", "pin-project-lite", - "tower 0.5.2", + "tower 0.5.3", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -9251,9 +12398,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -9263,32 +12410,32 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" dependencies = [ - "crossbeam-channel", - "thiserror 1.0.69", + "crossbeam-channel 0.5.15", + "thiserror 2.0.18", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -9335,9 +12482,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.20" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -9356,27 +12503,52 @@ dependencies = [ [[package]] name = "tree_hash" -version = "0.12.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2db21caa355767db4fd6129876e5ae278a8699f4a6959b1e3e7aff610b532d52" +checksum = "ee44f4cef85f88b4dea21c0b1f58320bdf35715cf56d840969487cff00613321" dependencies = [ "alloy-primitives", - "ethereum_hashing", - "ethereum_ssz", + "ethereum_hashing 0.7.0", + "ethereum_ssz 0.9.1", + "smallvec", + "typenum", +] + +[[package]] +name = "tree_hash" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fd51aa83d2eb83b04570808430808b5d24fdbf479a4d5ac5dee4a2e2dd2be4" +dependencies = [ + "alloy-primitives", + "ethereum_hashing 0.8.0", + "ethereum_ssz 0.10.1", "smallvec", "typenum", ] [[package]] name = "tree_hash_derive" -version = "0.12.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711cc655fcbb48384a87dc2bf641b991a15c5ad9afc3caa0b1ab1df3b436f70f" +checksum = "0bee2ea1551f90040ab0e34b6fb7f2fa3bad8acc925837ac654f2c78a13e3089" dependencies = [ - "darling 0.21.3", + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tree_hash_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8840ad4d852e325d3afa7fde8a50b2412f89dce47d7eb291c0cc7f87cd040f38" +dependencies = [ + "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -9386,25 +12558,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" dependencies = [ "hash-db", - "rlp", + "rlp 0.5.2", +] + +[[package]] +name = "triomphe" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" +dependencies = [ + "serde", + "stable_deref_trait", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http 1.4.0", + "httparse", + "log", + "rand 0.9.2", + "sha1", + "thiserror 2.0.18", + "utf-8", ] [[package]] -name = "triomphe" -version = "0.1.15" +name = "twirp" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" +checksum = "e3c52cc4e4423b6b3e2e2659523c8c9e19af514a06422fe77a95d86f6bf3478a" dependencies = [ + "anyhow", + "async-trait", + "axum 0.8.8", + "futures", + "http 1.4.0", + "http-body-util", + "hyper 1.8.1", + "prost", + "reqwest", "serde", - "stable_deref_trait", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tower 0.5.3", + "url", ] -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - [[package]] name = "typenum" version = "1.19.0" @@ -9419,23 +12631,23 @@ dependencies = [ "alloy-rlp", "arbitrary", "beacon_chain", - "bls", + "bls 0.2.0", "compare_fields", "context_deserialize", "criterion", "educe", - "eth2_interop_keypairs", - "ethereum_hashing", + "eth2_interop_keypairs 0.2.0", + "ethereum_hashing 0.8.0", "ethereum_serde_utils", - "ethereum_ssz", - "ethereum_ssz_derive", - "fixed_bytes", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "fixed_bytes 0.1.0", "hex", - "int_to_bytes", + "int_to_bytes 0.2.0", "itertools 0.10.5", - "kzg", + "kzg 0.1.0", "maplit", - "merkle_proof", + "merkle_proof 0.2.0", "metastruct", "milhouse", "parking_lot", @@ -9451,16 +12663,63 @@ dependencies = [ "serde_json", "serde_yaml", "smallvec", - "ssz_types", + "ssz_types 0.14.0", "state_processing", "superstruct", - "swap_or_not_shuffle", + "swap_or_not_shuffle 0.2.0", "tempfile", - "test_random_derive", + "test_random_derive 0.2.0", "tokio", "tracing", - "tree_hash", - "tree_hash_derive", + "tree_hash 0.12.1", + "tree_hash_derive 0.12.1", + "typenum", +] + +[[package]] +name = "types" +version = "0.2.1" +source = "git+https://github.com/sigp/lighthouse?branch=unstable#a965bfdf77a0b1a3cb2471b9df787edbe99779e8" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "bls 0.2.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", + "compare_fields", + "context_deserialize", + "educe", + "eth2_interop_keypairs 0.2.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", + "ethereum_hashing 0.8.0", + "ethereum_serde_utils", + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "fixed_bytes 0.1.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", + "hex", + "int_to_bytes 0.2.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", + "itertools 0.14.0", + "kzg 0.1.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", + "maplit", + "merkle_proof 0.2.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", + "metastruct", + "milhouse", + "parking_lot", + "rand 0.9.2", + "rand_xorshift 0.4.0", + "rayon", + "regex", + "rpds", + "safe_arith", + "serde", + "serde_json", + "serde_yaml", + "smallvec", + "ssz_types 0.14.0", + "superstruct", + "swap_or_not_shuffle 0.2.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", + "tempfile", + "test_random_derive 0.2.0 (git+https://github.com/sigp/lighthouse?branch=unstable)", + "tracing", + "tree_hash 0.12.1", + "tree_hash_derive 0.12.1", "typenum", ] @@ -9508,15 +12767,15 @@ checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e" [[package]] name = "unicase" -version = "2.8.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-normalization" @@ -9527,6 +12786,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -9572,18 +12837,31 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "url" -version = "2.5.7" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", + "serde_derive", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -9602,17 +12880,17 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" dependencies = [ - "getrandom 0.2.16", + "getrandom 0.2.17", "serde", ] [[package]] name = "uuid" -version = "1.18.1" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.2", "js-sys", "wasm-bindgen", ] @@ -9636,7 +12914,7 @@ dependencies = [ "hyper 1.8.1", "initialized_validators", "lighthouse_validator_store", - "metrics", + "metrics 0.2.0", "monitoring_api", "parking_lot", "reqwest", @@ -9646,7 +12924,7 @@ dependencies = [ "slot_clock", "tokio", "tracing", - "types", + "types 0.2.1", "validator_http_api", "validator_http_metrics", "validator_metrics", @@ -9658,7 +12936,7 @@ dependencies = [ name = "validator_dir" version = "0.1.0" dependencies = [ - "bls", + "bls 0.2.0", "deposit_contract", "educe", "eth2_keystore", @@ -9667,8 +12945,8 @@ dependencies = [ "lockfile", "rand 0.9.2", "tempfile", - "tree_hash", - "types", + "tree_hash 0.12.1", + "types 0.2.1", ] [[package]] @@ -9677,7 +12955,7 @@ version = "0.1.0" dependencies = [ "account_utils", "beacon_node_fallback", - "bls", + "bls 0.2.0", "deposit_contract", "directory", "dirs", @@ -9686,7 +12964,7 @@ dependencies = [ "eth2_keystore", "ethereum_serde_utils", "filesystem", - "fixed_bytes", + "fixed_bytes 0.1.0", "futures", "graffiti_file", "health_metrics", @@ -9703,7 +12981,7 @@ dependencies = [ "signing_method", "slashing_protection", "slot_clock", - "ssz_types", + "ssz_types 0.14.0", "sysinfo", "system_health", "task_executor", @@ -9712,7 +12990,7 @@ dependencies = [ "tokio-stream", "tracing", "typenum", - "types", + "types 0.2.1", "url", "validator_dir", "validator_services", @@ -9731,12 +13009,12 @@ dependencies = [ "lighthouse_version", "logging", "malloc_utils", - "metrics", + "metrics 0.2.0", "parking_lot", "serde", "slot_clock", "tracing", - "types", + "types 0.2.1", "validator_metrics", "validator_services", "warp", @@ -9749,7 +13027,7 @@ version = "0.1.0" dependencies = [ "account_utils", "beacon_chain", - "bls", + "bls 0.2.0", "clap", "clap_utils", "educe", @@ -9766,8 +13044,8 @@ dependencies = [ "slot_clock", "tempfile", "tokio", - "tree_hash", - "types", + "tree_hash 0.12.1", + "types 0.2.1", "validator_http_api", "zeroize", ] @@ -9776,7 +13054,7 @@ dependencies = [ name = "validator_metrics" version = "0.1.0" dependencies = [ - "metrics", + "metrics 0.2.0", ] [[package]] @@ -9784,36 +13062,35 @@ name = "validator_services" version = "0.1.0" dependencies = [ "beacon_node_fallback", - "bls", + "bls 0.2.0", "either", "eth2", "execution_layer", "futures", "graffiti_file", - "lighthouse_validator_store", "logging", "parking_lot", "safe_arith", "serde_json", "slot_clock", + "ssz_types 0.14.0", "task_executor", "tokio", "tracing", - "tree_hash", - "types", + "tree_hash 0.12.1", + "types 0.2.1", "validator_metrics", "validator_store", - "warp_utils", ] [[package]] name = "validator_store" version = "0.1.0" dependencies = [ - "bls", + "bls 0.2.0", "eth2", "slashing_protection", - "types", + "types 0.2.1", ] [[package]] @@ -9826,7 +13103,7 @@ dependencies = [ "sensitive_url", "serde_json", "tracing", - "types", + "types 0.2.1", ] [[package]] @@ -9890,7 +13167,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "headers", + "headers 0.3.9", "http 0.2.12", "hyper 0.14.32", "log", @@ -9916,13 +13193,13 @@ version = "0.1.0" dependencies = [ "bytes", "eth2", - "headers", + "headers 0.3.9", "safe_arith", "serde", "serde_array_query", "serde_json", "tokio", - "types", + "types 0.2.1", "warp", ] @@ -9934,20 +13211,29 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "once_cell", "rustversion", "wasm-bindgen-macro", @@ -9956,11 +13242,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.55" +version = "0.4.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", + "futures-util", "js-sys", "once_cell", "wasm-bindgen", @@ -9969,9 +13256,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -9979,26 +13266,48 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" 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.13.0", + "wasm-encoder", + "wasmparser", +] + [[package]] name = "wasm-streams" version = "0.4.2" @@ -10012,6 +13321,18 @@ 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.0", + "hashbrown 0.15.5", + "indexmap 2.13.0", + "semver 1.0.27", +] + [[package]] name = "wasmtimer" version = "0.4.3" @@ -10028,9 +13349,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.82" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", @@ -10052,12 +13373,12 @@ version = "0.1.0" dependencies = [ "account_utils", "async-channel 1.9.0", - "bls", + "bls 0.2.0", "environment", "eth2", "eth2_keystore", "eth2_network_config", - "fixed_bytes", + "fixed_bytes 0.1.0", "futures", "initialized_validators", "lighthouse_validator_store", @@ -10069,11 +13390,11 @@ dependencies = [ "serde_yaml", "slashing_protection", "slot_clock", - "ssz_types", + "ssz_types 0.14.0", "task_executor", "tempfile", "tokio", - "types", + "types 0.2.1", "url", "validator_store", "zip", @@ -10081,9 +13402,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] @@ -10100,6 +13421,16 @@ dependencies = [ "rustix 0.38.44", ] +[[package]] +name = "wide" +version = "0.7.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce5da8ecb62bcd8ec8b7ea19f69a51275e91299be594ea5cc6ef7819e16cd03" +dependencies = [ + "bytemuck", + "safe_arch", +] + [[package]] name = "widestring" version = "0.4.3" @@ -10134,7 +13465,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -10145,12 +13476,14 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.53.0" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efc5cf48f83140dcaab716eeaea345f9e93d0018fb81162753a3f76c3397b538" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ - "windows-core 0.53.0", - "windows-targets 0.52.6", + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", ] [[package]] @@ -10166,13 +13499,12 @@ dependencies = [ ] [[package]] -name = "windows-core" -version = "0.53.0" +name = "windows-collections" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dcc5b895a6377f1ab9fa55acedab1fd5ac0db66ad1e6c7f47e28a22e446a5dd" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-result 0.1.2", - "windows-targets 0.52.6", + "windows-core", ] [[package]] @@ -10184,10 +13516,21 @@ dependencies = [ "windows-implement", "windows-interface", "windows-link", - "windows-result 0.4.1", + "windows-result", "windows-strings", ] +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + [[package]] name = "windows-implement" version = "0.60.2" @@ -10196,7 +13539,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -10207,7 +13550,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -10217,12 +13560,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-result" -version = "0.1.2" +name = "windows-numerics" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-targets 0.52.6", + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", ] [[package]] @@ -10336,6 +13691,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -10476,9 +13840,18 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" dependencies = [ "memchr", ] @@ -10489,15 +13862,97 @@ version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "cfg-if", + "cfg-if 1.0.4", "windows-sys 0.48.0", ] [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "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.13.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.0", + "indexmap 2.13.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 = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.13.0", + "log", + "semver 1.0.27", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "workspace_members" @@ -10547,7 +14002,7 @@ dependencies = [ "nom", "oid-registry", "rusticata-macros", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", ] @@ -10580,6 +14035,12 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "xxhash-rust" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + [[package]] name = "yaml-rust2" version = "0.8.1" @@ -10608,9 +14069,9 @@ dependencies = [ [[package]] name = "yamux" -version = "0.13.8" +version = "0.13.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deab71f2e20691b4728b349c6cee8fc7223880fa67b6b4f92225ec32225447e5" +checksum = "1991f6690292030e31b0144d73f5e8368936c58e45e7068254f7138b23b00672" dependencies = [ "futures", "log", @@ -10650,28 +14111,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.27" +version = "0.8.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.27" +version = "0.8.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -10691,7 +14152,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", "synstructure", ] @@ -10707,13 +14168,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.4.2" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -10746,7 +14207,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.110", + "syn 2.0.117", ] [[package]] @@ -10758,16 +14219,108 @@ dependencies = [ "arbitrary", "crc32fast", "flate2", - "indexmap 2.12.0", + "indexmap 2.13.0", "memchr", "zopfli", ] +[[package]] +name = "zkboost-server" +version = "0.1.0" +source = "git+https://github.com/eth-act/zkboost?branch=master#1715344c097f56f2837dc3c4f8a652f28643e3bf" +dependencies = [ + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "anyhow", + "axum 0.8.8", + "bincode 1.3.3", + "bytes", + "clap", + "ere-server", + "ere-zkvm-interface", + "lru 0.12.5", + "metrics 0.24.3", + "metrics-exporter-prometheus", + "rand 0.9.2", + "reqwest", + "reth-ethereum-primitives", + "reth-stateless", + "serde", + "serde_json", + "sha2", + "stateless-validator-ethrex", + "stateless-validator-reth", + "strum", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "toml_edit 0.24.1+spec-1.1.0", + "tower-http", + "tracing", + "tracing-subscriber", + "url", + "zkboost-types", +] + +[[package]] +name = "zkboost-types" +version = "0.1.0" +source = "git+https://github.com/eth-act/zkboost?branch=master#1715344c097f56f2837dc3c4f8a652f28643e3bf" +dependencies = [ + "ethereum_ssz 0.10.1", + "ethereum_ssz_derive 0.10.1", + "serde", + "serde_json", + "ssz_types 0.14.0", + "strum", + "superstruct", + "tree_hash 0.12.1", + "tree_hash_derive 0.12.1", + "types 0.2.1 (git+https://github.com/sigp/lighthouse?branch=unstable)", +] + +[[package]] +name = "zkhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4352d1081da6922701401cdd4cbf29a2723feb4cfabb5771f6fee8e9276da1c7" +dependencies = [ + "ark-ff 0.4.2", + "ark-std 0.4.0", + "bitvec", + "blake2", + "bls12_381 0.7.1", + "byteorder", + "cfg-if 1.0.4", + "group 0.12.1", + "group 0.13.0", + "halo2", + "hex", + "jubjub", + "lazy_static", + "pasta_curves 0.5.1", + "rand 0.8.5", + "serde", + "sha2", + "sha3", + "subtle", +] + [[package]] name = "zlib-rs" -version = "0.5.4" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" + +[[package]] +name = "zmij" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f936044d677be1a1168fae1d03b583a285a5dd9d8cbf7b24c23aa1fc775235" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zopfli" diff --git a/Cargo.toml b/Cargo.toml index 81c2bb847a6..e0f1409dd73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ members = [ "testing/execution_engine_integration", "testing/node_test_rig", "testing/proof_engine", + "testing/proof_engine_zkboost", "testing/simulator", "testing/state_transition_vectors", "testing/validator_test_rig", @@ -108,6 +109,7 @@ alloy-signer-local = { version = "1", default-features = false } anyhow = "1" arbitrary = { version = "1", features = ["derive"] } async-channel = "1.9.0" +async-stream = "0.3" axum = "0.7.7" beacon_chain = { path = "beacon_node/beacon_chain" } beacon_node = { path = "beacon_node" } @@ -184,6 +186,7 @@ malloc_utils = { path = "common/malloc_utils" } maplit = "1" merkle_proof = { path = "consensus/merkle_proof" } metrics = { path = "common/metrics" } +metrics-exporter-prometheus = "0.16" milhouse = { version = "0.9", default-features = false, features = ["context_deserialize"] } mockall = "0.13" mockall_double = "0.3" @@ -214,8 +217,8 @@ reqwest = { version = "0.12", default-features = false, features = [ "json", "stream", "rustls-tls", - "native-tls-vendored", ] } +reqwest-eventsource = "0.6" ring = "0.17" rpds = "0.11" rusqlite = { version = "0.28", features = ["bundled"] } @@ -278,6 +281,8 @@ workspace_members = { path = "common/workspace_members" } xdelta3 = { git = "https://github.com/sigp/xdelta3-rs", rev = "4db64086bb02e9febb584ba93b9d16bb2ae3825a" } zeroize = { version = "1", features = ["zeroize_derive", "serde"] } zip = { version = "6.0", default-features = false, features = ["deflate"] } +zkboost-server = { git = "https://github.com/eth-act/zkboost", branch = "master" } +zkboost-types = { git = "https://github.com/eth-act/zkboost", branch = "master" } zstd = "0.13" [profile.maxperf] @@ -292,3 +297,4 @@ debug = true [patch.crates-io] quick-protobuf = { git = "https://github.com/sigp/quick-protobuf.git", rev = "681f413312404ab6e51f0b46f39b0075c6f4ebfd" } +quinn = { git = "https://github.com/sigp/quinn", rev = "59af87979c8411864c1cb68613222f54ed2930a7" } diff --git a/Dockerfile b/Dockerfile index 8cc20ab000f..ccfd2826b1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -FROM rust:1.88.0-bullseye AS builder -RUN apt-get update && apt-get -y upgrade && apt-get install -y cmake libclang-dev +FROM rust:1.91.0-bullseye AS builder +RUN apt-get update && apt-get -y upgrade && apt-get install -y cmake libclang-dev clang ARG FEATURES ARG PROFILE=release ARG CARGO_USE_GIT_CLI=true @@ -7,6 +7,9 @@ ENV FEATURES=$FEATURES ENV PROFILE=$PROFILE ENV CARGO_NET_GIT_FETCH_WITH_CLI=$CARGO_USE_GIT_CLI ENV CARGO_INCREMENTAL=1 +# Use Clang for C/C++ compilation (leveldb-sys requires -Wthread-safety, a Clang-only flag) +ENV CC=clang +ENV CXX=clang++ WORKDIR /lighthouse COPY . . diff --git a/Dockerfile.reproducible b/Dockerfile.reproducible index 903515373f8..c4526c73170 100644 --- a/Dockerfile.reproducible +++ b/Dockerfile.reproducible @@ -1,5 +1,5 @@ -# Define the Rust image as an argument with a default to x86_64 Rust 1.88 image based on Debian Bullseye -ARG RUST_IMAGE="rust:1.88-bullseye@sha256:8e3c421122bf4cd3b2a866af41a4dd52d87ad9e315fd2cb5100e87a7187a9816" +# Define the Rust image as an argument with a default to x86_64 Rust 1.91 image based on Debian Bullseye +ARG RUST_IMAGE="rust:1.91-bullseye@sha256:ed6afcf912afc6aeddf0d1ff0dc6894c9b1c8f865964ef3f533e3ea77a64ffea" FROM ${RUST_IMAGE} AS builder # Install specific version of the build dependencies diff --git a/Makefile b/Makefile index 9d08c3ebe18..0be86e3d180 100644 --- a/Makefile +++ b/Makefile @@ -177,14 +177,19 @@ build-release-tarballs: test-release: cargo nextest run --workspace --release --features "$(TEST_FEATURES)" \ --exclude ef_tests --exclude beacon_chain --exclude slasher --exclude network \ - --exclude http_api + --exclude http_api --exclude proof_engine_zkboost_test # Runs the full workspace tests in **debug**, without downloading any additional test # vectors. test-debug: cargo nextest run --workspace --features "$(TEST_FEATURES)" \ - --exclude ef_tests --exclude beacon_chain --exclude network --exclude http_api + --exclude ef_tests --exclude beacon_chain --exclude network --exclude http_api \ + --exclude proof_engine_zkboost_test + +# Runs the proof_engine_zkboost integration tests against a real (mock-backend) zkBoost server. +test-zkboost: + cargo nextest run -p proof_engine_zkboost_test --release --features "$(TEST_FEATURES)" # Runs cargo-fmt (linter). cargo-fmt: diff --git a/account_manager/Cargo.toml b/account_manager/Cargo.toml index 8dd50cbc6ee..05e6f125546 100644 --- a/account_manager/Cargo.toml +++ b/account_manager/Cargo.toml @@ -1,10 +1,7 @@ [package] name = "account_manager" version = { workspace = true } -authors = [ - "Paul Hauner ", - "Luke Anderson ", -] +authors = ["Paul Hauner ", "Luke Anderson "] edition = { workspace = true } [dependencies] diff --git a/beacon_node/Cargo.toml b/beacon_node/Cargo.toml index 5352814dd5d..796d62deca3 100644 --- a/beacon_node/Cargo.toml +++ b/beacon_node/Cargo.toml @@ -1,10 +1,7 @@ [package] name = "beacon_node" version = { workspace = true } -authors = [ - "Paul Hauner ", - "Age Manning ", "Age Manning BeaconChain { )?)) } + /// Load persisted ProofEngine state from disk, returning `None` if not found or corrupt. + pub fn load_proof_engine_state(store: BeaconStore) -> Option { + match store + .hot_db + .get_bytes(DBColumn::ProofEngine, PROOF_ENGINE_DB_KEY.as_slice()) + { + Ok(Some(bytes)) => { + match PersistedProofEngineState::from_bytes(&bytes, store.get_config()) { + Ok(persisted) => { + tracing::info!("Loaded ProofEngine state from disk"); + Some(persisted) + } + Err(e) => { + tracing::warn!(error = ?e, "Failed to decode ProofEngine state from disk, starting fresh"); + None + } + } + } + Ok(None) => None, + Err(e) => { + tracing::warn!(error = ?e, "Failed to read ProofEngine state from disk, starting fresh"); + None + } + } + } + /// Persists `self.op_pool` to disk. /// /// ## Notes @@ -4316,9 +4342,6 @@ impl BeaconChain { payload_verification_status: PayloadVerificationStatus, current_slot: Slot, ) { - // TODO: Optimise this so we don't have to clone. - let beacon_block = Arc::unwrap_or_clone(signed_block.clone()); - let (beacon_block, _) = beacon_block.deconstruct(); let block = signed_block.message(); // Only present some metrics for blocks from the previous epoch or later. @@ -4361,21 +4384,6 @@ impl BeaconChain { execution_optimistic: payload_verification_status.is_optimistic(), })); } - - // Emit BlockFull event if there are block_full subscribers - if event_handler.has_block_full_subscribers() { - let slot = block.slot(); - // Convert BeaconBlockRef to owned BeaconBlock for the event - event_handler.register(EventKind::BlockFull(Box::new(ForkVersionedResponse { - data: SseBlockFull { - slot, - block: beacon_block, - execution_optimistic: payload_verification_status.is_optimistic(), - }, - metadata: Default::default(), - version: self.spec.fork_name_at_slot::(slot), - }))); - } } // Do not trigger light_client server update producer for old blocks, to extra work @@ -7502,7 +7510,7 @@ impl BeaconChain { pub async fn verify_execution_proof( self: &Arc, signed_proof: types::SignedExecutionProof, - ) -> Result { + ) -> Result<(ProofStatus, Option<(Hash256, Slot)>), Error> { // TODO: This function clones the proof multiple times. Optimise it. // Clone for moving into closures @@ -7567,7 +7575,7 @@ impl BeaconChain { // Step 3: Update the fork choice if the proof engine returns valid. // The proof engine returns valid if the proof is valid and the criteria for the associated block root to be considered valid are met. // The proof engine returns ACCEPTED if the proof is valid but block validity criteria are not met. - if verification_result.is_valid() { + if verification_result.is_valid() || verification_result.is_accepted() { let request_root = signed_proof.request_root(); // Look up the beacon block root from request root @@ -7602,19 +7610,28 @@ impl BeaconChain { ?request_root, "Updated fork choice for verified proof" ); + + // Look up the slot so callers can update local execution proof status. + let slot = self + .store + .get_blinded_block(&block_root) + .ok() + .flatten() + .map(|b| b.slot()); + return Ok((verification_result, slot.map(|s| (block_root, s)))); } - Ok(verification_result) + Ok((verification_result, None)) } } impl Drop for BeaconChain { fn drop(&mut self) { let drop = || -> Result<(), Error> { - // TODO: Persist the proof engine state if the BeaconChain is dropped. self.persist_fork_choice()?; self.persist_op_pool()?; - self.persist_custody_context() + self.persist_custody_context()?; + self.persist_proof_engine() }; if let Err(e) = drop() { diff --git a/beacon_node/beacon_chain/src/bellatrix_readiness.rs b/beacon_node/beacon_chain/src/bellatrix_readiness.rs index 33bf9367ebd..d588885ea1d 100644 --- a/beacon_node/beacon_chain/src/bellatrix_readiness.rs +++ b/beacon_node/beacon_chain/src/bellatrix_readiness.rs @@ -2,7 +2,6 @@ //! transition. use crate::{BeaconChain, BeaconChainError as Error, BeaconChainTypes}; -use execution_layer::eip8025::ProofEngine; use execution_layer::{BlockByNumberQuery, ForkchoiceState}; use serde::{Deserialize, Serialize, Serializer}; use std::fmt; diff --git a/beacon_node/beacon_chain/src/builder.rs b/beacon_node/beacon_chain/src/builder.rs index dc38fc1c292..6ccf340217a 100644 --- a/beacon_node/beacon_chain/src/builder.rs +++ b/beacon_node/beacon_chain/src/builder.rs @@ -975,6 +975,20 @@ where }; debug!(?custody_context, "Loaded persisted custody context"); + // Restore ProofEngine state from disk if available. + if let Some(proof_engine) = self + .execution_layer + .as_ref() + .and_then(|el| el.proof_engine()) + && let Some(store) = self.store + && let Some(persisted) = + crate::BeaconChain::>::load_proof_engine_state( + store.clone(), + ) + { + proof_engine.restore_from_persisted(persisted); + } + let beacon_chain = BeaconChain { spec: self.spec.clone(), config: self.chain_config, diff --git a/beacon_node/beacon_chain/src/canonical_head.rs b/beacon_node/beacon_chain/src/canonical_head.rs index 76c08c5e39c..9f2f3d4c1b4 100644 --- a/beacon_node/beacon_chain/src/canonical_head.rs +++ b/beacon_node/beacon_chain/src/canonical_head.rs @@ -42,6 +42,7 @@ use crate::{ validator_monitor::get_slot_delay_ms, }; use eth2::types::{EventKind, SseChainReorg, SseFinalizedCheckpoint, SseHead, SseLateHead}; +use execution_layer::eip8025::PROOF_ENGINE_DB_KEY; use fork_choice::{ ExecutionStatus, ForkChoiceStore, ForkChoiceView, ForkchoiceUpdateParameters, ProtoBlock, ResetPayloadStatuses, @@ -867,6 +868,7 @@ impl BeaconChain { if is_epoch_transition || reorg_distance.is_some() { self.persist_fork_choice()?; + self.persist_proof_engine()?; self.op_pool.prune_attestations(self.epoch()?); } @@ -1047,6 +1049,23 @@ impl BeaconChain { .map_err(Into::into) } + /// Persist the proof engine to disk, writing immediately. + pub fn persist_proof_engine(&self) -> Result<(), Error> { + let Some(proof_engine) = self + .execution_layer + .as_ref() + .and_then(|el| el.proof_engine()) + else { + return Ok(()); + }; + + let op = proof_engine + .to_persisted() + .as_kv_store_op(PROOF_ENGINE_DB_KEY, self.store.get_config())?; + self.store.hot_db.do_atomically(vec![op])?; + Ok(()) + } + /// Return a database operation for writing fork choice to disk. pub fn persist_fork_choice_in_batch_standalone( fork_choice: &BeaconForkChoice, diff --git a/beacon_node/beacon_chain/src/custody_context.rs b/beacon_node/beacon_chain/src/custody_context.rs index c512ce616a1..d706dd99707 100644 --- a/beacon_node/beacon_chain/src/custody_context.rs +++ b/beacon_node/beacon_chain/src/custody_context.rs @@ -377,13 +377,10 @@ impl CustodyContext { current_slot: Slot, spec: &ChainSpec, ) -> Option { - let Some((effective_epoch, new_validator_custody)) = self + let (effective_epoch, new_validator_custody) = self .validator_registrations .write() - .register_validators::(validators_and_balance, current_slot, spec) - else { - return None; - }; + .register_validators::(validators_and_balance, current_slot, spec)?; let current_cgc = self.validator_custody_count.load(Ordering::Relaxed); diff --git a/beacon_node/beacon_chain/src/eip8025/proof_verification.rs b/beacon_node/beacon_chain/src/eip8025/proof_verification.rs index ba00f69307c..32c57e085e7 100644 --- a/beacon_node/beacon_chain/src/eip8025/proof_verification.rs +++ b/beacon_node/beacon_chain/src/eip8025/proof_verification.rs @@ -25,8 +25,6 @@ pub enum ExecutionProofError { InvalidValidatorPubkey, /// Failed to decompress the signature. InvalidSignatureFormat, - /// The fork does not support EIP-8025. - UnsupportedFork, /// Failed to retrieve beacon state. StateError(String), /// No execution layer configured. @@ -55,9 +53,6 @@ impl fmt::Display for ExecutionProofError { ExecutionProofError::InvalidSignatureFormat => { write!(f, "Invalid signature format") } - ExecutionProofError::UnsupportedFork => { - write!(f, "Fork does not support EIP-8025") - } ExecutionProofError::StateError(msg) => { write!(f, "Beacon state error: {}", msg) } @@ -303,39 +298,6 @@ mod tests { )); } - #[test] - fn test_verify_unsupported_fork() { - let keypair = Keypair::random(); - let spec = MainnetEthSpec::default_spec(); - let genesis_validators_root = Hash256::repeat_byte(0xcd); - let proof = create_test_proof(vec![1, 2, 3, 4]); - - // Use Electra spec (pre-Fulu, EIP-8025 not enabled) - let electra_spec = ForkName::Electra.make_genesis_spec(spec.clone()); - let signed = sign_proof( - &proof, - &keypair, - ForkName::Electra, - genesis_validators_root, - &electra_spec, - ); - - let result = verify_signed_execution_proof_signature::( - &signed, - &keypair.pk.compress(), - ForkName::Electra, // Pre-Fulu fork - genesis_validators_root, - &electra_spec, - ); - - assert!(matches!( - result, - Err(BeaconChainError::ExecutionProofError( - ExecutionProofError::UnsupportedFork - )) - )); - } - #[test] fn test_verify_invalid_pubkey_format() { let keypair = Keypair::random(); diff --git a/beacon_node/beacon_chain/src/events.rs b/beacon_node/beacon_chain/src/events.rs index 5adce3f8dd2..4684db96ba9 100644 --- a/beacon_node/beacon_chain/src/events.rs +++ b/beacon_node/beacon_chain/src/events.rs @@ -1,4 +1,6 @@ -pub use eth2::types::{EventKind, SseBlock, SseFinalizedCheckpoint, SseHead}; +pub use eth2::types::{ + EventKind, SseBlock, SseExecutionProofValidated, SseFinalizedCheckpoint, SseHead, +}; use tokio::sync::broadcast; use tokio::sync::broadcast::{Receiver, Sender, error::SendError}; use tracing::trace; @@ -10,7 +12,6 @@ pub struct ServerSentEventHandler { attestation_tx: Sender>, single_attestation_tx: Sender>, block_tx: Sender>, - block_full_tx: Sender>, blob_sidecar_tx: Sender>, data_column_sidecar_tx: Sender>, finalized_tx: Sender>, @@ -27,6 +28,7 @@ pub struct ServerSentEventHandler { attester_slashing_tx: Sender>, bls_to_execution_change_tx: Sender>, block_gossip_tx: Sender>, + execution_proof_validated_tx: Sender>, } impl ServerSentEventHandler { @@ -38,7 +40,6 @@ impl ServerSentEventHandler { let (attestation_tx, _) = broadcast::channel(capacity); let (single_attestation_tx, _) = broadcast::channel(capacity); let (block_tx, _) = broadcast::channel(capacity); - let (block_full_tx, _) = broadcast::channel(capacity); let (blob_sidecar_tx, _) = broadcast::channel(capacity); let (data_column_sidecar_tx, _) = broadcast::channel(capacity); let (finalized_tx, _) = broadcast::channel(capacity); @@ -55,12 +56,12 @@ impl ServerSentEventHandler { let (attester_slashing_tx, _) = broadcast::channel(capacity); let (bls_to_execution_change_tx, _) = broadcast::channel(capacity); let (block_gossip_tx, _) = broadcast::channel(capacity); + let (execution_proof_validated_tx, _) = broadcast::channel(capacity); Self { attestation_tx, single_attestation_tx, block_tx, - block_full_tx, blob_sidecar_tx, data_column_sidecar_tx, finalized_tx, @@ -77,6 +78,7 @@ impl ServerSentEventHandler { attester_slashing_tx, bls_to_execution_change_tx, block_gossip_tx, + execution_proof_validated_tx, } } @@ -101,10 +103,6 @@ impl ServerSentEventHandler { .block_tx .send(kind) .map(|count| log_count("block", count)), - EventKind::BlockFull(_) => self - .block_full_tx - .send(kind) - .map(|count| log_count("block_full", count)), EventKind::BlobSidecar(_) => self .blob_sidecar_tx .send(kind) @@ -169,6 +167,10 @@ impl ServerSentEventHandler { .block_gossip_tx .send(kind) .map(|count| log_count("block gossip", count)), + EventKind::ExecutionProofValidated(_) => self + .execution_proof_validated_tx + .send(kind) + .map(|count| log_count("execution proof validated", count)), }; if let Err(SendError(event)) = result { trace!(?event, "No receivers registered to listen for event"); @@ -187,10 +189,6 @@ impl ServerSentEventHandler { self.block_tx.subscribe() } - pub fn subscribe_block_full(&self) -> Receiver> { - self.block_full_tx.subscribe() - } - pub fn subscribe_blob_sidecar(&self) -> Receiver> { self.blob_sidecar_tx.subscribe() } @@ -267,10 +265,6 @@ impl ServerSentEventHandler { self.block_tx.receiver_count() > 0 } - pub fn has_block_full_subscribers(&self) -> bool { - self.block_full_tx.receiver_count() > 0 - } - pub fn has_blob_sidecar_subscribers(&self) -> bool { self.blob_sidecar_tx.receiver_count() > 0 } @@ -326,4 +320,12 @@ impl ServerSentEventHandler { pub fn has_block_gossip_subscribers(&self) -> bool { self.block_gossip_tx.receiver_count() > 0 } + + pub fn subscribe_execution_proof_validated(&self) -> Receiver> { + self.execution_proof_validated_tx.subscribe() + } + + pub fn has_execution_proof_validated_subscribers(&self) -> bool { + self.execution_proof_validated_tx.receiver_count() > 0 + } } diff --git a/beacon_node/beacon_chain/src/schema_change.rs b/beacon_node/beacon_chain/src/schema_change.rs index ddc59783394..5a11e3a5e00 100644 --- a/beacon_node/beacon_chain/src/schema_change.rs +++ b/beacon_node/beacon_chain/src/schema_change.rs @@ -5,6 +5,7 @@ mod migration_schema_v25; mod migration_schema_v26; mod migration_schema_v27; mod migration_schema_v28; +mod migration_schema_v29; use crate::beacon_chain::BeaconChainTypes; use std::sync::Arc; @@ -88,6 +89,14 @@ pub fn migrate_schema( let ops = migration_schema_v28::downgrade_from_v28::(db.clone())?; db.store_schema_version_atomically(to, ops) } + (SchemaVersion(28), SchemaVersion(29)) => { + let ops = migration_schema_v29::upgrade_to_v29()?; + db.store_schema_version_atomically(to, ops) + } + (SchemaVersion(29), SchemaVersion(28)) => { + let ops = migration_schema_v29::downgrade_from_v29()?; + db.store_schema_version_atomically(to, ops) + } // Anything else is an error. (_, _) => Err(HotColdDBError::UnsupportedSchemaVersion { target_version: to, diff --git a/beacon_node/beacon_chain/src/schema_change/migration_schema_v29.rs b/beacon_node/beacon_chain/src/schema_change/migration_schema_v29.rs new file mode 100644 index 00000000000..fc1c4892d22 --- /dev/null +++ b/beacon_node/beacon_chain/src/schema_change/migration_schema_v29.rs @@ -0,0 +1,20 @@ +use store::{DBColumn, Error, KeyValueStoreOp}; +use tracing::info; +use types::Hash256; + +pub const PROOF_ENGINE_DB_KEY: Hash256 = Hash256::ZERO; + +/// Upgrade to v29: no-op. The new `ProofEngine` column is populated lazily at runtime. +pub fn upgrade_to_v29() -> Result, Error> { + info!("Upgrading to v29 (ProofEngine column — no data migration needed)"); + Ok(vec![]) +} + +/// Downgrade from v29: delete any persisted ProofEngine state. +pub fn downgrade_from_v29() -> Result, Error> { + info!("Downgrading from v29 (deleting ProofEngine state)"); + Ok(vec![KeyValueStoreOp::DeleteKey( + DBColumn::ProofEngine, + PROOF_ENGINE_DB_KEY.as_slice().to_vec(), + )]) +} diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index b6c235a4cb0..b73aa968e9d 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -804,12 +804,7 @@ where pub fn shutdown_reasons(&self) -> Vec { let mutex = self.shutdown_receiver.clone(); let mut receiver = mutex.lock(); - std::iter::from_fn(move || match receiver.try_next() { - Ok(Some(s)) => Some(s), - Ok(None) => panic!("shutdown sender dropped"), - Err(_) => None, - }) - .collect() + std::iter::from_fn(move || receiver.try_recv().ok()).collect() } pub fn get_current_state(&self) -> BeaconState { diff --git a/beacon_node/beacon_chain/tests/attestation_verification.rs b/beacon_node/beacon_chain/tests/attestation_verification.rs index 208798dfdfc..37990edbb34 100644 --- a/beacon_node/beacon_chain/tests/attestation_verification.rs +++ b/beacon_node/beacon_chain/tests/attestation_verification.rs @@ -1274,6 +1274,7 @@ async fn attestation_validator_receive_proposer_reward_and_withdrawals() { } #[tokio::test] +#[allow(clippy::result_large_err)] async fn attestation_to_finalized_block() { let harness = get_harness(VALIDATOR_COUNT); diff --git a/beacon_node/beacon_chain/tests/payload_invalidation.rs b/beacon_node/beacon_chain/tests/payload_invalidation.rs index 5bd43835e33..f9e69ee2e3a 100644 --- a/beacon_node/beacon_chain/tests/payload_invalidation.rs +++ b/beacon_node/beacon_chain/tests/payload_invalidation.rs @@ -1164,6 +1164,7 @@ async fn payload_preparation_before_transition_block() { } #[tokio::test] +#[allow(clippy::result_large_err)] async fn attesting_to_optimistic_head() { let mut rig = InvalidPayloadRig::new(); rig.move_to_terminal_block(); diff --git a/beacon_node/beacon_chain/tests/schema_stability.rs b/beacon_node/beacon_chain/tests/schema_stability.rs index db7f7dbdbbd..55df9b6a4b7 100644 --- a/beacon_node/beacon_chain/tests/schema_stability.rs +++ b/beacon_node/beacon_chain/tests/schema_stability.rs @@ -107,7 +107,7 @@ fn check_db_columns() { let expected_columns = vec![ "bma", "blk", "blb", "bdc", "bdi", "ste", "hsd", "hsn", "bsn", "bsd", "bss", "bs3", "bcs", "bst", "exp", "bch", "opo", "etc", "frk", "pkc", "brp", "bsx", "bsr", "bbx", "bbr", "bhr", - "brm", "dht", "cus", "otb", "bhs", "olc", "lcu", "scb", "scm", "dmy", + "brm", "dht", "cus", "otb", "bhs", "olc", "lcu", "scb", "scm", "dmy", "prf", ]; assert_eq!(expected_columns, current_columns); } diff --git a/beacon_node/beacon_chain/tests/store_tests.rs b/beacon_node/beacon_chain/tests/store_tests.rs index ba0621ae720..0876b961ad7 100644 --- a/beacon_node/beacon_chain/tests/store_tests.rs +++ b/beacon_node/beacon_chain/tests/store_tests.rs @@ -1119,6 +1119,7 @@ fn get_state_for_block(harness: &TestHarness, block_root: Hash256) -> BeaconStat } /// Check the invariants that apply to `shuffling_is_compatible`. +#[allow(clippy::result_large_err)] fn check_shuffling_compatible( harness: &TestHarness, head_state: &BeaconState, diff --git a/beacon_node/beacon_processor/src/lib.rs b/beacon_node/beacon_processor/src/lib.rs index 4efd5bdec18..109103234c4 100644 --- a/beacon_node/beacon_processor/src/lib.rs +++ b/beacon_node/beacon_processor/src/lib.rs @@ -1255,12 +1255,8 @@ impl BeaconProcessor { WorkType::BlobsByRootsRequest => work_queues.blob_broots_queue.len(), WorkType::DataColumnsByRootsRequest => work_queues.dcbroots_queue.len(), WorkType::DataColumnsByRangeRequest => work_queues.dcbrange_queue.len(), - WorkType::ExecutionProofsByRangeRequest => { - work_queues.epbrange_queue.len() - } - WorkType::ExecutionProofsByRootRequest => { - work_queues.epbroots_queue.len() - } + WorkType::ExecutionProofsByRangeRequest => work_queues.epbrange_queue.len(), + WorkType::ExecutionProofsByRootRequest => work_queues.epbroots_queue.len(), WorkType::GossipBlsToExecutionChange => { work_queues.gossip_bls_to_execution_change_queue.len() } diff --git a/beacon_node/execution_layer/Cargo.toml b/beacon_node/execution_layer/Cargo.toml index 7696fa9cb20..29c86d45ef0 100644 --- a/beacon_node/execution_layer/Cargo.toml +++ b/beacon_node/execution_layer/Cargo.toml @@ -10,6 +10,7 @@ alloy-primitives = { workspace = true } alloy-rlp = { workspace = true } alloy-rpc-types-eth = { workspace = true } arc-swap = "1.6.0" +async-stream = { workspace = true } async-trait = "0.1" bls = { workspace = true } builder_client = { path = "../builder_client" } @@ -17,8 +18,10 @@ bytes = { workspace = true } eth2 = { workspace = true, features = ["events", "lighthouse"] } ethereum_serde_utils = { workspace = true } ethereum_ssz = { workspace = true } +ethereum_ssz_derive = { workspace = true } fixed_bytes = { workspace = true } fork_choice = { workspace = true } +futures = { workspace = true } hash-db = "0.15.2" hash256-std-hasher = "0.15.2" hex = { workspace = true } @@ -33,6 +36,7 @@ parking_lot = { workspace = true } pretty_reqwest_error = { workspace = true } rand = { workspace = true } reqwest = { workspace = true } +reqwest-eventsource = { workspace = true } sensitive_url = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } @@ -40,6 +44,7 @@ sha2 = { workspace = true } slot_clock = { workspace = true } ssz_types = { workspace = true } state_processing = { workspace = true } +store = { workspace = true } strum = { workspace = true } superstruct = { workspace = true } task_executor = { workspace = true } diff --git a/beacon_node/execution_layer/src/eip8025/errors.rs b/beacon_node/execution_layer/src/eip8025/errors.rs index 72da1b651f0..aa3330f5ddb 100644 --- a/beacon_node/execution_layer/src/eip8025/errors.rs +++ b/beacon_node/execution_layer/src/eip8025/errors.rs @@ -25,6 +25,8 @@ pub enum ProofEngineError { SerdeError(serde_json::Error), /// SSZ error. SszError(ssz_types::Error), + /// SSE stream error. + SseError(String), /// The specified fork is not supported. ForkNotSupported(String), /// The execution engine does not support the requested proof type. @@ -113,6 +115,9 @@ impl fmt::Display for ProofEngineError { ProofEngineError::SszError(err) => { write!(f, "SSZ error: {}", err) } + ProofEngineError::SseError(msg) => { + write!(f, "SSE stream error: {}", msg) + } ProofEngineError::ForkNotSupported(fork) => { write!(f, "Fork not supported: {}", fork) } diff --git a/beacon_node/execution_layer/src/eip8025/mod.rs b/beacon_node/execution_layer/src/eip8025/mod.rs index 98b3f05bf93..28dcbd2c229 100644 --- a/beacon_node/execution_layer/src/eip8025/mod.rs +++ b/beacon_node/execution_layer/src/eip8025/mod.rs @@ -2,19 +2,26 @@ //! //! This module provides the execution layer integration for EIP-8025 optional proofs. //! It includes: -//! - Engine API methods for proof verification and generation -//! - ProofEngine trait for abstracting proof engine communication -//! - JSON structures for Engine API serialization +//! - HttpProofEngine combining transport with proof state management +//! - ProofNodeClient trait for low-level transport abstraction (REST+SSZ+SSE) +//! - HttpProofNodeClient for production HTTP transport +//! - SSE event types for proof completion streaming pub mod errors; -pub mod json_structures; +pub mod persisted_state; pub mod proof_engine; -mod state; +pub mod proof_node_client; +pub mod state; +#[cfg(test)] +mod tests; +pub mod types; pub use errors::ProofEngineError; -pub use json_structures::*; -pub use proof_engine::{ - ENGINE_REQUEST_PROOFS_V1, ENGINE_VERIFY_EXECUTION_PROOF_V1, - ENGINE_VERIFY_NEW_PAYLOAD_REQUEST_HEADER_V1, HttpProofEngine, PROOF_ENGINE_TIMEOUT, - ProofEngine, +pub use persisted_state::{PROOF_ENGINE_DB_KEY, PersistedProofEngineState}; +pub use proof_engine::HttpProofEngine; +pub use proof_node_client::{ + HttpProofNodeClient, PROOF_ENGINE_TIMEOUT, ProofNodeClient, ProofRequestResponse, +}; +pub use types::{ + ProofComplete, ProofEvent, ProofEventInfo, ProofFailure, ProofType, SseEventParts, }; diff --git a/beacon_node/execution_layer/src/eip8025/persisted_state.rs b/beacon_node/execution_layer/src/eip8025/persisted_state.rs new file mode 100644 index 00000000000..8491fb076e3 --- /dev/null +++ b/beacon_node/execution_layer/src/eip8025/persisted_state.rs @@ -0,0 +1,493 @@ +//! Persistent storage types for ProofEngine state (EIP-8025). +//! +//! These structs are the SSZ-serializable forms of the in-memory `State`, `TreeState`, +//! and `RequestBuffer`. HashMaps/HashSets are flattened to Vecs for SSZ compatibility. + +use super::state::{PayloadRequest, RequestBuffer, RequestMetadata, State, TreeState}; +use crate::ForkchoiceState; +use ssz::{Decode, Encode}; +use ssz_derive::{Decode, Encode}; +use std::collections::{BTreeMap, HashMap, HashSet}; +use store::{DBColumn, Error as StoreError, KeyValueStoreOp, StoreConfig, StoreItem}; +use types::{ExecutionBlockHash, Hash256, SignedExecutionProof}; + +/// Version field for future format migrations within the ProofEngine state. +pub const PROOF_ENGINE_STATE_VERSION: u64 = 1; + +/// Top-level persisted state for the ProofEngine. +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub struct PersistedProofEngineState { + /// Schema version for future migrations. + pub version: u64, + /// The last fork choice state marked as valid. + pub last_valid_fcs: ForkchoiceState, + /// The latest observed fork choice state. + pub latest_fcs: Option, + /// Persisted tree state. + pub tree: PersistedTreeState, + /// Persisted request buffer. + pub buffer: PersistedRequestBuffer, +} + +/// Persisted form of TreeState. HashMaps flattened to Vecs for SSZ. +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub struct PersistedTreeState { + pub proofs_by_block_hash: Vec, + pub request_root_to_block_hash: Vec, + pub parent_to_children: Vec, + pub block_number_to_block_hash: Vec, + pub current_canonical_head: ExecutionBlockHash, +} + +/// Flattened PayloadRequest: RequestMetadata + Vec. +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub struct PersistedBlockProofs { + pub block_hash: ExecutionBlockHash, + pub request_root: Hash256, + pub parent_hash: ExecutionBlockHash, + pub block_number: u64, + pub proofs: Vec, +} + +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub struct RequestRootMapping { + pub request_root: Hash256, + pub block_hash: ExecutionBlockHash, +} + +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub struct PersistedParentChildren { + pub parent: ExecutionBlockHash, + pub children: Vec, +} + +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub struct PersistedBlockNumberMapping { + pub block_number: u64, + pub block_hashes: Vec, +} + +#[derive(Clone, Debug, PartialEq, Encode, Decode)] +pub struct PersistedRequestBuffer { + pub requests: Vec, +} + +/// Fixed database key for the single `PersistedProofEngineState` record. +pub const PROOF_ENGINE_DB_KEY: Hash256 = Hash256::ZERO; + +impl StoreItem for PersistedProofEngineState { + fn db_column() -> DBColumn { + DBColumn::ProofEngine + } + + fn as_store_bytes(&self) -> Vec { + self.as_ssz_bytes() + } + + fn from_store_bytes(bytes: &[u8]) -> Result { + Self::from_ssz_bytes(bytes).map_err(Into::into) + } +} + +impl PersistedProofEngineState { + /// Decompress and decode from bytes using zstd (mirrors `PersistedForkChoiceV28::from_bytes`). + pub fn from_bytes(bytes: &[u8], store_config: &StoreConfig) -> Result { + let decompressed_bytes = store_config + .decompress_bytes(bytes) + .map_err(StoreError::Compression)?; + Self::from_ssz_bytes(&decompressed_bytes).map_err(Into::into) + } + + /// Encode and compress to bytes using zstd (mirrors `PersistedForkChoiceV28::as_bytes`). + pub fn as_bytes(&self, store_config: &StoreConfig) -> Result, StoreError> { + let ssz_bytes = self.as_ssz_bytes(); + store_config + .compress_bytes(&ssz_bytes) + .map_err(StoreError::Compression) + } + + /// Produce a compressed `KeyValueStoreOp` for atomic persistence. + pub fn as_kv_store_op( + &self, + key: Hash256, + store_config: &StoreConfig, + ) -> Result { + Ok(KeyValueStoreOp::PutKeyValue( + DBColumn::ProofEngine, + key.as_slice().to_vec(), + self.as_bytes(store_config)?, + )) + } + + pub fn from_state(state: &State) -> Self { + Self { + version: PROOF_ENGINE_STATE_VERSION, + last_valid_fcs: state.last_valid_fcs, + latest_fcs: state.latest_fcs, + tree: PersistedTreeState::from_tree(&state.tree), + buffer: PersistedRequestBuffer::from_buffer(&state.buffer), + } + } + + pub fn to_state(&self) -> State { + State { + latest_fcs: self.latest_fcs, + last_valid_fcs: self.last_valid_fcs, + tree: self.tree.to_tree(), + buffer: self.buffer.to_buffer(), + min_required_proofs: types::MIN_REQUIRED_EXECUTION_PROOFS, + } + } +} + +impl PersistedTreeState { + fn from_tree(tree: &TreeState) -> Self { + let proofs_by_block_hash = tree + .proofs_by_block_hash + .iter() + .map(|(block_hash, payload_req)| PersistedBlockProofs { + block_hash: *block_hash, + request_root: payload_req.metadata.request_root, + parent_hash: payload_req.metadata.parent_hash, + block_number: payload_req.metadata.block_number, + proofs: payload_req.proofs.clone(), + }) + .collect(); + + let request_root_to_block_hash = tree + .request_root_to_block_hash + .iter() + .map(|(root, hash)| RequestRootMapping { + request_root: *root, + block_hash: *hash, + }) + .collect(); + + let parent_to_children = tree + .parent_to_children + .iter() + .map(|(parent, children)| PersistedParentChildren { + parent: *parent, + children: children.iter().copied().collect(), + }) + .collect(); + + let block_number_to_block_hash = tree + .block_number_to_block_hash + .iter() + .map(|(num, hashes)| PersistedBlockNumberMapping { + block_number: *num, + block_hashes: hashes.iter().copied().collect(), + }) + .collect(); + + Self { + proofs_by_block_hash, + request_root_to_block_hash, + parent_to_children, + block_number_to_block_hash, + current_canonical_head: tree.current_canonical_head, + } + } + + fn to_tree(&self) -> TreeState { + let proofs_by_block_hash: HashMap = self + .proofs_by_block_hash + .iter() + .map(|p| { + ( + p.block_hash, + PayloadRequest { + metadata: RequestMetadata { + request_root: p.request_root, + block_hash: p.block_hash, + parent_hash: p.parent_hash, + block_number: p.block_number, + }, + proofs: p.proofs.clone(), + }, + ) + }) + .collect(); + + let request_root_to_block_hash: HashMap = self + .request_root_to_block_hash + .iter() + .map(|m| (m.request_root, m.block_hash)) + .collect(); + + let parent_to_children: HashMap> = self + .parent_to_children + .iter() + .map(|p| (p.parent, p.children.iter().copied().collect())) + .collect(); + + let block_number_to_block_hash: BTreeMap> = self + .block_number_to_block_hash + .iter() + .map(|m| (m.block_number, m.block_hashes.iter().copied().collect())) + .collect(); + + TreeState { + proofs_by_block_hash, + request_root_to_block_hash, + parent_to_children, + block_number_to_block_hash, + current_canonical_head: self.current_canonical_head, + } + } +} + +impl PersistedRequestBuffer { + fn from_buffer(buffer: &RequestBuffer) -> Self { + let requests = buffer + .proofs + .values() + .map(|payload_req| PersistedBlockProofs { + block_hash: payload_req.metadata.block_hash, + request_root: payload_req.metadata.request_root, + parent_hash: payload_req.metadata.parent_hash, + block_number: payload_req.metadata.block_number, + proofs: payload_req.proofs.clone(), + }) + .collect(); + Self { requests } + } + + fn to_buffer(&self) -> RequestBuffer { + let proofs: HashMap = self + .requests + .iter() + .map(|p| { + ( + p.request_root, + PayloadRequest { + metadata: RequestMetadata { + request_root: p.request_root, + block_hash: p.block_hash, + parent_hash: p.parent_hash, + block_number: p.block_number, + }, + proofs: p.proofs.clone(), + }, + ) + }) + .collect(); + RequestBuffer { proofs } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::eip8025::state::test_utils::*; + use store::StoreItem; + use types::MIN_REQUIRED_EXECUTION_PROOFS; + + /// Builds a fully-populated `State` with canonical chain, a fork (in buffer), and both FCS + /// fields set, then verifies a `from_state` → `to_state` round-trip preserves all data. + #[test] + fn test_state_serialization_round_trip() { + // 3-block canonical chain; fork from block 1 with 0 proofs so it stays in buffer, additional fork with 3 proofs so we have a promoted fork as well. + let fixture = TestStateFixtureBuilder::simple_chain() + .with_fork(1, 2, Some(0)) + .with_fork(1, 3, Some(3)) + .build(); + + let mut state = State::new(); + + // Populate canonical chain into tree and set latest_fcs via the empty-tree path. + fixture.bootstrap_canonical(&mut state).unwrap(); + + // Insert fork blocks into buffer (0 proofs → not promoted). + fixture.insert_fork(&mut state, 0, None).unwrap(); + + // Issue a valid forkchoice update so last_valid_fcs points into the tree. + let head = fixture.canonical_block_hash(2); + let safe = fixture.canonical_block_hash(1); + let finalized = fixture.canonical_block_hash(0); + state + .forkchoice_updated(create_forkchoice_state(head, safe, finalized)) + .unwrap(); + + // Sanity: both FCS fields should be populated. + assert!(state.latest_fcs.is_some()); + + // --- Round-trip --- + let persisted = PersistedProofEngineState::from_state(&state); + let restored = persisted.to_state(); + + // FCS fields. + assert_eq!(restored.last_valid_fcs, state.last_valid_fcs); + assert_eq!(restored.latest_fcs, state.latest_fcs); + + // min_required_proofs is not persisted — always restored to the constant. + assert_eq!(restored.min_required_proofs, MIN_REQUIRED_EXECUTION_PROOFS); + + // Tree: proofs_by_block_hash. + assert_eq!( + restored.tree.proofs_by_block_hash.len(), + state.tree.proofs_by_block_hash.len() + ); + for (hash, req) in &state.tree.proofs_by_block_hash { + let r = restored.tree.proofs_by_block_hash.get(hash).unwrap(); + assert_eq!(r.metadata.request_root, req.metadata.request_root); + assert_eq!(r.metadata.block_hash, req.metadata.block_hash); + assert_eq!(r.metadata.parent_hash, req.metadata.parent_hash); + assert_eq!(r.metadata.block_number, req.metadata.block_number); + assert_eq!(r.proofs, req.proofs); + } + + // Tree: request_root_to_block_hash. + assert_eq!( + restored.tree.request_root_to_block_hash, + state.tree.request_root_to_block_hash + ); + + // Tree: parent_to_children (HashSet equality via HashMap comparison). + assert_eq!( + restored.tree.parent_to_children, + state.tree.parent_to_children + ); + + // Tree: block_number_to_block_hash (BTreeMap). + assert_eq!( + restored.tree.block_number_to_block_hash, + state.tree.block_number_to_block_hash + ); + + // Tree: current_canonical_head. + assert_eq!( + restored.tree.current_canonical_head, + state.tree.current_canonical_head + ); + + // Buffer: entries match. + assert_eq!(restored.buffer.proofs.len(), state.buffer.proofs.len()); + for (root, req) in &state.buffer.proofs { + let r = restored.buffer.proofs.get(root).unwrap(); + assert_eq!(r.metadata.request_root, req.metadata.request_root); + assert_eq!(r.metadata.block_hash, req.metadata.block_hash); + assert_eq!(r.metadata.parent_hash, req.metadata.parent_hash); + assert_eq!(r.metadata.block_number, req.metadata.block_number); + assert_eq!(r.proofs, req.proofs); + } + } + + /// Encodes a `PersistedProofEngineState` via `StoreItem::as_store_bytes`, then decodes with + /// `StoreItem::from_store_bytes` and asserts all fields are equal. + #[test] + fn test_encode_decode_round_trip() { + let fixture = TestStateFixtureBuilder::simple_chain() + .with_fork(1, 2, Some(0)) + .with_fork(1, 3, Some(3)) + .build(); + + let mut state = State::new(); + fixture.bootstrap_canonical(&mut state).unwrap(); + fixture.insert_fork(&mut state, 0, None).unwrap(); + + let head = fixture.canonical_block_hash(2); + let safe = fixture.canonical_block_hash(1); + let finalized = fixture.canonical_block_hash(0); + state + .forkchoice_updated(create_forkchoice_state(head, safe, finalized)) + .unwrap(); + + let persisted = PersistedProofEngineState::from_state(&state); + + let bytes = persisted.as_store_bytes(); + let decoded = PersistedProofEngineState::from_store_bytes(&bytes).unwrap(); + + assert_eq!(decoded.version, persisted.version); + assert_eq!(decoded.last_valid_fcs, persisted.last_valid_fcs); + assert_eq!(decoded.latest_fcs, persisted.latest_fcs); + + assert_eq!( + decoded.tree.proofs_by_block_hash, + persisted.tree.proofs_by_block_hash + ); + assert_eq!( + decoded.tree.request_root_to_block_hash, + persisted.tree.request_root_to_block_hash + ); + + // Sort children within each parent entry for determinism. + assert_eq!( + decoded.tree.parent_to_children.len(), + persisted.tree.parent_to_children.len() + ); + let mut orig_ptc = persisted.tree.parent_to_children.clone(); + let mut dec_ptc = decoded.tree.parent_to_children.clone(); + orig_ptc.sort_by_key(|p| p.parent.into_root()); + dec_ptc.sort_by_key(|p| p.parent.into_root()); + for (o, d) in orig_ptc.iter().zip(dec_ptc.iter()) { + assert_eq!(o.parent, d.parent); + let mut oc = o.children.clone(); + let mut dc = d.children.clone(); + oc.sort_by_key(|h| h.into_root()); + dc.sort_by_key(|h| h.into_root()); + assert_eq!(oc, dc); + } + + assert_eq!( + decoded.tree.block_number_to_block_hash, + persisted.tree.block_number_to_block_hash + ); + assert_eq!( + decoded.tree.current_canonical_head, + persisted.tree.current_canonical_head + ); + assert_eq!(decoded.buffer.requests, persisted.buffer.requests); + } + + /// Verifies that compress → decompress round-trip via `as_bytes`/`from_bytes` + /// preserves all fields, and that compressed output is smaller than raw SSZ. + #[test] + fn test_compressed_round_trip() { + let fixture = TestStateFixtureBuilder::simple_chain() + .with_fork(1, 2, Some(0)) + .with_fork(1, 3, Some(3)) + .build(); + + let mut state = State::new(); + fixture.bootstrap_canonical(&mut state).unwrap(); + fixture.insert_fork(&mut state, 0, None).unwrap(); + + let head = fixture.canonical_block_hash(2); + let safe = fixture.canonical_block_hash(1); + let finalized = fixture.canonical_block_hash(0); + state + .forkchoice_updated(create_forkchoice_state(head, safe, finalized)) + .unwrap(); + + let persisted = PersistedProofEngineState::from_state(&state); + let store_config = StoreConfig::default(); + + // Compress. + let compressed = persisted.as_bytes(&store_config).unwrap(); + let raw_ssz = persisted.as_store_bytes(); + + // Compressed should differ from raw SSZ (zstd adds framing even if not smaller). + assert_ne!(compressed, raw_ssz); + + // Decompress and verify equality. + let decoded = PersistedProofEngineState::from_bytes(&compressed, &store_config).unwrap(); + assert_eq!(decoded.version, persisted.version); + assert_eq!(decoded.last_valid_fcs, persisted.last_valid_fcs); + assert_eq!(decoded.latest_fcs, persisted.latest_fcs); + assert_eq!( + decoded.tree.proofs_by_block_hash, + persisted.tree.proofs_by_block_hash + ); + assert_eq!( + decoded.tree.request_root_to_block_hash, + persisted.tree.request_root_to_block_hash + ); + assert_eq!( + decoded.tree.current_canonical_head, + persisted.tree.current_canonical_head + ); + assert_eq!(decoded.buffer.requests, persisted.buffer.requests); + } +} diff --git a/beacon_node/execution_layer/src/eip8025/proof_engine.rs b/beacon_node/execution_layer/src/eip8025/proof_engine.rs index 27cd355bd40..c24e02c4fdf 100644 --- a/beacon_node/execution_layer/src/eip8025/proof_engine.rs +++ b/beacon_node/execution_layer/src/eip8025/proof_engine.rs @@ -1,110 +1,39 @@ -//! ProofEngine trait and HTTP implementation for EIP-8025. +//! HTTP proof engine implementation for EIP-8025. //! -//! This module defines the interface for interacting with proof engines -//! and provides an HTTP JSON-RPC implementation with an internal proof cache. +//! Provides an HTTP implementation with an internal proof cache. +//! HTTP transport is delegated to a [`ProofNodeClient`] implementation. -use super::{errors::ProofEngineError, json_structures::*}; +use super::errors::ProofEngineError; +use super::persisted_state::PersistedProofEngineState; +use super::proof_node_client::{HttpProofNodeClient, ProofNodeClient}; +use super::types::ProofEvent; use crate::{ ForkchoiceState, ForkchoiceUpdatedResponse, MissingProofInfo, NewPayloadRequest, - NewPayloadRequestFulu, PayloadStatusV1, PayloadStatusV1Status, + PayloadStatusV1, PayloadStatusV1Status, eip8025::state::{RequestMetadata, State}, - json_structures::{JsonExecutionPayload, JsonRequestBody, JsonResponseBody}, }; +use bytes::Bytes; +use futures::stream::Stream; use parking_lot::RwLock; -use reqwest::Client; -use reqwest::header::CONTENT_TYPE; use sensitive_url::SensitiveUrl; -use serde::de::DeserializeOwned; -use serde_json::json; +use ssz::Encode; use std::collections::HashMap; +use std::pin::Pin; use std::time::Duration; -use types::execution::eip8025::{ProofAttributes, ProofGenId, ProofStatus, SignedExecutionProof}; +use types::execution::eip8025::{ProofAttributes, ProofStatus, SignedExecutionProof}; use types::{EthSpec, Hash256}; -/// Static ID for JSON-RPC requests. -const STATIC_ID: u32 = 1; +// ─── HttpProofEngine ───────────────────────────────────────────────────────── -/// JSON-RPC version string. -pub const JSONRPC_VERSION: &str = "2.0"; - -/// This error is returned during a `chainId` call by Geth. -pub const EIP155_ERROR_STR: &str = "chain not synced beyond EIP-155 replay-protection fork block"; - -/// Engine API method for verifying execution proofs. -pub const ENGINE_VERIFY_EXECUTION_PROOF_V1: &str = "engine_verifyExecutionProofV1"; - -/// Engine API method for verifying new payload request headers. +/// Proof engine with internal proof storage. /// -/// This is currently unused but defined for completeness. We may use it in the future -pub const ENGINE_VERIFY_NEW_PAYLOAD_REQUEST_HEADER_V1: &str = - "engine_verifyNewPayloadRequestHeaderV1"; - -/// Engine API method for requesting proof generation. -pub const ENGINE_REQUEST_PROOFS_V1: &str = "engine_requestProofsV1"; - -/// Default timeout for proof engine requests (1 second per spec). -pub const PROOF_ENGINE_TIMEOUT: Duration = Duration::from_secs(1); - -/// Trait defining the interface for a proof engine. -#[async_trait::async_trait] -pub trait ProofEngine: Send + Sync { - /// Get all proofs for a given new_payload_request_root. - fn get_proofs_by_root(&self, root: &Hash256) -> Vec; - - /// Return all buffer entries that do not yet have sufficient proofs for promotion. - /// - /// `MissingProofInfo.root` is populated with the new-payload request root. - /// The beacon chain layer replaces it with the beacon block root before the - /// sync manager issues `ExecutionProofsByRoot` RPC requests. - fn missing_proofs(&self) -> Vec; - - /// Verify an individual execution proof via RPC. - /// - /// Maps to `engine_verifyExecutionProofV1`. - async fn verify_execution_proof( - &self, - proof: &SignedExecutionProof, - ) -> Result; - - /// Verify that sufficient proofs exist for a new payload request via RPC. - /// - /// Maps to `engine_verifyNewPayloadRequestHeaderV*`. - async fn new_payload( - &self, - header: &NewPayloadRequest<'_, E>, - ) -> Result; - - /// Notify the proof engine of a forkchoice update. - async fn forkchoice_updated( - &self, - forkchoice_state: ForkchoiceState, - ) -> Result; - - /// Request asynchronous proof generation via RPC. - /// - /// Maps to `engine_requestProofsV1`. - /// Returns a ProofGenId to track the generation request. - /// Generated proofs are delivered asynchronously via the beacon API endpoint - /// POST /eth/v1/prover/execution_proofs. - async fn request_proofs( - &self, - new_payload_request: NewPayloadRequest<'_, E>, - attributes: ProofAttributes, - ) -> Result; -} - -/// HTTP JSON-RPC implementation of the ProofEngine trait with internal proof storage. -/// -/// This implementation: /// - Stores ALL unfinalized proofs indexed by new_payload_request_root (unbounded) -/// - Calls out to the execution engine RPC for proof verification +/// - Delegates transport to a [`ProofNodeClient`] implementation /// - Prunes proofs when finalization events occur pub struct HttpProofEngine { - /// HTTP client for making requests. - client: Client, - /// URL of the proof engine endpoint. - url: SensitiveUrl, + /// Transport client for proof engine REST+SSZ+SSE API. + proof_node: Box, /// The internal state storing execution proofs in a tree structure and buffer. state: RwLock, /// Buffered proofs for request roots not yet seen. @@ -112,63 +41,54 @@ pub struct HttpProofEngine { } impl HttpProofEngine { - /// Create a new HTTP proof engine client with internal proof storage. + /// Create a new proof engine backed by the HTTP proof node client. pub fn new(url: SensitiveUrl, timeout: Option) -> Self { - let client = Client::builder() - .timeout(timeout.unwrap_or(PROOF_ENGINE_TIMEOUT)) - .build() - .expect("Failed to build HTTP client"); + Self::with_proof_node(HttpProofNodeClient::new(url, timeout)) + } + /// Create a proof engine backed by a custom [`ProofNodeClient`] implementation. + /// + /// Useful for injecting a [`MockProofNodeClient`] in tests. + /// + /// [`MockProofNodeClient`]: super::super::test_utils::MockProofNodeClient + pub fn with_proof_node(proof_node: impl ProofNodeClient + 'static) -> Self { Self { - client, - url, + proof_node: Box::new(proof_node), state: RwLock::new(State::new()), buffered_proofs: RwLock::new(HashMap::new()), } } - /// Make a generic JSON-RPC request to the proof engine. - pub async fn rpc_request( + /// Subscribe to method-invocation events emitted by a mock proof node client. + /// + /// Returns `None` for production (HTTP) clients. + pub fn subscribe_client_events( &self, - method: &str, - params: serde_json::Value, - timeout: Duration, - ) -> Result { - let body = JsonRequestBody { - jsonrpc: JSONRPC_VERSION, - method, - params, - id: json!(STATIC_ID), - }; - - let request = self - .client - .post(self.url.expose_full().clone()) - .timeout(timeout) - .header(CONTENT_TYPE, "application/json") - .json(&body); - - // TODO: do we want to support authentication? - // Generate and add a jwt token to the header if auth is defined. - // if let Some(auth) = &self.auth { - // request = request.bearer_auth(auth.generate_token()?); - // }; + ) -> Option> { + self.proof_node.subscribe_client_events() + } - let body: JsonResponseBody = request.send().await?.error_for_status()?.json().await?; + /// Subscribe to SSE proof events from the proof engine. + pub fn subscribe_proof_events( + &self, + filter_root: Option, + ) -> Pin> + Send + '_>> { + self.proof_node.subscribe_proof_events(filter_root) + } - match (body.result, body.error) { - (result, None) => Ok(serde_json::from_value(result)?), - (_, Some(error)) => Err(ProofEngineError::JsonRpcError { - code: error.code, - message: error.message, - }), - } + /// Download a completed execution proof by proof type. + pub async fn get_proof( + &self, + new_payload_request_root: Hash256, + proof_type: u8, + ) -> Result { + self.proof_node + .get_proof(new_payload_request_root, proof_type) + .await } -} -#[async_trait::async_trait] -impl ProofEngine for HttpProofEngine { - fn get_proofs_by_root(&self, root: &Hash256) -> Vec { + /// Get all proofs for a given new_payload_request_root. + pub fn get_proofs_by_root(&self, root: &Hash256) -> Vec { self.state .read() .get_proofs(root) @@ -176,11 +96,17 @@ impl ProofEngine for HttpProofEngine { .unwrap_or_default() } - fn missing_proofs(&self) -> Vec { + /// Return all buffer entries that do not yet have sufficient proofs for promotion. + /// + /// `MissingProofInfo.root` is populated with the new-payload request root. + /// The beacon chain layer replaces it with the beacon block root before the + /// sync manager issues `ExecutionProofsByRoot` RPC requests. + pub fn missing_proofs(&self) -> Vec { self.state.read().missing_proofs() } - async fn verify_execution_proof( + /// Verify an individual execution proof via the proof engine. + pub async fn verify_execution_proof( &self, proof: &SignedExecutionProof, ) -> Result { @@ -198,33 +124,23 @@ impl ProofEngine for HttpProofEngine { return Ok(ProofStatus::Syncing); } - let json_proof: JsonExecutionProofV1 = proof.message.clone().into(); - let params = json!([json_proof]); - - let result = self - .rpc_request( - ENGINE_VERIFY_EXECUTION_PROOF_V1, - params, - PROOF_ENGINE_TIMEOUT, - ) + let status = self + .proof_node + .verify_proof(proof.request_root(), proof.proof_type(), proof.proof_data()) .await?; - let status: JsonProofStatusV1 = serde_json::from_value(result)?; - let status: ProofStatus = status.into(); if status.is_valid() { - // Insert the valid proof into state. return Ok(self.state.write().insert_proof(proof.clone())?); } Ok(status) } - async fn new_payload( + /// Buffer a new payload request for proof association. + pub async fn new_payload( &self, request: &NewPayloadRequest<'_, E>, ) -> Result { - // We buffer the request in state for future proof association and return Syncing. - // TODO: Currently we don't support proof verification before payload processing to prevent DOS so its not possible that proofs are verified yet. Is this reasonable? let request: RequestMetadata = request.into(); let buffered_proofs = self .buffered_proofs @@ -248,7 +164,8 @@ impl ProofEngine for HttpProofEngine { }) } - async fn forkchoice_updated( + /// Notify the proof engine of a forkchoice update. + pub async fn forkchoice_updated( &self, forkchoice_state: ForkchoiceState, ) -> Result { @@ -256,11 +173,15 @@ impl ProofEngine for HttpProofEngine { Ok(self.state.write().forkchoice_updated(forkchoice_state)?) } - async fn request_proofs( + /// Request proof generation from the proof engine. + /// + /// SSZ-encodes the payload then sends it to `POST /v1/execution_proof_requests`. + /// Returns the `new_payload_request_root` identifying this request. + pub async fn request_proofs( &self, new_payload_request: NewPayloadRequest<'_, E>, proof_attributes: ProofAttributes, - ) -> Result { + ) -> Result { match new_payload_request { NewPayloadRequest::Bellatrix(_) => { Err(ProofEngineError::ForkNotSupported("Bellatrix".to_string())) @@ -274,8 +195,9 @@ impl ProofEngine for HttpProofEngine { NewPayloadRequest::Electra(_) => { Err(ProofEngineError::ForkNotSupported("Electra".to_string())) } - NewPayloadRequest::Fulu(new_payload_request_fulu) => { - self.request_proofs_v4_fulu(new_payload_request_fulu, proof_attributes) + NewPayloadRequest::Fulu(fulu) => { + self.proof_node + .request_proofs(fulu.as_ssz_bytes(), proof_attributes) .await } NewPayloadRequest::Gloas(_) => { @@ -283,33 +205,16 @@ impl ProofEngine for HttpProofEngine { } } } -} -impl HttpProofEngine { - pub async fn request_proofs_v4_fulu( - &self, - new_payload_request_fulu: NewPayloadRequestFulu<'_, E>, - proof_attributes: ProofAttributes, - ) -> Result { - let params = json!([ - JsonExecutionPayload::Fulu( - new_payload_request_fulu - .execution_payload - .clone() - .try_into()? - ), - new_payload_request_fulu.versioned_hashes, - new_payload_request_fulu.parent_beacon_block_root, - new_payload_request_fulu - .execution_requests - .get_execution_requests_list(), - proof_attributes - ]); - - let response: TransparentJsonProofGenId = self - .rpc_request(ENGINE_REQUEST_PROOFS_V1, params, PROOF_ENGINE_TIMEOUT) - .await?; + /// Snapshot the current state into a persisted form for serialization. + pub fn to_persisted(&self) -> PersistedProofEngineState { + let state = self.state.read(); + PersistedProofEngineState::from_state(&state) + } - Ok(response.into()) + /// Restore in-memory state from a previously persisted snapshot. + pub fn restore_from_persisted(&self, persisted: PersistedProofEngineState) { + let restored = persisted.to_state(); + *self.state.write() = restored; } } diff --git a/beacon_node/execution_layer/src/eip8025/proof_node_client.rs b/beacon_node/execution_layer/src/eip8025/proof_node_client.rs new file mode 100644 index 00000000000..25af9895479 --- /dev/null +++ b/beacon_node/execution_layer/src/eip8025/proof_node_client.rs @@ -0,0 +1,255 @@ +//! Proof node client trait and HTTP implementation for EIP-8025. +//! +//! [`ProofNodeClient`] abstracts over the transport used to communicate with the +//! proof engine. [`HttpProofNodeClient`] is the production implementation. + +use super::errors::ProofEngineError; +use bytes::Bytes; +use futures::stream::Stream; +use reqwest::Client; +use reqwest_eventsource::{Event, EventSource}; +use sensitive_url::SensitiveUrl; +use std::pin::Pin; +use std::time::Duration; +use tokio_stream::StreamExt; + +use super::types::{ProofEvent, ProofType, SseEventParts}; +use types::Hash256; +use types::execution::eip8025::{ProofAttributes, ProofStatus}; + +/// Default timeout for proof engine requests (1 second per spec). +pub const PROOF_ENGINE_TIMEOUT: Duration = Duration::from_secs(1); + +const PATH_PROOF_REQUESTS: &str = "/v1/execution_proof_requests"; +const PATH_PROOF_VERIFICATIONS: &str = "/v1/execution_proof_verifications"; +const PATH_PROOFS: &str = "/v1/execution_proofs"; + +const QUERY_PROOF_TYPES: &str = "proof_types"; +const QUERY_NEW_PAYLOAD_REQUEST_ROOT: &str = "new_payload_request_root"; +const QUERY_PROOF_TYPE: &str = "proof_type"; + +const HEADER_CONTENT_TYPE: &str = "content-type"; +const HEADER_VALUE_SSZ: &str = "application/octet-stream"; + +// ─── ProofNodeClient Trait ─────────────────────────────────────────────────── + +/// Transport abstraction for communicating with a proof engine. +/// +/// The SSZ encoding of the payload is done by the caller ([`HttpProofEngine`]) +/// before invoking [`request_proofs`], so implementations receive raw bytes and +/// do not need to be generic over [`EthSpec`]. +/// +/// [`HttpProofEngine`]: super::proof_engine::HttpProofEngine +/// [`request_proofs`]: ProofNodeClient::request_proofs +/// [`EthSpec`]: types::EthSpec +#[async_trait::async_trait] +pub trait ProofNodeClient: Send + Sync { + /// Submit an SSZ-encoded `NewPayloadRequest` to the proof engine. + /// + /// Returns the `new_payload_request_root` assigned by the proof engine. + async fn request_proofs( + &self, + ssz_body: Vec, + proof_attributes: ProofAttributes, + ) -> Result; + + /// Verify a single proof via the proof engine. + async fn verify_proof( + &self, + root: Hash256, + proof_type: u8, + proof_data: &[u8], + ) -> Result; + + /// Download a completed proof by root and proof type. + async fn get_proof(&self, root: Hash256, proof_type: u8) -> Result; + + /// Subscribe to SSE proof events from the proof engine. + /// + /// When `filter_root` is provided, only events for that root are yielded. + fn subscribe_proof_events( + &self, + filter_root: Option, + ) -> Pin> + Send + '_>>; + + /// Subscribe to method-invocation events emitted by a mock client. + /// + /// Returns `None` for production clients; overridden in [`MockProofNodeClient`] to + /// expose its internal broadcast channel for test assertions. + /// + /// [`MockProofNodeClient`]: crate::test_utils::MockProofNodeClient + fn subscribe_client_events( + &self, + ) -> Option> { + None + } +} + +// ─── REST API Response Types ───────────────────────────────────────────────── + +/// Response for `POST /v1/execution_proof_requests`. +#[derive(Debug, Clone, serde::Deserialize)] +pub struct ProofRequestResponse { + pub new_payload_request_root: Hash256, +} + +/// Response for `POST /v1/execution_proof_verifications`. +#[derive(Debug, Clone, serde::Deserialize)] +struct ProofVerificationResponse { + status: ProofVerificationStatus, +} + +#[derive(Debug, Clone, serde::Deserialize)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +enum ProofVerificationStatus { + Valid, + Invalid, +} + +// ─── HttpProofNodeClient ───────────────────────────────────────────────────── + +/// HTTP implementation of [`ProofNodeClient`]. +/// +/// Handles all network I/O — SSZ body transport, HTTP requests, SSE streams. +/// No proof state management; that stays in [`HttpProofEngine`]. +/// +/// [`HttpProofEngine`]: super::proof_engine::HttpProofEngine +pub struct HttpProofNodeClient { + client: Client, + url: SensitiveUrl, +} + +impl HttpProofNodeClient { + /// Create a new HTTP proof node client. + pub fn new(url: SensitiveUrl, timeout: Option) -> Self { + let client = Client::builder() + .timeout(timeout.unwrap_or(PROOF_ENGINE_TIMEOUT)) + .build() + .expect("Failed to build HTTP client"); + + Self { client, url } + } + + /// Build a URL from the base URL and a path. + fn url(&self, path: &str) -> reqwest::Url { + let mut url = self.url.expose_full().clone(); + url.set_path(path); + url + } +} + +#[async_trait::async_trait] +impl ProofNodeClient for HttpProofNodeClient { + /// `POST /v1/execution_proof_requests?proof_types=reth-sp1,ethrex-risc0` + /// + /// Converts EIP-8025 `u8` proof types to string identifiers for the wire + /// format. + async fn request_proofs( + &self, + ssz_body: Vec, + proof_attributes: ProofAttributes, + ) -> Result { + // Convert u8 proof types to string identifiers. + // proof node expects: `proof_types=reth-sp1,ethrex-risc0` + let proof_types_csv = proof_attributes + .proof_types + .iter() + .map(|t| ProofType::from_u8(*t).map(|pt| pt.as_str().to_string())) + .collect::, _>>()? + .join(","); + + let response: ProofRequestResponse = self + .client + .post(self.url(PATH_PROOF_REQUESTS)) + .query(&[(QUERY_PROOF_TYPES, &proof_types_csv)]) + .header(HEADER_CONTENT_TYPE, HEADER_VALUE_SSZ) + .body(ssz_body) + .send() + .await? + .error_for_status()? + .json() + .await?; + + Ok(response.new_payload_request_root) + } + + /// `POST /v1/execution_proof_verifications?new_payload_request_root=...&proof_type=reth-sp1` + /// + /// Converts the `u8` proof type to a string identifier for the query param. + async fn verify_proof( + &self, + root: Hash256, + proof_type: u8, + proof_data: &[u8], + ) -> Result { + let proof_type_str = ProofType::from_u8(proof_type)?; + let response: ProofVerificationResponse = self + .client + .post(self.url(PATH_PROOF_VERIFICATIONS)) + .query(&[ + (QUERY_NEW_PAYLOAD_REQUEST_ROOT, &root.to_string()), + (QUERY_PROOF_TYPE, &proof_type_str.to_string()), + ]) + .header(HEADER_CONTENT_TYPE, HEADER_VALUE_SSZ) + .body(proof_data.to_vec()) + .send() + .await? + .error_for_status()? + .json() + .await?; + + match response.status { + ProofVerificationStatus::Valid => Ok(ProofStatus::Valid), + ProofVerificationStatus::Invalid => Ok(ProofStatus::Invalid), + } + } + + /// `GET /v1/execution_proofs/{root}/{proof_type}` + /// + /// Uses string identifier in the URL path (e.g. `/reth-sp1`). + async fn get_proof(&self, root: Hash256, proof_type: u8) -> Result { + let proof_type_str = ProofType::from_u8(proof_type)?; + Ok(self + .client + .get(self.url(&format!("{PATH_PROOFS}/{root}/{proof_type_str}"))) + .send() + .await? + .error_for_status()? + .bytes() + .await?) + } + + /// Opens `GET /v1/execution_proof_requests` as an SSE stream. + fn subscribe_proof_events( + &self, + filter_root: Option, + ) -> Pin> + Send + '_>> { + let client = self.client.clone(); + let url = self.url(PATH_PROOF_REQUESTS); + + Box::pin(async_stream::try_stream! { + let builder = if let Some(root) = filter_root { + client.get(url).query(&[(QUERY_NEW_PAYLOAD_REQUEST_ROOT, &root.to_string())]) + } else { + client.get(url) + }; + let mut es = EventSource::new(builder) + .map_err(|e| ProofEngineError::SseError( + format!("failed to create event source: {e}") + ))?; + + while let Some(event) = es.next().await { + match event { + Ok(Event::Open) => {} + Ok(Event::Message(message)) => { + yield ProofEvent::try_from(SseEventParts(&message.event, &message.data))?; + } + Err(error) => { + es.close(); + Err(ProofEngineError::SseError(error.to_string()))?; + } + } + } + }) + } +} diff --git a/beacon_node/execution_layer/src/eip8025/state.rs b/beacon_node/execution_layer/src/eip8025/state.rs index 0f21a8a96a3..509a502f79a 100644 --- a/beacon_node/execution_layer/src/eip8025/state.rs +++ b/beacon_node/execution_layer/src/eip8025/state.rs @@ -28,9 +28,9 @@ pub struct State { pub min_required_proofs: usize, } -impl State { - /// Create a new State with the specified proof buffer size. - pub fn new() -> Self { +impl Default for State { + /// Create a new State with default min required proofs. + fn default() -> Self { Self { latest_fcs: None, last_valid_fcs: ForkchoiceState { @@ -39,10 +39,17 @@ impl State { finalized_block_hash: ExecutionBlockHash::zero(), }, tree: TreeState::default(), - buffer: RequestBuffer::new(), + buffer: RequestBuffer::default(), min_required_proofs: MIN_REQUIRED_EXECUTION_PROOFS, } } +} + +impl State { + /// Create a new State with default values. + pub fn new() -> Self { + Self::default() + } /// Return all buffer entries that do not yet have sufficient proofs for promotion. /// @@ -526,7 +533,7 @@ impl State { finalized_block_hash: ExecutionBlockHash::zero(), }, tree: TreeState::default(), - buffer: RequestBuffer::new(), + buffer: RequestBuffer::default(), min_required_proofs, } } @@ -557,7 +564,7 @@ impl TreeState { } /// A buffer of new payload requests and their associated execution proofs. -#[derive(Debug, Clone)] +#[derive(Debug, Default, Clone)] pub struct RequestBuffer { /// Map of new payload request root to execution proofs. pub proofs: HashMap, @@ -603,15 +610,6 @@ pub struct RequestMetadata { pub block_number: u64, } -impl RequestBuffer { - /// Create a new ProofBuffer with the specified maximum size. - pub fn new() -> Self { - Self { - proofs: Default::default(), - } - } -} - impl From<&NewPayloadRequest<'_, E>> for RequestMetadata { fn from(request: &NewPayloadRequest<'_, E>) -> Self { Self { @@ -624,21 +622,21 @@ impl From<&NewPayloadRequest<'_, E>> for RequestMetadata { } #[cfg(test)] -mod tests { +pub mod test_utils { use super::*; use bls::SignatureBytes; use ssz_types::VariableList; use types::{ExecutionProof, PublicInput}; - fn test_hash(byte: u8) -> Hash256 { + pub fn test_hash(byte: u8) -> Hash256 { Hash256::repeat_byte(byte) } - fn test_exec_hash(byte: u8) -> ExecutionBlockHash { + pub fn test_exec_hash(byte: u8) -> ExecutionBlockHash { ExecutionBlockHash::repeat_byte(byte) } - fn create_request_metadata( + pub fn create_request_metadata( request_root: Hash256, block_hash: ExecutionBlockHash, parent_hash: ExecutionBlockHash, @@ -652,7 +650,10 @@ mod tests { } } - fn create_signed_proof(request_root: Hash256, validator_index: u64) -> SignedExecutionProof { + pub fn create_signed_proof( + request_root: Hash256, + validator_index: u64, + ) -> SignedExecutionProof { SignedExecutionProof { message: ExecutionProof { proof_data: VariableList::new(vec![0xaa, 0xbb, 0xcc]).unwrap(), @@ -666,7 +667,7 @@ mod tests { } } - fn create_forkchoice_state( + pub fn create_forkchoice_state( head: ExecutionBlockHash, safe: ExecutionBlockHash, finalized: ExecutionBlockHash, @@ -681,20 +682,20 @@ mod tests { /// Test data provider for state tests /// /// Generates payload requests, proofs, and hashes. - struct TestStateFixture { + pub struct TestStateFixture { /// Generated block data /// blocks[0] = canonical chain /// blocks[1] = fork 0 /// blocks[2] = fork 1 /// etc. - blocks: Vec>, + pub blocks: Vec>, } impl TestStateFixture { /// Get the genesis fcs /// /// Defined as the first block in the canonical chain - fn genesis_fcs(&self) -> ForkchoiceState { + pub fn genesis_fcs(&self) -> ForkchoiceState { let finalized_block = &self.blocks[0][0]; create_forkchoice_state( finalized_block.metadata.block_hash, @@ -704,58 +705,58 @@ mod tests { } /// Get canonical chain block data - fn canonical(&self, index: usize) -> &PayloadRequest { + pub fn canonical(&self, index: usize) -> &PayloadRequest { &self.blocks[0][index] } /// Get fork block data - fn fork(&self, fork_id: usize, index: usize) -> &PayloadRequest { + pub fn fork(&self, fork_id: usize, index: usize) -> &PayloadRequest { &self.blocks[fork_id + 1][index] } /// Get canonical block hash - fn canonical_block_hash(&self, index: usize) -> ExecutionBlockHash { + pub fn canonical_block_hash(&self, index: usize) -> ExecutionBlockHash { self.canonical(index).metadata.block_hash } /// Get fork block hash - fn fork_block_hash(&self, fork_id: usize, index: usize) -> ExecutionBlockHash { + pub fn fork_block_hash(&self, fork_id: usize, index: usize) -> ExecutionBlockHash { self.fork(fork_id, index).metadata.block_hash } /// Get canonical request root - fn canonical_request_root(&self, index: usize) -> Hash256 { + pub fn canonical_request_root(&self, index: usize) -> Hash256 { self.canonical(index).metadata.request_root } /// Get canonical metadata - fn canonical_metadata(&self, index: usize) -> RequestMetadata { + pub fn canonical_metadata(&self, index: usize) -> RequestMetadata { self.canonical(index).metadata.clone() } /// Get fork metadata - fn fork_metadata(&self, fork_id: usize, index: usize) -> RequestMetadata { + pub fn fork_metadata(&self, fork_id: usize, index: usize) -> RequestMetadata { self.fork(fork_id, index).metadata.clone() } /// Get canonical proofs - fn canonical_proofs(&self, index: usize) -> &[SignedExecutionProof] { + pub fn canonical_proofs(&self, index: usize) -> &[SignedExecutionProof] { &self.canonical(index).proofs } /// Get fork proofs - fn fork_proofs(&self, fork_id: usize, index: usize) -> &[SignedExecutionProof] { + pub fn fork_proofs(&self, fork_id: usize, index: usize) -> &[SignedExecutionProof] { &self.fork(fork_id, index).proofs } - fn bootstrap_canonical(&self, state: &mut State) -> anyhow::Result<()> { + pub fn bootstrap_canonical(&self, state: &mut State) -> anyhow::Result<()> { state.forkchoice_updated(self.genesis_fcs())?; self.insert_canonical(state, None)?; Ok(()) } /// Insert the canonical chain into state (buffer + add proofs) - fn insert_canonical( + pub fn insert_canonical( &self, state: &mut State, block_index: Option, @@ -774,7 +775,7 @@ mod tests { } /// Insert a fork into state (buffer + add proofs) - fn insert_fork( + pub fn insert_fork( &self, state: &mut State, fork_id: usize, @@ -796,23 +797,29 @@ mod tests { } /// Builder for test state fixture - struct TestStateFixtureBuilder { + pub struct TestStateFixtureBuilder { /// Number of blocks in canonical chain - canonical_chain_length: usize, + pub canonical_chain_length: usize, /// Fork configurations (branch_point, fork_length, proofs_per_block) - forks: Vec<(usize, usize, Option)>, + pub forks: Vec<(usize, usize, Option)>, /// Default proofs per block - proofs_per_block: usize, + pub proofs_per_block: usize, /// Starting block number - starting_block_number: u64, + pub starting_block_number: u64, + } + + impl Default for TestStateFixtureBuilder { + fn default() -> Self { + Self::new() + } } impl TestStateFixtureBuilder { /// Create new builder - fn new() -> Self { + pub fn new() -> Self { Self { canonical_chain_length: 0, forks: Vec::new(), @@ -822,24 +829,24 @@ mod tests { } /// Create a simple chain with 3 blocks in the canonical chain - fn simple_chain() -> Self { + pub fn simple_chain() -> Self { Self::new().with_canonical_chain(3) } /// Set default proofs per block - fn with_proofs_per_block(mut self, proofs: usize) -> Self { + pub fn with_proofs_per_block(mut self, proofs: usize) -> Self { self.proofs_per_block = proofs; self } /// Set canonical chain length - fn with_canonical_chain(mut self, length: usize) -> Self { + pub fn with_canonical_chain(mut self, length: usize) -> Self { self.canonical_chain_length = length; self } /// Add a fork (uses default proofs per block) - fn with_fork( + pub fn with_fork( mut self, branch_point: usize, fork_length: usize, @@ -851,7 +858,7 @@ mod tests { } /// Build the fixture - fn build(self) -> TestStateFixture { + pub fn build(self) -> TestStateFixture { let mut fixture = TestStateFixture { blocks: vec![Vec::new()], // Start with empty canonical chain }; @@ -913,7 +920,7 @@ mod tests { } /// Generate data for a single block - fn generate_block( + pub fn generate_block( &self, chain_id: usize, block_index: usize, @@ -941,6 +948,12 @@ mod tests { PayloadRequest { metadata, proofs } } } +} // end test_utils + +#[cfg(test)] +mod tests { + use super::test_utils::*; + use super::*; #[test] fn test_buffer_request_new() { diff --git a/beacon_node/execution_layer/src/eip8025/tests.rs b/beacon_node/execution_layer/src/eip8025/tests.rs new file mode 100644 index 00000000000..882d33dea34 --- /dev/null +++ b/beacon_node/execution_layer/src/eip8025/tests.rs @@ -0,0 +1,287 @@ +//! Unit tests for [`HttpProofEngine`] using [`MockProofNodeClient`]. + +use crate::eip8025::proof_engine::HttpProofEngine; +use crate::eip8025::proof_node_client::ProofNodeClient; +use crate::test_utils::{MockClientEvent, MockProofNodeClient, make_test_fulu_ssz}; +use bls::{FixedBytesExtended, SignatureBytes}; +use futures::StreamExt; +use tokio::time::{Duration, timeout}; +use types::Hash256; +use types::execution::eip8025::{ + ExecutionProof, ProofAttributes, PublicInput, SignedExecutionProof, +}; + +// ─── helpers ───────────────────────────────────────────────────────────────── + +fn make_proof(request_root: Hash256, proof_type: u8) -> SignedExecutionProof { + SignedExecutionProof { + message: ExecutionProof { + proof_data: Default::default(), + proof_type, + public_input: PublicInput { + new_payload_request_root: request_root, + }, + }, + validator_index: 0, + signature: SignatureBytes::empty(), + } +} + +/// Receive the next [`MockClientEvent`] within 2 seconds. +async fn next_event(rx: &mut tokio::sync::broadcast::Receiver) -> MockClientEvent { + timeout(Duration::from_secs(2), rx.recv()) + .await + .expect("timed out waiting for MockClientEvent") + .expect("channel closed") +} + +// ─── MockProofNodeClient tests ──────────────────────────────────────────────── + +/// `request_proofs` decodes SSZ, records the body, and emits `ProofRequested`. +#[tokio::test] +async fn mock_client_request_proofs_emits_event() { + let mock = MockProofNodeClient::new(0); + let mut rx = mock.subscribe_client_events(); + + let (body, expected_root) = make_test_fulu_ssz(Hash256::repeat_byte(0xAA)); + let attrs = ProofAttributes { + proof_types: vec![1, 2], + }; + + let root = mock + .request_proofs(body.clone(), attrs.clone()) + .await + .expect("request_proofs should succeed"); + + assert_eq!(root, expected_root); + assert_eq!(mock.request_count(), 1); + + let event = next_event(&mut rx).await; + assert!(matches!( + event, + MockClientEvent::ProofRequested { ssz_body, proof_attributes, root: r } + if r == root && ssz_body == body && proof_attributes == attrs + )); +} + +/// `verify_proof` emits `ProofVerified`. +#[tokio::test] +async fn mock_client_verify_proof_emits_event() { + let mock = MockProofNodeClient::new(0); + let mut rx = mock.subscribe_client_events(); + + let root = Hash256::repeat_byte(0xBB); + let _ = mock.verify_proof(root, 1, &[]).await.unwrap(); + + let event = next_event(&mut rx).await; + assert!(matches!( + event, + MockClientEvent::ProofVerified { root: r, proof_type: 1 } if r == root + )); +} + +/// `get_proof` emits `ProofFetched`. +#[tokio::test] +async fn mock_client_get_proof_emits_event() { + let mock = MockProofNodeClient::new(0); + let mut rx = mock.subscribe_client_events(); + + let root = Hash256::repeat_byte(0xCC); + let _ = mock.get_proof(root, 2).await.unwrap(); + + let event = next_event(&mut rx).await; + assert!(matches!( + event, + MockClientEvent::ProofFetched { root: r, proof_type: 2 } if r == root + )); +} + +/// `request_proofs` broadcasts a `ProofComplete` SSE event for each proof type. +#[tokio::test] +async fn mock_client_request_proofs_broadcasts_sse_events() { + let mock = MockProofNodeClient::new(0); + let mut sse = mock.subscribe_proof_events(None); + + let attrs = ProofAttributes { + proof_types: vec![0, 1], + }; + let (body, expected_root) = make_test_fulu_ssz(Hash256::repeat_byte(0x42)); + let root = mock + .request_proofs(body, attrs) + .await + .expect("request_proofs should succeed"); + + assert_eq!(root, expected_root); + + for expected_type in [0u8, 1u8] { + let event = timeout(Duration::from_secs(2), sse.next()) + .await + .expect("timed out waiting for SSE event") + .expect("stream ended") + .expect("stream error"); + assert_eq!(event.new_payload_request_root(), root); + assert_eq!(event.proof_type(), expected_type); + } +} + +/// Multiple subscribers each receive every event independently. +#[tokio::test] +async fn mock_client_multiple_subscribers_each_get_events() { + let mock = MockProofNodeClient::new(0); + let mut rx1 = mock.subscribe_client_events(); + let mut rx2 = mock.subscribe_client_events(); + + let (body, _) = make_test_fulu_ssz(Hash256::repeat_byte(0x01)); + let _ = mock + .request_proofs( + body, + ProofAttributes { + proof_types: vec![], + }, + ) + .await + .unwrap(); + + assert!(matches!( + next_event(&mut rx1).await, + MockClientEvent::ProofRequested { .. } + )); + assert!(matches!( + next_event(&mut rx2).await, + MockClientEvent::ProofRequested { .. } + )); +} + +/// Different SSZ bodies produce different roots (computed via tree-hash). +#[tokio::test] +async fn mock_client_computes_distinct_roots_from_ssz() { + let mock = MockProofNodeClient::new(0); + let attrs = ProofAttributes { + proof_types: vec![], + }; + + let (body1, expected1) = make_test_fulu_ssz(Hash256::repeat_byte(0x01)); + let (body2, expected2) = make_test_fulu_ssz(Hash256::repeat_byte(0x02)); + let (body3, expected3) = make_test_fulu_ssz(Hash256::repeat_byte(0x03)); + + let root1 = mock.request_proofs(body1, attrs.clone()).await.unwrap(); + let root2 = mock.request_proofs(body2, attrs.clone()).await.unwrap(); + let root3 = mock.request_proofs(body3, attrs).await.unwrap(); + + assert_eq!(root1, expected1); + assert_eq!(root2, expected2); + assert_eq!(root3, expected3); + assert_ne!(root1, root2); + assert_ne!(root2, root3); + assert_eq!(mock.request_count(), 3); +} + +// ─── HttpProofEngine tests ──────────────────────────────────────────────────── + +/// `verify_execution_proof` returns `Syncing` for an unknown root and does NOT +/// call `verify_proof` on the underlying client. +#[tokio::test] +async fn engine_verify_proof_unknown_root_returns_syncing() { + let mock = MockProofNodeClient::new(0); + let mut rx = mock.subscribe_client_events(); + let engine = HttpProofEngine::with_proof_node(mock); + + let proof = make_proof(Hash256::repeat_byte(0xAB), 0); + let status = engine + .verify_execution_proof(&proof) + .await + .expect("verify should not error"); + + assert!( + status.is_syncing(), + "expected Syncing for unknown root, got {status:?}" + ); + + // verify_proof on the client must not be called for unknown roots. + assert!( + timeout(Duration::from_millis(50), rx.recv()).await.is_err(), + "verify_proof should not be called for an unknown root" + ); +} + +/// `get_proof` delegates to the underlying client and emits `ProofFetched`. +#[tokio::test] +async fn engine_get_proof_delegates_to_client() { + let mock = MockProofNodeClient::new(0); + let mut rx = mock.subscribe_client_events(); + let engine = HttpProofEngine::with_proof_node(mock); + + let root = Hash256::repeat_byte(0xDE); + let bytes = engine + .get_proof(root, 3) + .await + .expect("get_proof should succeed"); + + assert_eq!(bytes.as_ref(), &[0xDE, 0xAD, 0xBE, 0xEF]); + + let event = next_event(&mut rx).await; + assert!(matches!( + event, + MockClientEvent::ProofFetched { root: r, proof_type: 3 } if r == root + )); +} + +/// A proof received before the matching payload is buffered (`Syncing`), and +/// the buffer grows while no `ProofVerified` event is emitted. +#[tokio::test] +async fn engine_unknown_root_proof_is_buffered() { + let mock = MockProofNodeClient::new(0); + let mut rx = mock.subscribe_client_events(); + let engine = HttpProofEngine::with_proof_node(mock); + + let root = Hash256::from_low_u64_be(42); + let proof = make_proof(root, 0); + + // First call: root unknown → Syncing, proof buffered. + let status = engine.verify_execution_proof(&proof).await.unwrap(); + assert!(status.is_syncing(), "expected Syncing, got {status:?}"); + + // The proof must not reach the engine state (tree/buffer promotion requires new_payload). + assert_eq!(engine.get_proofs_by_root(&root).len(), 0); + + // No ProofVerified event should have been emitted. + assert!( + timeout(Duration::from_millis(50), rx.recv()).await.is_err(), + "verify_proof should not be called for an unknown root" + ); +} + +/// `subscribe_proof_events` with a root filter only forwards matching events. +#[tokio::test] +async fn engine_subscribe_proof_events_filters_by_root() { + let mock = MockProofNodeClient::new(0); + let attrs = ProofAttributes { + proof_types: vec![0], + }; + + let (body1, root1) = make_test_fulu_ssz(Hash256::from_low_u64_be(1)); + let (body2, _root2) = make_test_fulu_ssz(Hash256::from_low_u64_be(2)); + + // Subscribe before making requests. + let mut filtered = mock.subscribe_proof_events(Some(root1)); + + // root1 matches the filter; root2 should be silently dropped. + let _ = mock.request_proofs(body1, attrs.clone()).await.unwrap(); + let _ = mock.request_proofs(body2, attrs).await.unwrap(); + + // Only the event for root1 should arrive on the filtered stream. + let event = timeout(Duration::from_secs(2), filtered.next()) + .await + .expect("timed out") + .expect("stream ended") + .expect("stream error"); + assert_eq!(event.new_payload_request_root(), root1); + + // No second event for root2 should arrive within a short window. + assert!( + timeout(Duration::from_millis(100), filtered.next()) + .await + .is_err(), + "filtered stream should not forward events for other roots" + ); +} diff --git a/beacon_node/execution_layer/src/eip8025/types.rs b/beacon_node/execution_layer/src/eip8025/types.rs new file mode 100644 index 00000000000..d89bc708acc --- /dev/null +++ b/beacon_node/execution_layer/src/eip8025/types.rs @@ -0,0 +1,245 @@ +//! API types for EIP-8025 proof engine communication. +//! +//! This module contains: +//! - [`ProofType`]: an independent string enum that mirrors the +//! proof node API's `ProofType` exactly. +//! - SSE event types broadcast by the proof engine. +//! +//! ## ProofType encoding +//! +//! EIP-8025 uses `u8` for `ProofType` in SSZ containers (consensus layer). +//! The proof node API uses kebab-case string identifiers +//! (`"reth-sp1"`, `"ethrex-risc0"`, etc.) in HTTP query params, URL paths, +//! and SSE event payloads. +//! +//! [`ProofType`] bridges this gap: the [`HttpProofNodeClient`] converts +//! between `u8` (internal) and string (wire) at the HTTP boundary. + +use super::errors::ProofEngineError; +use serde::{Deserialize, Deserializer, Serialize}; +use std::fmt; +use std::str::FromStr; +use types::Hash256; + +// ─── ProofType ───────────────────────────────────────────────────────────── + +/// Proof type identifiers. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(into = "String", try_from = "String")] +#[repr(u8)] +pub enum ProofType { + EthrexRisc0 = 0, + EthrexSP1 = 1, + EthrexZisk = 2, + RethOpenVM = 3, + RethRisc0 = 4, + RethSP1 = 5, + RethZisk = 6, +} + +impl ProofType { + /// Canonical string representation, matching exactly. + pub fn as_str(&self) -> &'static str { + match self { + Self::EthrexRisc0 => "ethrex-risc0", + Self::EthrexSP1 => "ethrex-sp1", + Self::EthrexZisk => "ethrex-zisk", + Self::RethOpenVM => "reth-openvm", + Self::RethRisc0 => "reth-risc0", + Self::RethSP1 => "reth-sp1", + Self::RethZisk => "reth-zisk", + } + } + + /// Convert from EIP-8025 `u8` proof type to a string identifier. + /// + /// The mapping follows the order defined in the `ProofType` enum. + pub fn from_u8(value: u8) -> Result { + match value { + 0 => Ok(Self::EthrexRisc0), + 1 => Ok(Self::EthrexSP1), + 2 => Ok(Self::EthrexZisk), + 3 => Ok(Self::RethOpenVM), + 4 => Ok(Self::RethRisc0), + 5 => Ok(Self::RethSP1), + 6 => Ok(Self::RethZisk), + _ => Err(ProofEngineError::InvalidProofType(format!( + "no mapping for proof type {value}" + ))), + } + } + + /// Convert back to EIP-8025 `u8` proof type. + pub fn to_u8(self) -> u8 { + self as u8 + } + + /// All known proof type variants. + pub fn all() -> &'static [ProofType] { + &[ + Self::EthrexRisc0, + Self::EthrexSP1, + Self::EthrexZisk, + Self::RethOpenVM, + Self::RethRisc0, + Self::RethSP1, + Self::RethZisk, + ] + } +} + +impl FromStr for ProofType { + type Err = ProofEngineError; + + fn from_str(s: &str) -> Result { + match s { + "ethrex-risc0" => Ok(Self::EthrexRisc0), + "ethrex-sp1" => Ok(Self::EthrexSP1), + "ethrex-zisk" => Ok(Self::EthrexZisk), + "reth-openvm" => Ok(Self::RethOpenVM), + "reth-risc0" => Ok(Self::RethRisc0), + "reth-sp1" => Ok(Self::RethSP1), + "reth-zisk" => Ok(Self::RethZisk), + _ => Err(ProofEngineError::InvalidProofType(format!( + "unknown proof type: {s}" + ))), + } + } +} + +impl fmt::Display for ProofType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +impl From for String { + fn from(pt: ProofType) -> Self { + pt.as_str().to_string() + } +} + +impl TryFrom for ProofType { + type Error = ProofEngineError; + + fn try_from(s: String) -> Result { + s.parse() + } +} + +// ─── SSE Event Types ──────────────────────────────────────────────────────── + +/// SSE event types broadcast by the proof engine. +#[derive(Debug, Clone, PartialEq)] +pub enum ProofEvent { + /// A proof completed successfully. + ProofComplete(ProofComplete), + /// A proof failed. + ProofFailure(ProofFailure), + /// Witness fetch timed out. + WitnessTimeout(ProofEventInfo), + /// Proof generation timed out. + ProofTimeout(ProofEventInfo), +} + +/// Payload for a successful proof event. +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct ProofComplete { + pub new_payload_request_root: Hash256, + #[serde(deserialize_with = "deserialize_proof_type")] + pub proof_type: u8, +} + +/// Payload for a failed proof event. +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct ProofFailure { + pub new_payload_request_root: Hash256, + #[serde(deserialize_with = "deserialize_proof_type")] + pub proof_type: u8, + pub error: String, +} + +/// Common info for timeout events. +#[derive(Debug, Clone, PartialEq, Deserialize)] +pub struct ProofEventInfo { + pub new_payload_request_root: Hash256, + #[serde(deserialize_with = "deserialize_proof_type")] + pub proof_type: u8, +} + +/// Deserialize `proof_type` from either a string (`"reth-sp1"`) or a +/// numeric value (`0`). This allows Lighthouse to consume SSE events from both +/// servers (string format) and test mocks (numeric format). +fn deserialize_proof_type<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum ProofTypeValue { + Number(u8), + String(String), + } + + match ProofTypeValue::deserialize(deserializer)? { + ProofTypeValue::Number(n) => Ok(n), + ProofTypeValue::String(s) => { + // Try parsing as string identifier first. + if let Ok(pt) = s.parse::() { + return Ok(pt.to_u8()); + } + // Fall back to parsing as numeric string (e.g. "0"). + s.parse::().map_err(serde::de::Error::custom) + } + } +} + +/// SSE event name + JSON data pair used to construct a [`ProofEvent`]. +pub struct SseEventParts<'a>(pub &'a str, pub &'a str); + +impl<'a> TryFrom> for ProofEvent { + type Error = ProofEngineError; + + fn try_from(parts: SseEventParts<'a>) -> Result { + let SseEventParts(name, data) = parts; + match name { + "proof_complete" => Ok(Self::ProofComplete( + serde_json::from_str(data).map_err(ProofEngineError::SerdeError)?, + )), + "proof_failure" => Ok(Self::ProofFailure( + serde_json::from_str(data).map_err(ProofEngineError::SerdeError)?, + )), + "witness_timeout" => Ok(Self::WitnessTimeout( + serde_json::from_str(data).map_err(ProofEngineError::SerdeError)?, + )), + "proof_timeout" => Ok(Self::ProofTimeout( + serde_json::from_str(data).map_err(ProofEngineError::SerdeError)?, + )), + other => Err(ProofEngineError::SseError(format!( + "unknown SSE event type: {other}" + ))), + } + } +} + +impl ProofEvent { + /// Returns the `new_payload_request_root` from the event. + pub fn new_payload_request_root(&self) -> Hash256 { + match self { + Self::ProofComplete(inner) => inner.new_payload_request_root, + Self::ProofFailure(inner) => inner.new_payload_request_root, + Self::WitnessTimeout(inner) => inner.new_payload_request_root, + Self::ProofTimeout(inner) => inner.new_payload_request_root, + } + } + + /// Returns the proof type from the event. + pub fn proof_type(&self) -> u8 { + match self { + Self::ProofComplete(inner) => inner.proof_type, + Self::ProofFailure(inner) => inner.proof_type, + Self::WitnessTimeout(inner) => inner.proof_type, + Self::ProofTimeout(inner) => inner.proof_type, + } + } +} diff --git a/beacon_node/execution_layer/src/engine_api/new_payload_request.rs b/beacon_node/execution_layer/src/engine_api/new_payload_request.rs index 4334a99ce8c..6ef617a0bff 100644 --- a/beacon_node/execution_layer/src/engine_api/new_payload_request.rs +++ b/beacon_node/execution_layer/src/engine_api/new_payload_request.rs @@ -1,6 +1,7 @@ use crate::{Error, block_hash::calculate_execution_block_hash, metrics}; use crate::versioned_hashes::verify_versioned_hashes; +use ssz_derive::Encode as SszEncode; use ssz_types::VariableList; use state_processing::per_block_processing::deneb::kzg_commitment_to_versioned_hash; use superstruct::superstruct; @@ -16,7 +17,7 @@ use types::{ #[superstruct( variants(Bellatrix, Capella, Deneb, Electra, Fulu, Gloas), - variant_attributes(derive(Clone, Debug, PartialEq, TreeHash),), + variant_attributes(derive(Clone, Debug, PartialEq, SszEncode, TreeHash),), map_into(ExecutionPayload), map_ref_into(ExecutionPayloadRef), cast_error( diff --git a/beacon_node/execution_layer/src/engines.rs b/beacon_node/execution_layer/src/engines.rs index 3e6f78abbe9..6559ca0e90e 100644 --- a/beacon_node/execution_layer/src/engines.rs +++ b/beacon_node/execution_layer/src/engines.rs @@ -6,6 +6,7 @@ use crate::engine_api::{ }; use crate::{ClientVersionV1, HttpJsonRpc}; use lru::LruCache; +use ssz_derive::{Decode, Encode}; use std::future::Future; use std::num::NonZeroUsize; use std::sync::Arc; @@ -100,7 +101,7 @@ impl State { } } -#[derive(Copy, Clone, PartialEq, Debug)] +#[derive(Copy, Clone, PartialEq, Debug, Encode, Decode)] pub struct ForkchoiceState { pub head_block_hash: ExecutionBlockHash, pub safe_block_hash: ExecutionBlockHash, diff --git a/beacon_node/execution_layer/src/lib.rs b/beacon_node/execution_layer/src/lib.rs index 85f1f4b06ee..657d6ffe5ea 100644 --- a/beacon_node/execution_layer/src/lib.rs +++ b/beacon_node/execution_layer/src/lib.rs @@ -4,7 +4,6 @@ //! This crate only provides useful functionality for "The Merge", it does not provide any of the //! deposit-contract functionality that the `beacon_node/eth1` crate already provides. -use crate::eip8025::proof_engine::ProofEngine; use crate::json_structures::{BlobAndProofV1, BlobAndProofV2}; use crate::payload_cache::PayloadCache; use arc_swap::ArcSwapOption; @@ -71,6 +70,20 @@ mod payload_status; pub mod test_utils; pub mod versioned_hashes; +/// Combine two optional results, preferring `Ok` values over `Err` values. +/// +/// If both are `Some`, the first `Ok` is returned. If only one is `Ok`, that one wins. +/// If both are `Err`, the first error is returned. +fn prefer_ok(a: Option>, b: Option>) -> Option> { + match (a, b) { + (Some(Ok(val)), _) => Some(Ok(val)), + (_, Some(Ok(val))) => Some(Ok(val)), + (some @ Some(_), _) => some, + (_, some @ Some(_)) => some, + (None, None) => None, + } +} + /// Indicates the default jwt authenticated execution endpoint. pub const DEFAULT_EXECUTION_ENDPOINT: &str = "http://localhost:8551/"; @@ -560,11 +573,28 @@ impl ExecutionLayer { None }; - // Create ProofEngine if proof_engine_endpoint is provided + // Create ProofEngine if proof_engine_endpoint is provided. + // Mock URLs of the form "http://mock/{n}/" look up a pre-registered MockProofNodeClient + // from the global registry instead of opening a real HTTP connection — useful for tests + // and simulation. let proof_engine: Option> = if let Some(proof_url) = proof_engine_endpoint { - debug!(endpoint = %proof_url, "Loaded proof engine endpoint"); - Some(Arc::new(eip8025::HttpProofEngine::new(proof_url, None))) + if let Some(idx) = test_utils::parse_mock_index(proof_url.expose_full().as_str()) { + let mock = test_utils::get_mock_proof_engine(idx).unwrap_or_else(|| { + debug!( + idx, + "No pre-registered mock; creating MockProofNodeClient on the fly" + ); + test_utils::register_mock_proof_engine(idx, 0) + }); + debug!(idx, "Instantiating mock proof engine from registry"); + Some(Arc::new(eip8025::HttpProofEngine::with_proof_node( + (*mock).clone(), + ))) + } else { + debug!(endpoint = %proof_url, "Loaded proof engine endpoint"); + Some(Arc::new(eip8025::HttpProofEngine::new(proof_url, None))) + } } else { None }; @@ -607,6 +637,15 @@ impl ExecutionLayer { self.inner.proof_engine.clone() } + /// Subscribe to method-invocation events emitted by a mock proof node client. + /// + /// Returns `None` if no proof engine is configured or the client is a production HTTP client. + pub fn subscribe_proof_node_client_events( + &self, + ) -> Option> { + self.inner.proof_engine.as_ref()?.subscribe_client_events() + } + pub fn builder(&self) -> Option> { self.inner.builder.load_full() } @@ -1456,13 +1495,18 @@ impl ExecutionLayer { }; let proof_engine_result = if let Some(proof_engine) = self.proof_engine() { - Some(Ok(proof_engine.new_payload(&new_payload_request).await?)) + match proof_engine.new_payload(&new_payload_request).await { + Ok(status) => Some(Ok(status)), + Err(e) => { + debug!(error = ?e, "Proof engine new_payload error (non-fatal)"); + None + } + } } else { None }; - let result = engine_result - .or(proof_engine_result) + let result = prefer_ok(engine_result, proof_engine_result) .expect("at least one of engine or proof engine must be present"); if let Ok(status) = &result { @@ -1615,15 +1659,18 @@ impl ExecutionLayer { }; let proof_engine_result = if let Some(proof_engine) = self.proof_engine() { - Some(Ok(proof_engine - .forkchoice_updated(forkchoice_state) - .await?)) + match proof_engine.forkchoice_updated(forkchoice_state).await { + Ok(response) => Some(Ok(response)), + Err(e) => { + debug!(error = ?e, "Proof engine forkchoice_updated error (non-fatal)"); + None + } + } } else { None }; - let result = engine_result - .or(proof_engine_result) + let result = prefer_ok(engine_result, proof_engine_result) .expect("at least one of engine or proof engine must be present"); if let Ok(status) = &result { @@ -2344,16 +2391,14 @@ fn verify_builder_bid( bid.data.message.value().to_i64(), ); - let expected_withdrawals_root = payload_attributes - .withdrawals() - .ok() - .cloned() - .map(|withdrawals| { + let expected_withdrawals_root = match payload_attributes.withdrawals().ok().cloned() { + Some(withdrawals) => Some( Withdrawals::::try_from(withdrawals) .map_err(InvalidBuilderPayload::SszTypesError) - .map(|w| w.tree_hash_root()) - }) - .transpose()?; + .map(|w| w.tree_hash_root())?, + ), + None => None, + }; let payload_withdrawals_root = header.withdrawals_root().ok(); let expected_gas_limit = proposer_gas_limit diff --git a/beacon_node/execution_layer/src/test_utils/mock_proof_node_client.rs b/beacon_node/execution_layer/src/test_utils/mock_proof_node_client.rs new file mode 100644 index 00000000000..4b305e2b027 --- /dev/null +++ b/beacon_node/execution_layer/src/test_utils/mock_proof_node_client.rs @@ -0,0 +1,271 @@ +//! Mock [`ProofNodeClient`] for unit testing [`HttpProofEngine`]. +//! +//! [`MockProofNodeClient`] implements [`ProofNodeClient`] entirely in memory — +//! no HTTP server required. It records received requests, broadcasts proof +//! events after a configurable delay, and always returns `Valid` for verification. +//! +//! [`ProofNodeClient`]: crate::eip8025::ProofNodeClient +//! [`HttpProofEngine`]: crate::eip8025::HttpProofEngine + +use crate::eip8025::errors::ProofEngineError; +use crate::eip8025::proof_node_client::ProofNodeClient; +use crate::eip8025::types::{ProofComplete, ProofEvent}; +use crate::engine_api::NewPayloadRequestFulu; +use bytes::Bytes; +use futures::stream::Stream; +use parking_lot::Mutex; +use ssz::{Encode, SszDecoderBuilder}; +use ssz_types::VariableList; +use std::collections::HashMap; +use std::pin::Pin; +use std::sync::{Arc, LazyLock}; +use std::time::Duration; +use tokio::sync::broadcast; +use tokio_stream::StreamExt; +use tokio_stream::wrappers::BroadcastStream; +use tree_hash::TreeHash; +use types::execution::eip8025::{ProofAttributes, ProofStatus}; +use types::{ + EthSpec, ExecutionPayloadFulu, ExecutionRequests, Hash256, MainnetEthSpec, VersionedHash, +}; + +/// Events emitted by [`MockProofNodeClient`] for each method invocation. +/// +/// Subscribe via [`MockProofNodeClient::subscribe_client_events`] to observe +/// calls in tests without polling shared state. +#[derive(Debug, Clone)] +pub enum MockClientEvent { + /// Emitted when [`ProofNodeClient::request_proofs`] is called. + ProofRequested { + ssz_body: Vec, + proof_attributes: ProofAttributes, + root: Hash256, + }, + /// Emitted when [`ProofNodeClient::verify_proof`] is called. + ProofVerified { root: Hash256, proof_type: u8 }, + /// Emitted when [`ProofNodeClient::get_proof`] is called. + ProofFetched { root: Hash256, proof_type: u8 }, +} + +static MOCK_REGISTRY: LazyLock>>> = + LazyLock::new(|| parking_lot::Mutex::new(HashMap::new())); + +/// Register a mock at `index`. Must be called before `ExecutionLayer::from_config`. +pub fn register_mock_proof_engine( + index: usize, + callback_delay_ms: u64, +) -> Arc { + let client = Arc::new(MockProofNodeClient::new(callback_delay_ms)); + MOCK_REGISTRY.lock().insert(index, client.clone()); + client +} + +/// Fetch a registered mock by index (returns a clone sharing internal state). +pub fn get_mock_proof_engine(index: usize) -> Option> { + MOCK_REGISTRY.lock().get(&index).cloned() +} + +/// URL encoding an index: `"http://mock/{n}/"`. +pub fn mock_proof_engine_url(index: usize) -> String { + format!("http://mock/{}/", index) +} + +/// Parse the index from a mock URL. Returns `None` for non-mock URLs. +pub fn parse_mock_index(url: &str) -> Option { + url.strip_prefix("http://mock/").map(|s| { + let s = s.strip_suffix('/').unwrap_or(s); + if s.is_empty() { + 0 + } else { + s.parse().unwrap_or(0) + } + }) +} + +/// Decode SSZ bytes as a `NewPayloadRequestFulu` and compute +/// the tree-hash root. +/// +/// Decodes each field individually via `SszDecoderBuilder`, constructs a +/// `NewPayloadRequestFulu` borrowing the owned fields, and returns the +/// tree-hash root of the real superstruct type. +fn decode_fulu_tree_hash_root(ssz_body: &[u8]) -> Result { + let mut builder = SszDecoderBuilder::new(ssz_body); + builder.register_type::>()?; + builder.register_type::::MaxBlobCommitmentsPerBlock>>()?; + builder.register_type::()?; + builder.register_type::>()?; + let mut decoder = builder.build()?; + + let execution_payload: ExecutionPayloadFulu = decoder.decode_next()?; + let versioned_hashes: VariableList< + VersionedHash, + ::MaxBlobCommitmentsPerBlock, + > = decoder.decode_next()?; + let parent_beacon_block_root: Hash256 = decoder.decode_next()?; + let execution_requests: ExecutionRequests = decoder.decode_next()?; + + let request = NewPayloadRequestFulu { + execution_payload: &execution_payload, + versioned_hashes, + parent_beacon_block_root, + execution_requests: &execution_requests, + }; + Ok(request.tree_hash_root()) +} + +/// Build a test SSZ body encoding a `NewPayloadRequestFulu` with the given +/// parent beacon block root. Returns `(ssz_bytes, expected_tree_hash_root)`. +pub fn make_test_fulu_ssz(parent_root: Hash256) -> (Vec, Hash256) { + let execution_payload = ExecutionPayloadFulu::::default(); + let versioned_hashes = VariableList::< + VersionedHash, + ::MaxBlobCommitmentsPerBlock, + >::default(); + let execution_requests = ExecutionRequests::::default(); + let request = NewPayloadRequestFulu { + execution_payload: &execution_payload, + versioned_hashes, + parent_beacon_block_root: parent_root, + execution_requests: &execution_requests, + }; + (request.as_ssz_bytes(), request.tree_hash_root()) +} + +/// In-memory proof node client for testing. +/// +/// Each call to [`request_proofs`] decodes the SSZ body as a Fulu +/// `NewPayloadRequest`, computes the tree-hash root, records the raw SSZ body, +/// and schedules a [`ProofEvent::ProofComplete`] event for each requested +/// proof type after `callback_delay_ms` milliseconds. +/// +/// Call [`subscribe_client_events`] to receive a [`MockClientEvent`] stream +/// that fires once per method invocation — useful for asserting that the proof +/// engine issues the expected calls without polling shared state. +/// +/// [`request_proofs`]: MockProofNodeClient::request_proofs +/// [`subscribe_client_events`]: MockProofNodeClient::subscribe_client_events +#[derive(Clone)] +pub struct MockProofNodeClient { + /// Received SSZ request bodies in order of arrival. + requests: Arc>>>, + /// Broadcast channel for in-memory SSE events. + event_tx: broadcast::Sender, + /// Broadcast channel for method-invocation events. + call_tx: broadcast::Sender, + /// Delay in milliseconds before broadcasting proof complete events. + callback_delay_ms: u64, +} + +impl MockProofNodeClient { + /// Create a new mock client. + /// + /// `callback_delay_ms` controls how long after `request_proofs` the + /// proof complete events are broadcast. + pub fn new(callback_delay_ms: u64) -> Self { + let (event_tx, _) = broadcast::channel(256); + let (call_tx, _) = broadcast::channel(256); + Self { + requests: Arc::new(Mutex::new(Vec::new())), + event_tx, + call_tx, + callback_delay_ms, + } + } + + /// Returns the number of proof requests received. + pub fn request_count(&self) -> usize { + self.requests.lock().len() + } + + /// Returns a clone of all received SSZ request bodies. + pub fn received_requests(&self) -> Vec> { + self.requests.lock().clone() + } + + /// Subscribe to method-invocation events. + /// + /// Each call to `request_proofs`, `verify_proof`, or `get_proof` on this + /// client sends one [`MockClientEvent`] to all active receivers. Use this + /// in tests to assert that the proof engine issues the expected calls. + pub fn subscribe_client_events(&self) -> broadcast::Receiver { + self.call_tx.subscribe() + } +} + +#[async_trait::async_trait] +impl ProofNodeClient for MockProofNodeClient { + async fn request_proofs( + &self, + ssz_body: Vec, + proof_attributes: ProofAttributes, + ) -> Result { + let root = decode_fulu_tree_hash_root(&ssz_body) + .map_err(|e| ProofEngineError::InvalidPayload(format!("SSZ decode failed: {e:?}")))?; + + self.requests.lock().push(ssz_body.clone()); + + let _ = self.call_tx.send(MockClientEvent::ProofRequested { + ssz_body, + proof_attributes: proof_attributes.clone(), + root, + }); + + let event_tx = self.event_tx.clone(); + let delay = self.callback_delay_ms; + let proof_types = proof_attributes.proof_types.clone(); + + tokio::spawn(async move { + tokio::time::sleep(Duration::from_millis(delay)).await; + for proof_type in proof_types { + let _ = event_tx.send(ProofEvent::ProofComplete(ProofComplete { + new_payload_request_root: root, + proof_type, + })); + } + }); + + Ok(root) + } + + async fn verify_proof( + &self, + root: Hash256, + proof_type: u8, + _proof_data: &[u8], + ) -> Result { + let _ = self + .call_tx + .send(MockClientEvent::ProofVerified { root, proof_type }); + Ok(ProofStatus::Valid) + } + + async fn get_proof(&self, root: Hash256, proof_type: u8) -> Result { + let _ = self + .call_tx + .send(MockClientEvent::ProofFetched { root, proof_type }); + Ok(Bytes::from(vec![0xDE, 0xAD, 0xBE, 0xEF])) + } + + fn subscribe_client_events( + &self, + ) -> Option> { + Some(self.call_tx.subscribe()) + } + + fn subscribe_proof_events( + &self, + filter_root: Option, + ) -> Pin> + Send + '_>> { + let rx = self.event_tx.subscribe(); + let stream = BroadcastStream::new(rx).filter_map(move |result| match result { + Ok(event) => { + if filter_root.is_some_and(|root| event.new_payload_request_root() != root) { + return None; + } + Some(Ok(event)) + } + Err(_) => None, + }); + Box::pin(stream) + } +} diff --git a/beacon_node/execution_layer/src/test_utils/mod.rs b/beacon_node/execution_layer/src/test_utils/mod.rs index b2a6d6f98e2..fd357737ce1 100644 --- a/beacon_node/execution_layer/src/test_utils/mod.rs +++ b/beacon_node/execution_layer/src/test_utils/mod.rs @@ -34,6 +34,10 @@ pub use execution_block_generator::{ pub use hook::Hook; pub use mock_builder::{MockBuilder, Operation, mock_builder_extra_data}; pub use mock_execution_layer::MockExecutionLayer; +pub use mock_proof_node_client::{ + MockClientEvent, MockProofNodeClient, get_mock_proof_engine, make_test_fulu_ssz, + mock_proof_engine_url, parse_mock_index, register_mock_proof_engine, +}; pub const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400; pub const DEFAULT_TERMINAL_BLOCK: u64 = 64; @@ -73,6 +77,7 @@ mod handle_rpc; mod hook; mod mock_builder; mod mock_execution_layer; +mod mock_proof_node_client; /// Configuration for the MockExecutionLayer. #[derive(Clone)] diff --git a/beacon_node/http_api/src/attestation_performance.rs b/beacon_node/http_api/src/attestation_performance.rs index 6e285829d22..19793ce9b1b 100644 --- a/beacon_node/http_api/src/attestation_performance.rs +++ b/beacon_node/http_api/src/attestation_performance.rs @@ -32,6 +32,7 @@ impl From for AttestationPerformanceError { } } +#[allow(clippy::result_large_err)] pub fn get_attestation_performance( target: String, query: AttestationPerformanceQuery, @@ -111,6 +112,7 @@ pub fn get_attestation_performance( let first_block = chain .get_blinded_block(first_block_root) .and_then(|maybe_block| { + #[allow(clippy::result_large_err)] maybe_block.ok_or(BeaconChainError::MissingBeaconBlock(*first_block_root)) }) .map_err(unhandled_error)?; @@ -119,6 +121,7 @@ pub fn get_attestation_performance( let prior_block = chain .get_blinded_block(&first_block.parent_root()) .and_then(|maybe_block| { + #[allow(clippy::result_large_err)] maybe_block .ok_or_else(|| BeaconChainError::MissingBeaconBlock(first_block.parent_root())) }) @@ -131,7 +134,10 @@ pub fn get_attestation_performance( // to cache states so that future calls are faster. let state = chain .get_state(&state_root, Some(prior_slot), true) - .and_then(|maybe_state| maybe_state.ok_or(BeaconChainError::MissingBeaconState(state_root))) + .and_then(|maybe_state| { + #[allow(clippy::result_large_err)] + maybe_state.ok_or(BeaconChainError::MissingBeaconState(state_root)) + }) .map_err(unhandled_error)?; // Allocate an AttestationPerformance vector for each validator in the range. @@ -199,6 +205,7 @@ pub fn get_attestation_performance( chain .get_blinded_block(root) .and_then(|maybe_block| { + #[allow(clippy::result_large_err)] maybe_block.ok_or(BeaconChainError::MissingBeaconBlock(*root)) }) .map_err(unhandled_error) diff --git a/beacon_node/http_api/src/attester_duties.rs b/beacon_node/http_api/src/attester_duties.rs index b42e474b5c4..f9132e83630 100644 --- a/beacon_node/http_api/src/attester_duties.rs +++ b/beacon_node/http_api/src/attester_duties.rs @@ -79,6 +79,7 @@ fn cached_attestation_duties( /// Compute some attester duties by reading a `BeaconState` from disk, completely ignoring the /// shuffling cache. +#[allow(clippy::result_large_err)] fn compute_historic_attester_duties( request_epoch: Epoch, request_indices: &[u64], @@ -151,6 +152,7 @@ fn compute_historic_attester_duties( let duties = request_indices .iter() .map(|&validator_index| { + #[allow(clippy::result_large_err)] state .get_attestation_duties(validator_index as usize, relative_epoch) .map_err(BeaconChainError::from) diff --git a/beacon_node/http_api/src/block_packing_efficiency.rs b/beacon_node/http_api/src/block_packing_efficiency.rs index 3772470b281..3f1501e9f8d 100644 --- a/beacon_node/http_api/src/block_packing_efficiency.rs +++ b/beacon_node/http_api/src/block_packing_efficiency.rs @@ -236,6 +236,7 @@ impl PackingEfficiencyHandler { } } +#[allow(clippy::result_large_err)] pub fn get_block_packing_efficiency( query: BlockPackingEfficiencyQuery, chain: Arc>, @@ -278,6 +279,7 @@ pub fn get_block_packing_efficiency( let first_block = chain .get_blinded_block(first_block_root) .and_then(|maybe_block| { + #[allow(clippy::result_large_err)] maybe_block.ok_or(BeaconChainError::MissingBeaconBlock(*first_block_root)) }) .map_err(unhandled_error)?; @@ -290,6 +292,7 @@ pub fn get_block_packing_efficiency( let starting_state = chain .get_state(&starting_state_root, Some(prior_slot), true) .and_then(|maybe_state| { + #[allow(clippy::result_large_err)] maybe_state.ok_or(BeaconChainError::MissingBeaconState(starting_state_root)) }) .map_err(unhandled_error)?; @@ -392,6 +395,7 @@ pub fn get_block_packing_efficiency( chain .get_blinded_block(root) .and_then(|maybe_block| { + #[allow(clippy::result_large_err)] maybe_block.ok_or(BeaconChainError::MissingBeaconBlock(*root)) }) .map_err(unhandled_error) diff --git a/beacon_node/http_api/src/block_rewards.rs b/beacon_node/http_api/src/block_rewards.rs index 891f024bf9c..85b1a3ce49d 100644 --- a/beacon_node/http_api/src/block_rewards.rs +++ b/beacon_node/http_api/src/block_rewards.rs @@ -12,6 +12,7 @@ use warp_utils::reject::{beacon_state_error, custom_bad_request, unhandled_error const STATE_CACHE_SIZE: NonZeroUsize = new_non_zero_usize(2); /// Fetch block rewards for blocks from the canonical chain. +#[allow(clippy::result_large_err)] pub fn get_block_rewards( query: BlockRewardsQuery, chain: Arc>, @@ -46,7 +47,10 @@ pub fn get_block_rewards( // to cache states so that future calls are faster. let mut state = chain .get_state(&state_root, Some(prior_slot), true) - .and_then(|maybe_state| maybe_state.ok_or(BeaconChainError::MissingBeaconState(state_root))) + .and_then(|maybe_state| { + #[allow(clippy::result_large_err)] + maybe_state.ok_or(BeaconChainError::MissingBeaconState(state_root)) + }) .map_err(unhandled_error)?; state @@ -58,6 +62,7 @@ pub fn get_block_rewards( let block_replayer = BlockReplayer::new(state, &chain.spec) .pre_block_hook(Box::new(|state, block| { + #[allow(clippy::result_large_err)] state.build_all_committee_caches(&chain.spec)?; // Compute block reward. diff --git a/beacon_node/http_api/src/eip8025.rs b/beacon_node/http_api/src/eip8025.rs index c8ae9248c47..ff0298c2c72 100644 --- a/beacon_node/http_api/src/eip8025.rs +++ b/beacon_node/http_api/src/eip8025.rs @@ -6,14 +6,14 @@ use crate::block_id::BlockId; use beacon_chain::{BeaconChain, BeaconChainTypes}; -use execution_layer::eip8025::ProofEngine; -use lighthouse_network::PubsubMessage; +use lighthouse_network::rpc::methods::ExecutionProofStatus; +use lighthouse_network::{NetworkGlobals, PubsubMessage}; use network::NetworkMessage; use serde::{Deserialize, Serialize}; use std::sync::Arc; use tokio::sync::mpsc::UnboundedSender; use tracing::{debug, warn}; -use types::SignedExecutionProof; +use types::{ProofStatus, SignedExecutionProof}; use warp::Reply; use warp::http::Response; use warp::hyper::Body; @@ -48,11 +48,12 @@ pub fn get_execution_proofs( .as_ref() .ok_or_else(|| custom_server_error("Execution layer not available".to_string()))?; - let proof_engine = execution_layer - .proof_engine() - .ok_or_else(|| custom_bad_request( - "Proof engine not configured. Start with --proof-engine-endpoint to enable EIP-8025.".to_string(), - ))?; + let proof_engine = execution_layer.proof_engine().ok_or_else(|| { + custom_bad_request( + "Proof engine not configured. Start with --proof-engine-endpoint to enable EIP-8025." + .to_string(), + ) + })?; // Get the block to retrieve its execution payload root let (block_root, execution_optimistic, finalized) = block_id.root(&chain)?; @@ -85,6 +86,7 @@ pub fn get_execution_proofs( pub async fn submit_execution_proofs( request: SubmitExecutionProofsRequest, chain: Arc>, + network_globals: Arc>, network_send: UnboundedSender>, ) -> Result, warp::Rejection> { // TODO: should we add a verify: bool to verify_execution_proof to allow skipping verification checks from this endpoint if we trust the source? @@ -118,35 +120,71 @@ pub async fn submit_execution_proofs( ); // Verify proof (BLS signature + execution engine + fork choice update) - if let Err(e) = chain.verify_execution_proof(signed_proof.clone()).await { - warn!( - error = ?e, - ?request_root, - proof_type, - validator_index, - "Signed proof validation failed" - ); - return Err(custom_bad_request(format!( - "Proof validation failed: {e:?}" - ))); - } + let (status, verified_block) = chain + .verify_execution_proof(signed_proof.clone()) + .await + .map_err(|e| { + warn!( + error = ?e, + ?request_root, + proof_type, + validator_index, + "Signed proof validation failed" + ); + custom_bad_request(format!("Proof validation failed: {e:?}")) + })?; + + // Update local execution proof status watermark if the proof was fully valid. + if status.is_valid() + && let Some((block_root, slot)) = verified_block + { + network_globals.set_local_execution_proof_status(ExecutionProofStatus { + slot: slot.as_u64(), + block_root, + }); + }; - // Gossip publish the signed proof - if let Err(e) = network_send.send(NetworkMessage::Publish { - messages: vec![PubsubMessage::ExecutionProof(Box::new(signed_proof))], - }) { - warn!( - error = ?e, - ?request_root, - proof_type, - "Failed to gossip signed proof" - ); + // Only propagate proofs the execution engine accepted as valid or tentatively accepted. + // Invalid, Syncing, and NotSupported proofs must not be gossiped. + match status { + ProofStatus::Valid | ProofStatus::Accepted => { + if let Err(e) = network_send.send(NetworkMessage::Publish { + messages: vec![PubsubMessage::ExecutionProof(Box::new(signed_proof))], + }) { + warn!( + error = ?e, + ?request_root, + proof_type, + "Failed to gossip signed proof" + ); + } + debug!( + ?request_root, + proof_type, validator_index, "Signed execution proof verified and gossiped" + ); + } + ProofStatus::Invalid => { + return Err(custom_bad_request(format!( + "Proof {request_root:?} is invalid" + ))); + } + ProofStatus::Syncing => { + debug!( + ?request_root, + proof_type, + validator_index, + "Proof skipped: node is still syncing the associated block" + ); + } + ProofStatus::NotSupported => { + debug!( + ?request_root, + proof_type, + validator_index, + "Proof skipped: proof type not supported by local engine" + ); + } } - - debug!( - ?request_root, - proof_type, validator_index, "Signed execution proof verified, stored, and gossiped" - ); } Ok(warp::reply().into_response()) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 453fc633ac6..b84d0068cf8 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -1832,14 +1832,17 @@ pub fn serve( .clone() .and(warp::path::end()) .and(warp_utils::json::json()) + .and(network_globals.clone()) .and(network_tx_filter.clone()) .then( |task_spawner: TaskSpawner, chain: Arc>, proofs: eip8025::SubmitExecutionProofsRequest, + network_globals: Arc>, network_send: UnboundedSender>| { task_spawner.spawn_async_with_rejection(Priority::P1, async move { - eip8025::submit_execution_proofs(proofs, chain, network_send).await + eip8025::submit_execution_proofs(proofs, chain, network_globals, network_send) + .await }) }, ); @@ -3178,9 +3181,6 @@ pub fn serve( let receiver = match topic { api_types::EventTopic::Head => event_handler.subscribe_head(), api_types::EventTopic::Block => event_handler.subscribe_block(), - api_types::EventTopic::BlockFull => { - event_handler.subscribe_block_full() - } api_types::EventTopic::BlobSidecar => { event_handler.subscribe_blob_sidecar() } @@ -3232,6 +3232,9 @@ pub fn serve( api_types::EventTopic::BlockGossip => { event_handler.subscribe_block_gossip() } + api_types::EventTopic::ExecutionProofValidated => { + event_handler.subscribe_execution_proof_validated() + } }; receivers.push( diff --git a/beacon_node/http_api/src/sync_committee_rewards.rs b/beacon_node/http_api/src/sync_committee_rewards.rs index 9bc1f6ead4d..479dda451d4 100644 --- a/beacon_node/http_api/src/sync_committee_rewards.rs +++ b/beacon_node/http_api/src/sync_committee_rewards.rs @@ -45,6 +45,7 @@ pub fn compute_sync_committee_rewards( Ok((data, execution_optimistic, finalized)) } +#[allow(clippy::result_large_err)] pub fn get_state_before_applying_block( chain: Arc>, block: &SignedBlindedBeaconBlock, @@ -52,6 +53,7 @@ pub fn get_state_before_applying_block( let parent_block: SignedBlindedBeaconBlock = chain .get_blinded_block(&block.parent_root()) .and_then(|maybe_block| { + #[allow(clippy::result_large_err)] maybe_block.ok_or_else(|| BeaconChainError::MissingBeaconBlock(block.parent_root())) }) .map_err(|e| custom_not_found(format!("Parent block is not available! {:?}", e)))?; @@ -61,6 +63,7 @@ pub fn get_state_before_applying_block( let parent_state = chain .get_state(&parent_block.state_root(), Some(parent_block.slot()), true) .and_then(|maybe_state| { + #[allow(clippy::result_large_err)] maybe_state .ok_or_else(|| BeaconChainError::MissingBeaconState(parent_block.state_root())) }) diff --git a/beacon_node/http_api/src/sync_committees.rs b/beacon_node/http_api/src/sync_committees.rs index 6e2f4c95851..313ffb6fb94 100644 --- a/beacon_node/http_api/src/sync_committees.rs +++ b/beacon_node/http_api/src/sync_committees.rs @@ -136,6 +136,7 @@ fn duties_from_state_load( } } +#[allow(clippy::result_large_err)] fn verify_unknown_validators( duties: Vec, BeaconStateError>>, request_epoch: Epoch, @@ -147,6 +148,7 @@ fn verify_unknown_validators( duties .into_iter() .map(|res| { + #[allow(clippy::result_large_err)] res.or_else(|err| { // Make sure the validator is really unknown w.r.t. the request_epoch if let BeaconStateError::UnknownValidator(idx) = err { diff --git a/beacon_node/http_api/src/ui.rs b/beacon_node/http_api/src/ui.rs index 1538215a0b5..5111f4c71cf 100644 --- a/beacon_node/http_api/src/ui.rs +++ b/beacon_node/http_api/src/ui.rs @@ -20,6 +20,7 @@ pub struct ValidatorCountResponse { pub exited_slashed: u64, } +#[allow(clippy::result_large_err)] pub fn get_validator_count( chain: Arc>, ) -> Result { @@ -36,27 +37,30 @@ pub fn get_validator_count( chain .with_head(|head| { - let state = &head.beacon_state; - let epoch = state.current_epoch(); - for validator in state.validators() { - let status = - ValidatorStatus::from_validator(validator, epoch, spec.far_future_epoch); - - match status { - ValidatorStatus::ActiveOngoing => active_ongoing += 1, - ValidatorStatus::ActiveExiting => active_exiting += 1, - ValidatorStatus::ActiveSlashed => active_slashed += 1, - ValidatorStatus::PendingInitialized => pending_initialized += 1, - ValidatorStatus::PendingQueued => pending_queued += 1, - ValidatorStatus::WithdrawalPossible => withdrawal_possible += 1, - ValidatorStatus::WithdrawalDone => withdrawal_done += 1, - ValidatorStatus::ExitedUnslashed => exited_unslashed += 1, - ValidatorStatus::ExitedSlashed => exited_slashed += 1, - // Since we are not invoking `superset`, all other variants will be 0. - _ => (), + #[allow(clippy::result_large_err)] + { + let state = &head.beacon_state; + let epoch = state.current_epoch(); + for validator in state.validators() { + let status = + ValidatorStatus::from_validator(validator, epoch, spec.far_future_epoch); + + match status { + ValidatorStatus::ActiveOngoing => active_ongoing += 1, + ValidatorStatus::ActiveExiting => active_exiting += 1, + ValidatorStatus::ActiveSlashed => active_slashed += 1, + ValidatorStatus::PendingInitialized => pending_initialized += 1, + ValidatorStatus::PendingQueued => pending_queued += 1, + ValidatorStatus::WithdrawalPossible => withdrawal_possible += 1, + ValidatorStatus::WithdrawalDone => withdrawal_done += 1, + ValidatorStatus::ExitedUnslashed => exited_unslashed += 1, + ValidatorStatus::ExitedSlashed => exited_slashed += 1, + // Since we are not invoking `superset`, all other variants will be 0. + _ => (), + } } + Ok::<(), BeaconChainError>(()) } - Ok::<(), BeaconChainError>(()) }) .map_err(unhandled_error)?; diff --git a/beacon_node/lighthouse_network/src/peer_manager/mod.rs b/beacon_node/lighthouse_network/src/peer_manager/mod.rs index ccce98dd196..4c29c1023ad 100644 --- a/beacon_node/lighthouse_network/src/peer_manager/mod.rs +++ b/beacon_node/lighthouse_network/src/peer_manager/mod.rs @@ -610,6 +610,8 @@ impl PeerManager { Protocol::DataColumnsByRange => PeerAction::MidToleranceError, Protocol::ExecutionProofsByRange => PeerAction::MidToleranceError, Protocol::ExecutionProofsByRoot => PeerAction::MidToleranceError, + // ExecutionProofStatus is a soft informational request; rate limiting is fine. + Protocol::ExecutionProofStatus => return, Protocol::Goodbye => PeerAction::LowToleranceError, Protocol::MetaData => PeerAction::LowToleranceError, Protocol::Status => PeerAction::LowToleranceError, @@ -632,6 +634,7 @@ impl PeerManager { Protocol::DataColumnsByRange => return, Protocol::ExecutionProofsByRange => return, Protocol::ExecutionProofsByRoot => return, + Protocol::ExecutionProofStatus => return, Protocol::Goodbye => return, Protocol::LightClientBootstrap => return, Protocol::LightClientOptimisticUpdate => return, @@ -657,6 +660,7 @@ impl PeerManager { Protocol::DataColumnsByRange => PeerAction::MidToleranceError, Protocol::ExecutionProofsByRange => PeerAction::MidToleranceError, Protocol::ExecutionProofsByRoot => PeerAction::MidToleranceError, + Protocol::ExecutionProofStatus => return, Protocol::LightClientBootstrap => return, Protocol::LightClientOptimisticUpdate => return, Protocol::LightClientFinalityUpdate => return, diff --git a/beacon_node/lighthouse_network/src/rpc/codec.rs b/beacon_node/lighthouse_network/src/rpc/codec.rs index 4125887ddb7..c884e67ad97 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec.rs @@ -82,6 +82,7 @@ impl SSZSnappyInboundCodec { RpcSuccessResponse::DataColumnsByRange(res) => res.as_ssz_bytes(), RpcSuccessResponse::ExecutionProofsByRange(res) => res.as_ssz_bytes(), RpcSuccessResponse::ExecutionProofsByRoot(res) => res.as_ssz_bytes(), + RpcSuccessResponse::ExecutionProofStatus(s) => s.as_ssz_bytes(), RpcSuccessResponse::LightClientBootstrap(res) => res.as_ssz_bytes(), RpcSuccessResponse::LightClientOptimisticUpdate(res) => res.as_ssz_bytes(), RpcSuccessResponse::LightClientFinalityUpdate(res) => res.as_ssz_bytes(), @@ -165,6 +166,7 @@ impl Decoder for SSZSnappyInboundCodec { if self.protocol.versioned_protocol == SupportedProtocol::MetaDataV3 { return Ok(Some(RequestType::MetaData(MetadataRequest::new_v3()))); } + // ExecutionProofStatus now carries a 40-byte body; handled in the normal decode path below. let Some(length) = handle_length(&mut self.inner, &mut self.len, src)? else { return Ok(None); }; @@ -367,7 +369,8 @@ impl Encoder> for SSZSnappyOutboundCodec { RequestType::LightClientUpdatesByRange(req) => req.as_ssz_bytes(), RequestType::ExecutionProofsByRange(req) => req.as_ssz_bytes(), RequestType::ExecutionProofsByRoot(req) => req.block_roots.as_ssz_bytes(), - // no metadata to encode + RequestType::ExecutionProofStatus(s) => s.as_ssz_bytes(), + // no body to encode for these request types RequestType::MetaData(_) | RequestType::LightClientOptimisticUpdate | RequestType::LightClientFinalityUpdate => return Ok(()), @@ -631,6 +634,9 @@ fn handle_rpc_request( Ok(Some(RequestType::MetaData(MetadataRequest::new_v1()))) } } + SupportedProtocol::ExecutionProofStatusV1 => Ok(Some(RequestType::ExecutionProofStatus( + ExecutionProofStatus::from_ssz_bytes(decoded_buffer)?, + ))), } } @@ -868,6 +874,11 @@ fn handle_rpc_response( SignedExecutionProof::from_ssz_bytes(decoded_buffer)?, )))) } + SupportedProtocol::ExecutionProofStatusV1 => { + Ok(Some(RpcSuccessResponse::ExecutionProofStatus( + ExecutionProofStatus::from_ssz_bytes(decoded_buffer)?, + ))) + } SupportedProtocol::BlocksByRootV2 => match fork_name { Some(ForkName::Altair) => Ok(Some(RpcSuccessResponse::BlocksByRoot(Arc::new( SignedBeaconBlock::Altair(SignedBeaconBlockAltair::from_ssz_bytes(decoded_buffer)?), @@ -1317,6 +1328,9 @@ mod tests { RequestType::ExecutionProofsByRoot(ep_root) => { assert_eq!(decoded, RequestType::ExecutionProofsByRoot(ep_root)) } + RequestType::ExecutionProofStatus(s) => { + assert_eq!(decoded, RequestType::ExecutionProofStatus(s)) + } } } diff --git a/beacon_node/lighthouse_network/src/rpc/config.rs b/beacon_node/lighthouse_network/src/rpc/config.rs index 1eb71ab49aa..8833b547d11 100644 --- a/beacon_node/lighthouse_network/src/rpc/config.rs +++ b/beacon_node/lighthouse_network/src/rpc/config.rs @@ -99,6 +99,7 @@ pub struct RateLimiterConfig { pub(super) light_client_updates_by_range_quota: Quota, pub(super) execution_proofs_by_range_quota: Quota, pub(super) execution_proofs_by_root_quota: Quota, + pub(super) execution_proof_status_quota: Quota, } impl RateLimiterConfig { @@ -133,6 +134,8 @@ impl RateLimiterConfig { Quota::n_every(NonZeroU64::new(128).unwrap(), 10); pub const DEFAULT_EXECUTION_PROOFS_BY_ROOT_QUOTA: Quota = Quota::n_every(NonZeroU64::new(128).unwrap(), 10); + pub const DEFAULT_EXECUTION_PROOF_STATUS_QUOTA: Quota = + Quota::n_every(NonZeroU64::new(5).unwrap(), 15); } impl Default for RateLimiterConfig { @@ -155,6 +158,7 @@ impl Default for RateLimiterConfig { light_client_updates_by_range_quota: Self::DEFAULT_LIGHT_CLIENT_UPDATES_BY_RANGE_QUOTA, execution_proofs_by_range_quota: Self::DEFAULT_EXECUTION_PROOFS_BY_RANGE_QUOTA, execution_proofs_by_root_quota: Self::DEFAULT_EXECUTION_PROOFS_BY_ROOT_QUOTA, + execution_proof_status_quota: Self::DEFAULT_EXECUTION_PROOF_STATUS_QUOTA, } } } @@ -216,6 +220,7 @@ impl FromStr for RateLimiterConfig { let mut light_client_updates_by_range_quota = None; let mut execution_proofs_by_range_quota = None; let mut execution_proofs_by_root_quota = None; + let mut execution_proof_status_quota = None; for proto_def in s.split(';') { let ProtocolQuota { protocol, quota } = proto_def.parse()?; @@ -256,6 +261,9 @@ impl FromStr for RateLimiterConfig { Protocol::ExecutionProofsByRoot => { execution_proofs_by_root_quota = execution_proofs_by_root_quota.or(quota) } + Protocol::ExecutionProofStatus => { + execution_proof_status_quota = execution_proof_status_quota.or(quota) + } } } Ok(RateLimiterConfig { @@ -286,6 +294,8 @@ impl FromStr for RateLimiterConfig { .unwrap_or(Self::DEFAULT_EXECUTION_PROOFS_BY_RANGE_QUOTA), execution_proofs_by_root_quota: execution_proofs_by_root_quota .unwrap_or(Self::DEFAULT_EXECUTION_PROOFS_BY_ROOT_QUOTA), + execution_proof_status_quota: execution_proof_status_quota + .unwrap_or(Self::DEFAULT_EXECUTION_PROOF_STATUS_QUOTA), }) } } diff --git a/beacon_node/lighthouse_network/src/rpc/methods.rs b/beacon_node/lighthouse_network/src/rpc/methods.rs index 8af7504599b..4b712fc3c85 100644 --- a/beacon_node/lighthouse_network/src/rpc/methods.rs +++ b/beacon_node/lighthouse_network/src/rpc/methods.rs @@ -574,6 +574,16 @@ impl LightClientUpdatesByRangeRequest { } } +/// The peer's current execution proof verification status, exchanged via the +/// `ExecutionProofStatus` RPC protocol. +#[derive(Encode, Decode, Default, Copy, Clone, Debug, PartialEq)] +pub struct ExecutionProofStatus { + /// The block root of the latest block verified by this peer. + pub block_root: Hash256, + /// The slot of the latest block verified by this peer. + pub slot: u64, +} + /// Request execution proofs for a slot range from a peer. #[derive(Encode, Decode, Clone, Debug, PartialEq)] pub struct ExecutionProofsByRangeRequest { @@ -585,8 +595,8 @@ pub struct ExecutionProofsByRangeRequest { impl ExecutionProofsByRangeRequest { pub fn max_requested(&self) -> u64 { - use types::execution::eip8025::MaxExecutionProofsPerPayload; use typenum::Unsigned; + use types::execution::eip8025::MaxExecutionProofsPerPayload; self.count .saturating_mul(MaxExecutionProofsPerPayload::to_u64()) } @@ -690,6 +700,9 @@ pub enum RpcSuccessResponse { /// A response to a META_DATA request. MetaData(Arc>), + + /// A response to an EXECUTION_PROOF_STATUS request. + ExecutionProofStatus(ExecutionProofStatus), } /// Indicates which response is being terminated by a stream termination response. @@ -839,6 +852,7 @@ impl RpcSuccessResponse { RpcSuccessResponse::LightClientUpdatesByRange(_) => Protocol::LightClientUpdatesByRange, RpcSuccessResponse::ExecutionProofsByRange(_) => Protocol::ExecutionProofsByRange, RpcSuccessResponse::ExecutionProofsByRoot(_) => Protocol::ExecutionProofsByRoot, + RpcSuccessResponse::ExecutionProofStatus(_) => Protocol::ExecutionProofStatus, } } @@ -859,7 +873,8 @@ impl RpcSuccessResponse { | Self::Status(_) | Self::Pong(_) | Self::ExecutionProofsByRange(_) - | Self::ExecutionProofsByRoot(_) => None, + | Self::ExecutionProofsByRoot(_) + | Self::ExecutionProofStatus(_) => None, } } } @@ -961,6 +976,9 @@ impl std::fmt::Display for RpcSuccessResponse { proof.validator_index ) } + RpcSuccessResponse::ExecutionProofStatus(s) => { + write!(f, "ExecutionProofStatus: slot={}", s.slot) + } } } } diff --git a/beacon_node/lighthouse_network/src/rpc/protocol.rs b/beacon_node/lighthouse_network/src/rpc/protocol.rs index f5e5bc271c3..5cb2b2ffeb3 100644 --- a/beacon_node/lighthouse_network/src/rpc/protocol.rs +++ b/beacon_node/lighthouse_network/src/rpc/protocol.rs @@ -286,6 +286,9 @@ pub enum Protocol { /// The `ExecutionProofsByRoot` protocol name. #[strum(serialize = "execution_proofs_by_root")] ExecutionProofsByRoot, + /// The `ExecutionProofStatus` protocol name. + #[strum(serialize = "execution_proof_status")] + ExecutionProofStatus, } impl Protocol { @@ -307,6 +310,7 @@ impl Protocol { Protocol::LightClientUpdatesByRange => None, Protocol::ExecutionProofsByRange => Some(ResponseTermination::ExecutionProofsByRange), Protocol::ExecutionProofsByRoot => Some(ResponseTermination::ExecutionProofsByRoot), + Protocol::ExecutionProofStatus => None, } } } @@ -341,6 +345,7 @@ pub enum SupportedProtocol { LightClientUpdatesByRangeV1, ExecutionProofsByRangeV1, ExecutionProofsByRootV1, + ExecutionProofStatusV1, } impl SupportedProtocol { @@ -367,6 +372,7 @@ impl SupportedProtocol { SupportedProtocol::LightClientUpdatesByRangeV1 => "1", SupportedProtocol::ExecutionProofsByRangeV1 => "1", SupportedProtocol::ExecutionProofsByRootV1 => "1", + SupportedProtocol::ExecutionProofStatusV1 => "1", } } @@ -395,6 +401,7 @@ impl SupportedProtocol { SupportedProtocol::LightClientUpdatesByRangeV1 => Protocol::LightClientUpdatesByRange, SupportedProtocol::ExecutionProofsByRangeV1 => Protocol::ExecutionProofsByRange, SupportedProtocol::ExecutionProofsByRootV1 => Protocol::ExecutionProofsByRoot, + SupportedProtocol::ExecutionProofStatusV1 => Protocol::ExecutionProofStatus, } } @@ -487,6 +494,10 @@ impl UpgradeInfo for RPCProtocol { SupportedProtocol::ExecutionProofsByRootV1, Encoding::SSZSnappy, )); + supported_protocols.push(ProtocolId::new( + SupportedProtocol::ExecutionProofStatusV1, + Encoding::SSZSnappy, + )); } supported_protocols } @@ -578,9 +589,12 @@ impl ProtocolId { ExecutionProofsByRangeRequest::ssz_max_len(), ), // ExecutionProofsByRoot request is a list of block roots — same size limit as BlocksByRoot. - Protocol::ExecutionProofsByRoot => { - RpcLimits::new(0, spec.max_blocks_by_root_request) - } + Protocol::ExecutionProofsByRoot => RpcLimits::new(0, spec.max_blocks_by_root_request), + // ExecutionProofStatus request carries the local node's status. + Protocol::ExecutionProofStatus => RpcLimits::new( + ExecutionProofStatus::ssz_fixed_len(), + ExecutionProofStatus::ssz_fixed_len(), + ), } } @@ -626,6 +640,11 @@ impl ProtocolId { SIGNED_EXECUTION_PROOF_MIN_SIZE, SIGNED_EXECUTION_PROOF_MAX_SIZE, ), + // ExecutionProofStatus response is fixed-size SSZ. + Protocol::ExecutionProofStatus => RpcLimits::new( + ExecutionProofStatus::ssz_fixed_len(), + ExecutionProofStatus::ssz_fixed_len(), + ), } } @@ -654,7 +673,8 @@ impl ProtocolId { | SupportedProtocol::GoodbyeV1 // Execution proof types are not fork-dependent, no context bytes needed. | SupportedProtocol::ExecutionProofsByRangeV1 - | SupportedProtocol::ExecutionProofsByRootV1 => false, + | SupportedProtocol::ExecutionProofsByRootV1 + | SupportedProtocol::ExecutionProofStatusV1 => false, } } } @@ -749,6 +769,7 @@ where SupportedProtocol::LightClientFinalityUpdateV1 => { Ok((RequestType::LightClientFinalityUpdate, socket)) } + // ExecutionProofStatus now carries a 40-byte body; fall through to normal decoder. _ => { match tokio::time::timeout( Duration::from_secs(REQUEST_TIMEOUT), @@ -784,6 +805,7 @@ pub enum RequestType { LightClientUpdatesByRange(LightClientUpdatesByRangeRequest), ExecutionProofsByRange(ExecutionProofsByRangeRequest), ExecutionProofsByRoot(ExecutionProofsByRootRequest), + ExecutionProofStatus(ExecutionProofStatus), Ping(Ping), MetaData(MetadataRequest), } @@ -816,6 +838,7 @@ impl RequestType { (req.block_roots.len() as u64) .saturating_mul(MaxExecutionProofsPerPayload::to_u64()) } + RequestType::ExecutionProofStatus(_) => 1, } } @@ -857,6 +880,7 @@ impl RequestType { } RequestType::ExecutionProofsByRange(_) => SupportedProtocol::ExecutionProofsByRangeV1, RequestType::ExecutionProofsByRoot(_) => SupportedProtocol::ExecutionProofsByRootV1, + RequestType::ExecutionProofStatus(_) => SupportedProtocol::ExecutionProofStatusV1, } } @@ -882,6 +906,7 @@ impl RequestType { RequestType::LightClientFinalityUpdate => unreachable!(), RequestType::LightClientOptimisticUpdate => unreachable!(), RequestType::LightClientUpdatesByRange(_) => unreachable!(), + RequestType::ExecutionProofStatus(_) => unreachable!(), } } @@ -953,6 +978,10 @@ impl RequestType { SupportedProtocol::ExecutionProofsByRootV1, Encoding::SSZSnappy, )], + RequestType::ExecutionProofStatus(_) => vec![ProtocolId::new( + SupportedProtocol::ExecutionProofStatusV1, + Encoding::SSZSnappy, + )], } } @@ -974,6 +1003,7 @@ impl RequestType { RequestType::LightClientUpdatesByRange(_) => true, RequestType::ExecutionProofsByRange(_) => false, RequestType::ExecutionProofsByRoot(_) => false, + RequestType::ExecutionProofStatus(_) => true, } } } @@ -1097,6 +1127,9 @@ impl std::fmt::Display for RequestType { } RequestType::ExecutionProofsByRange(req) => write!(f, "{}", req), RequestType::ExecutionProofsByRoot(req) => write!(f, "{}", req), + RequestType::ExecutionProofStatus(s) => { + write!(f, "ExecutionProofStatus(slot={})", s.slot) + } } } } diff --git a/beacon_node/lighthouse_network/src/rpc/rate_limiter.rs b/beacon_node/lighthouse_network/src/rpc/rate_limiter.rs index b4740afd066..84f57fcdffe 100644 --- a/beacon_node/lighthouse_network/src/rpc/rate_limiter.rs +++ b/beacon_node/lighthouse_network/src/rpc/rate_limiter.rs @@ -117,6 +117,8 @@ pub struct RPCRateLimiter { ep_by_range_rl: Limiter, /// ExecutionProofsByRoot rate limiter. ep_by_root_rl: Limiter, + /// ExecutionProofStatus rate limiter. + ep_status_rl: Limiter, fork_context: Arc, } @@ -164,6 +166,8 @@ pub struct RPCRateLimiterBuilder { ep_by_range_quota: Option, /// Quota for the ExecutionProofsByRoot protocol. ep_by_root_quota: Option, + /// Quota for the ExecutionProofStatus protocol. + ep_status_quota: Option, } impl RPCRateLimiterBuilder { @@ -187,6 +191,7 @@ impl RPCRateLimiterBuilder { Protocol::LightClientUpdatesByRange => self.lc_updates_by_range_quota = q, Protocol::ExecutionProofsByRange => self.ep_by_range_quota = q, Protocol::ExecutionProofsByRoot => self.ep_by_root_quota = q, + Protocol::ExecutionProofStatus => self.ep_status_quota = q, } self } @@ -239,6 +244,10 @@ impl RPCRateLimiterBuilder { .ep_by_root_quota .ok_or("ExecutionProofsByRoot quota not specified")?; + let ep_status_quota = self + .ep_status_quota + .ok_or("ExecutionProofStatus quota not specified")?; + // create the rate limiters let ping_rl = Limiter::from_quota(ping_quota)?; let metadata_rl = Limiter::from_quota(metadata_quota)?; @@ -256,6 +265,7 @@ impl RPCRateLimiterBuilder { let lc_updates_by_range_rl = Limiter::from_quota(lc_updates_by_range_quota)?; let ep_by_range_rl = Limiter::from_quota(ep_by_range_quota)?; let ep_by_root_rl = Limiter::from_quota(ep_by_root_quota)?; + let ep_status_rl = Limiter::from_quota(ep_status_quota)?; // check for peers to prune every 30 seconds, starting in 30 seconds let prune_every = tokio::time::Duration::from_secs(30); @@ -281,6 +291,7 @@ impl RPCRateLimiterBuilder { lc_updates_by_range_rl, ep_by_range_rl, ep_by_root_rl, + ep_status_rl, init_time: Instant::now(), fork_context, }) @@ -336,6 +347,7 @@ impl RPCRateLimiter { light_client_updates_by_range_quota, execution_proofs_by_range_quota, execution_proofs_by_root_quota, + execution_proof_status_quota, } = config; Self::builder() @@ -362,8 +374,15 @@ impl RPCRateLimiter { Protocol::LightClientUpdatesByRange, light_client_updates_by_range_quota, ) - .set_quota(Protocol::ExecutionProofsByRange, execution_proofs_by_range_quota) - .set_quota(Protocol::ExecutionProofsByRoot, execution_proofs_by_root_quota) + .set_quota( + Protocol::ExecutionProofsByRange, + execution_proofs_by_range_quota, + ) + .set_quota( + Protocol::ExecutionProofsByRoot, + execution_proofs_by_root_quota, + ) + .set_quota(Protocol::ExecutionProofStatus, execution_proof_status_quota) .build(fork_context) } @@ -404,6 +423,7 @@ impl RPCRateLimiter { Protocol::LightClientUpdatesByRange => &mut self.lc_updates_by_range_rl, Protocol::ExecutionProofsByRange => &mut self.ep_by_range_rl, Protocol::ExecutionProofsByRoot => &mut self.ep_by_root_rl, + Protocol::ExecutionProofStatus => &mut self.ep_status_rl, }; check(limiter) } @@ -430,6 +450,7 @@ impl RPCRateLimiter { lc_updates_by_range_rl, ep_by_range_rl, ep_by_root_rl, + ep_status_rl, fork_context: _, } = self; @@ -449,6 +470,7 @@ impl RPCRateLimiter { lc_updates_by_range_rl.prune(time_since_start); ep_by_range_rl.prune(time_since_start); ep_by_root_rl.prune(time_since_start); + ep_status_rl.prune(time_since_start); } } diff --git a/beacon_node/lighthouse_network/src/service/api_types.rs b/beacon_node/lighthouse_network/src/service/api_types.rs index 9adf4551165..e2babc31f6e 100644 --- a/beacon_node/lighthouse_network/src/service/api_types.rs +++ b/beacon_node/lighthouse_network/src/service/api_types.rs @@ -1,4 +1,6 @@ -use crate::rpc::methods::{ResponseTermination, RpcResponse, RpcSuccessResponse, StatusMessage}; +use crate::rpc::methods::{ + ExecutionProofStatus, ResponseTermination, RpcResponse, RpcSuccessResponse, StatusMessage, +}; use libp2p::PeerId; use std::fmt::{Display, Formatter}; use std::sync::Arc; @@ -35,6 +37,8 @@ pub enum SyncRequestId { ExecutionProofsByRange(ExecutionProofsByRangeRequestId), /// Execution proofs by root request ExecutionProofsByRoot(ExecutionProofsByRootRequestId), + /// Execution proof status request + ExecutionProofStatus(ExecutionProofStatusRequestId), } /// Request ID for data_columns_by_root requests. Block lookups do not issue this request directly. @@ -92,6 +96,12 @@ pub struct ExecutionProofsByRootRequestId { pub id: Id, } +/// Request ID for execution_proof_status requests. +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub struct ExecutionProofStatusRequestId { + pub id: Id, +} + /// Block components by range request for range sync. Includes an ID for downstream consumers to /// handle retries and tie all their sub requests together. #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] @@ -193,6 +203,8 @@ pub enum Response { ExecutionProofsByRange(Option>), /// A response to a get EXECUTION_PROOFS_BY_ROOT request. A None response signals end of batch. ExecutionProofsByRoot(Option>), + /// A response to an EXECUTION_PROOF_STATUS request. + ExecutionProofStatus(ExecutionProofStatus), } impl std::convert::From> for RpcResponse { @@ -240,16 +252,15 @@ impl std::convert::From> for RpcResponse { }, Response::ExecutionProofsByRange(r) => match r { Some(p) => RpcResponse::Success(RpcSuccessResponse::ExecutionProofsByRange(p)), - None => { - RpcResponse::StreamTermination(ResponseTermination::ExecutionProofsByRange) - } + None => RpcResponse::StreamTermination(ResponseTermination::ExecutionProofsByRange), }, Response::ExecutionProofsByRoot(r) => match r { Some(p) => RpcResponse::Success(RpcSuccessResponse::ExecutionProofsByRoot(p)), - None => { - RpcResponse::StreamTermination(ResponseTermination::ExecutionProofsByRoot) - } + None => RpcResponse::StreamTermination(ResponseTermination::ExecutionProofsByRoot), }, + Response::ExecutionProofStatus(s) => { + RpcResponse::Success(RpcSuccessResponse::ExecutionProofStatus(s)) + } } } } @@ -269,6 +280,7 @@ macro_rules! impl_display { // not losing information impl_display!(ExecutionProofsByRangeRequestId, "ExecProofsByRange/{}", id); impl_display!(ExecutionProofsByRootRequestId, "ExecProofsByRoot/{}", id); +impl_display!(ExecutionProofStatusRequestId, "ExecProofStatus/{}", id); impl_display!(BlocksByRangeRequestId, "{}/{}", id, parent_request_id); impl_display!(BlobsByRangeRequestId, "{}/{}", id, parent_request_id); impl_display!(DataColumnsByRangeRequestId, "{}/{}", id, parent_request_id); diff --git a/beacon_node/lighthouse_network/src/service/mod.rs b/beacon_node/lighthouse_network/src/service/mod.rs index 57d25f98123..90550884f04 100644 --- a/beacon_node/lighthouse_network/src/service/mod.rs +++ b/beacon_node/lighthouse_network/src/service/mod.rs @@ -9,7 +9,7 @@ use crate::peer_manager::{ peerdb::score::PeerAction, peerdb::score::ReportSource, }; use crate::peer_manager::{MIN_OUTBOUND_ONLY_FACTOR, PEER_EXCESS_FACTOR, PRIORITY_PEER_EXCESS}; -use crate::rpc::methods::MetadataRequest; +use crate::rpc::methods::{ExecutionProofStatus, MetadataRequest}; use crate::rpc::{ GoodbyeReason, HandlerErr, InboundRequestId, Protocol, RPC, RPCError, RPCMessage, RPCReceived, RequestType, ResponseTermination, RpcResponse, RpcSuccessResponse, @@ -105,6 +105,11 @@ pub enum NetworkEvent { ZeroListeners, /// A peer has an updated custody group count from MetaData. PeerUpdatedCustodyGroupCount(PeerId), + /// A peer sent us their `ExecutionProofStatus` in the body of an inbound request. + PeerExecutionProofStatus { + peer_id: PeerId, + status: ExecutionProofStatus, + }, } pub type Gossipsub = gossipsub::Behaviour; @@ -1634,6 +1639,20 @@ impl Network { request_type, }) } + RequestType::ExecutionProofStatus(peer_status) => { + // Respond immediately with our local status. + let local_status = + *self.network_globals.local_execution_proof_status.read(); + let response = RpcResponse::Success( + RpcSuccessResponse::ExecutionProofStatus(local_status), + ); + self.send_response(peer_id, inbound_request_id, response); + // Route peer's status to sync layer so it populates the ProofSync cache. + Some(NetworkEvent::PeerExecutionProofStatus { + peer_id, + status: peer_status, + }) + } } } Ok(RPCReceived::Response(id, resp)) => { @@ -1694,11 +1713,18 @@ impl Network { peer_id, Response::LightClientUpdatesByRange(Some(update)), ), - RpcSuccessResponse::ExecutionProofsByRange(proof) => { - self.build_response(id, peer_id, Response::ExecutionProofsByRange(Some(proof))) - } - RpcSuccessResponse::ExecutionProofsByRoot(proof) => { - self.build_response(id, peer_id, Response::ExecutionProofsByRoot(Some(proof))) + RpcSuccessResponse::ExecutionProofsByRange(proof) => self.build_response( + id, + peer_id, + Response::ExecutionProofsByRange(Some(proof)), + ), + RpcSuccessResponse::ExecutionProofsByRoot(proof) => self.build_response( + id, + peer_id, + Response::ExecutionProofsByRoot(Some(proof)), + ), + RpcSuccessResponse::ExecutionProofStatus(status) => { + self.build_response(id, peer_id, Response::ExecutionProofStatus(status)) } } } diff --git a/beacon_node/lighthouse_network/src/types/globals.rs b/beacon_node/lighthouse_network/src/types/globals.rs index 9bea929aa0d..681c6bcc642 100644 --- a/beacon_node/lighthouse_network/src/types/globals.rs +++ b/beacon_node/lighthouse_network/src/types/globals.rs @@ -1,7 +1,7 @@ //! A collection of variables that are accessible outside of the network thread itself. use super::TopicConfig; use crate::peer_manager::peerdb::PeerDB; -use crate::rpc::{MetaData, MetaDataV3}; +use crate::rpc::{MetaData, MetaDataV3, methods::ExecutionProofStatus}; use crate::types::{BackFillState, SyncState}; use crate::{Client, Enr, GossipTopic, Multiaddr, NetworkConfig, PeerId}; use eth2::lighthouse::sync_state::CustodyBackFillState; @@ -24,6 +24,13 @@ pub struct NetworkGlobals { pub peers: RwLock>, // The local meta data of our node. pub local_metadata: RwLock>, + /// The local execution proof status of our node. + /// + /// Updated via `set_local_execution_proof_status` whenever the beacon chain + /// successfully verifies an execution proof (see `verify_execution_proof` in + /// `beacon_chain.rs`). Sent to peers during `ExecutionProofStatus` RPC exchanges + /// so they can use our status for peer selection. + pub local_execution_proof_status: RwLock, /// The current gossipsub topic subscriptions. pub gossipsub_subscriptions: RwLock>, /// The current sync status of the node. @@ -90,6 +97,7 @@ impl NetworkGlobals { peer_id: RwLock::new(enr.peer_id()), listen_multiaddrs: RwLock::new(Vec::new()), local_metadata: RwLock::new(local_metadata), + local_execution_proof_status: RwLock::new(ExecutionProofStatus::default()), peers: RwLock::new(PeerDB::new(trusted_peers, disable_peer_scoring)), gossipsub_subscriptions: RwLock::new(HashSet::new()), sync_state: RwLock::new(SyncState::Stalled), @@ -186,6 +194,16 @@ impl NetworkGlobals { self.peers.read().trusted_peers() } + /// Returns the local execution proof status. + pub fn local_execution_proof_status(&self) -> ExecutionProofStatus { + *self.local_execution_proof_status.read() + } + + /// Updates the local execution proof status. + pub fn set_local_execution_proof_status(&self, status: ExecutionProofStatus) { + *self.local_execution_proof_status.write() = status; + } + /// Updates the syncing state of the node. /// /// The old state is returned diff --git a/beacon_node/network/src/network_beacon_processor/gossip_methods.rs b/beacon_node/network/src/network_beacon_processor/gossip_methods.rs index 597333b9002..02e5da8f9ea 100644 --- a/beacon_node/network/src/network_beacon_processor/gossip_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/gossip_methods.rs @@ -7,6 +7,7 @@ use crate::{ use beacon_chain::blob_verification::{GossipBlobError, GossipVerifiedBlob}; use beacon_chain::block_verification_types::AsBlock; use beacon_chain::data_column_verification::{GossipDataColumnError, GossipVerifiedDataColumn}; +use beacon_chain::events::{EventKind, SseExecutionProofValidated}; use beacon_chain::store::Error; use beacon_chain::{ AvailabilityProcessingStatus, BeaconChainError, BeaconChainTypes, BlockError, ForkChoiceError, @@ -20,6 +21,7 @@ use beacon_chain::{ validator_monitor::{get_block_delay_ms, get_slot_delay_ms}, }; use beacon_processor::{Work, WorkEvent}; +use lighthouse_network::rpc::methods::ExecutionProofStatus; use lighthouse_network::{Client, MessageAcceptance, MessageId, PeerAction, PeerId, ReportSource}; use lighthouse_tracing::{ SPAN_PROCESS_GOSSIP_BLOB, SPAN_PROCESS_GOSSIP_BLOCK, SPAN_PROCESS_GOSSIP_DATA_COLUMN, @@ -1876,9 +1878,31 @@ impl NetworkBeaconProcessor { let proof_type = execution_proof.proof_type(); let validator_index = execution_proof.validator_index(); + // Extract the inner proof before moving execution_proof into verification. + let execution_proof_message = execution_proof.message.clone(); + // Verify the execution proof. let verification_result = self.chain.verify_execution_proof(execution_proof).await; + // If we have a execution proof subscriber we assume a validator will resign the proof and therefore we do not propagate this proof to peers. + // We will wait for the validator to sign and submit the proof for gossip. + let gossip_behaviour = if let Ok((proof_status, block)) = &verification_result + && (proof_status.is_valid() || proof_status.is_accepted()) + && let Some(event_handler) = self.chain.event_handler.as_ref() + && event_handler.has_execution_proof_validated_subscribers() + && let Some((_block_root, slot)) = block + { + event_handler.register(EventKind::ExecutionProofValidated( + SseExecutionProofValidated { + execution_proof: execution_proof_message, + epoch: slot.epoch(T::EthSpec::slots_per_epoch()).as_u64(), + }, + )); + MessageAcceptance::Ignore + } else { + MessageAcceptance::Accept + }; + match verification_result { // TODO: split our error types and penalize accordingly Err(e) => { @@ -1896,14 +1920,21 @@ impl NetworkBeaconProcessor { "invalid_execution_proof", ); } - Ok(ProofStatus::Valid) => { + Ok((ProofStatus::Valid, verified_block)) => { debug!( ?request_root, validator_index, proof_type, "Execution proof is valid" ); - self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Accept); + if let Some((block_root, slot)) = verified_block { + self.network_globals + .set_local_execution_proof_status(ExecutionProofStatus { + slot: slot.as_u64(), + block_root, + }); + } + self.propagate_validation_result(message_id, peer_id, gossip_behaviour); } - Ok(ProofStatus::Invalid) => { + Ok((ProofStatus::Invalid, _)) => { debug!( ?request_root, %peer_id, @@ -1912,16 +1943,16 @@ impl NetworkBeaconProcessor { self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Reject); self.gossip_penalize_peer(peer_id, PeerAction::Fatal, "invalid_execution_proof"); } - Ok(ProofStatus::Accepted) => { + Ok((ProofStatus::Accepted, _)) => { debug!( ?request_root, validator_index, proof_type, "Execution proof is accepted but not fully verified" ); - self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Accept); + self.propagate_validation_result(message_id, peer_id, gossip_behaviour); } - Ok(ProofStatus::Syncing) => { + Ok((ProofStatus::Syncing, _)) => { debug!( ?request_root, validator_index, @@ -1931,7 +1962,7 @@ impl NetworkBeaconProcessor { self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore); } // TODO: Should we do this check earlier. This is a quick and cheap check, so it may be better to do it before the more expensive verification steps. - Ok(ProofStatus::NotSupported) => { + Ok((ProofStatus::NotSupported, _)) => { debug!( ?request_root, validator_index, proof_type, "Execution proof type not supported" @@ -1956,10 +1987,17 @@ impl NetworkBeaconProcessor { Err(e) => { debug!(%peer_id, error = ?e, "Error verifying RPC execution proof"); } - Ok(ProofStatus::Valid) => { + Ok((ProofStatus::Valid, verified_block)) => { debug!(%peer_id, "RPC execution proof valid"); + if let Some((block_root, slot)) = verified_block { + self.network_globals + .set_local_execution_proof_status(ExecutionProofStatus { + slot: slot.as_u64(), + block_root, + }); + } } - Ok(ProofStatus::Invalid) => { + Ok((ProofStatus::Invalid, _)) => { debug!(%peer_id, "RPC execution proof invalid, penalizing peer"); self.send_network_message(NetworkMessage::ReportPeer { peer_id, @@ -1968,13 +2006,13 @@ impl NetworkBeaconProcessor { msg: "invalid_rpc_execution_proof", }); } - Ok(ProofStatus::Accepted) => { + Ok((ProofStatus::Accepted, _)) => { debug!(%peer_id, "RPC execution proof accepted"); } - Ok(ProofStatus::NotSupported) => { + Ok((ProofStatus::NotSupported, _)) => { debug!(%peer_id, "RPC execution proof type not supported by local engine"); } - Ok(ProofStatus::Syncing) => { + Ok((ProofStatus::Syncing, _)) => { debug!(%peer_id, "RPC execution proof received while block still syncing"); } } diff --git a/beacon_node/network/src/network_beacon_processor/mod.rs b/beacon_node/network/src/network_beacon_processor/mod.rs index 6f00ef75cf6..15d618c181f 100644 --- a/beacon_node/network/src/network_beacon_processor/mod.rs +++ b/beacon_node/network/src/network_beacon_processor/mod.rs @@ -717,11 +717,7 @@ impl NetworkBeaconProcessor { ) -> Result<(), Error> { let processor = self.clone(); let process_fn = move || { - processor.handle_execution_proofs_by_range_request( - peer_id, - inbound_request_id, - request, - ) + processor.handle_execution_proofs_by_range_request(peer_id, inbound_request_id, request) }; self.try_send(BeaconWorkEvent { diff --git a/beacon_node/network/src/network_beacon_processor/rpc_methods.rs b/beacon_node/network/src/network_beacon_processor/rpc_methods.rs index 13a63eb10a7..345e008e1ff 100644 --- a/beacon_node/network/src/network_beacon_processor/rpc_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/rpc_methods.rs @@ -1354,8 +1354,11 @@ impl NetworkBeaconProcessor { "Received ExecutionProofsByRange Request" ); - let block_roots = - self.get_block_roots_for_slot_range(req.start_slot, req.count, "ExecutionProofsByRange")?; + let block_roots = self.get_block_roots_for_slot_range( + req.start_slot, + req.count, + "ExecutionProofsByRange", + )?; let mut proofs_sent = 0usize; for block_root in block_roots { diff --git a/beacon_node/network/src/router.rs b/beacon_node/network/src/router.rs index be447cf1a29..a6e2e75c107 100644 --- a/beacon_node/network/src/router.rs +++ b/beacon_node/network/src/router.rs @@ -12,6 +12,7 @@ use crate::sync::SyncMessage; use beacon_chain::{BeaconChain, BeaconChainTypes}; use beacon_processor::{BeaconProcessorSend, DuplicateCache}; use futures::prelude::*; +use lighthouse_network::rpc::methods::ExecutionProofStatus; use lighthouse_network::rpc::*; use lighthouse_network::{ MessageId, NetworkGlobals, PeerId, PubsubMessage, Response, @@ -24,8 +25,8 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; use tracing::{debug, error, trace, warn}; -use types::{BlobSidecar, DataColumnSidecar, EthSpec, ForkContext, SignedBeaconBlock}; use types::execution::eip8025::SignedExecutionProof; +use types::{BlobSidecar, DataColumnSidecar, EthSpec, ForkContext, SignedBeaconBlock}; /// Handles messages from the network and routes them to the appropriate service to be handled. pub struct Router { @@ -74,6 +75,11 @@ pub enum RouterMessage { StatusPeer(PeerId), /// The peer has an updated custody group count from METADATA. PeerUpdatedCustodyGroupCount(PeerId), + /// A peer sent their `ExecutionProofStatus` as an inbound request body. + PeerExecutionProofStatus { + peer_id: PeerId, + status: ExecutionProofStatus, + }, } impl Router { @@ -181,6 +187,13 @@ impl Router { RouterMessage::PubsubMessage(id, peer_id, gossip, should_process) => { self.handle_gossip(id, peer_id, gossip, should_process); } + RouterMessage::PeerExecutionProofStatus { peer_id, status } => { + self.send_to_sync(SyncMessage::RpcExecutionProofStatus { + peer_id, + request_id: None, + status, + }); + } } } @@ -329,11 +342,18 @@ impl Router { self.on_data_columns_by_range_response(peer_id, app_request_id, data_column); } Response::ExecutionProofsByRange(execution_proof) => { - self.on_execution_proofs_by_range_response(peer_id, app_request_id, execution_proof); + self.on_execution_proofs_by_range_response( + peer_id, + app_request_id, + execution_proof, + ); } Response::ExecutionProofsByRoot(execution_proof) => { self.on_execution_proofs_by_root_response(peer_id, app_request_id, execution_proof); } + Response::ExecutionProofStatus(status) => { + self.on_execution_proof_status_response(peer_id, app_request_id, status); + } // Light client responses should not be received Response::LightClientBootstrap(_) | Response::LightClientOptimisticUpdate(_) @@ -802,6 +822,27 @@ impl Router { } } + fn on_execution_proof_status_response( + &mut self, + peer_id: PeerId, + app_request_id: AppRequestId, + status: ExecutionProofStatus, + ) { + // `request_id` is `Some` here because this is an outbound response (the peer responded + // to our request). The `None` case is for inbound requests (the peer sent us their status + // unsolicited) and is handled via `RouterMessage::PeerExecutionProofStatus`. + if let AppRequestId::Sync(SyncRequestId::ExecutionProofStatus(request_id)) = app_request_id + { + self.send_to_sync(SyncMessage::RpcExecutionProofStatus { + peer_id, + request_id: Some(request_id), + status, + }); + } else { + debug!(%peer_id, "ExecutionProofStatus response with unexpected request id"); + } + } + fn handle_beacon_processor_send_result( &mut self, result: Result<(), crate::network_beacon_processor::Error>, diff --git a/beacon_node/network/src/service.rs b/beacon_node/network/src/service.rs index 0869b442aec..f76198eb4c0 100644 --- a/beacon_node/network/src/service.rs +++ b/beacon_node/network/src/service.rs @@ -560,6 +560,9 @@ impl NetworkService { } } } + NetworkEvent::PeerExecutionProofStatus { peer_id, status } => { + self.send_to_router(RouterMessage::PeerExecutionProofStatus { peer_id, status }); + } NetworkEvent::NewListenAddr(multiaddr) => { self.network_globals .listen_multiaddrs diff --git a/beacon_node/network/src/sync/backfill_sync/mod.rs b/beacon_node/network/src/sync/backfill_sync/mod.rs index 9802ec56a16..e6b1a4b6a9d 100644 --- a/beacon_node/network/src/sync/backfill_sync/mod.rs +++ b/beacon_node/network/src/sync/backfill_sync/mod.rs @@ -82,8 +82,10 @@ pub enum SyncStart { /// The chain started syncing or is already syncing. Syncing { /// The number of slots that have been processed so far. + #[cfg_attr(feature = "disable-backfill", allow(dead_code))] completed: usize, /// The number of slots still to be processed. + #[cfg_attr(feature = "disable-backfill", allow(dead_code))] remaining: usize, }, /// The chain didn't start syncing. @@ -156,6 +158,7 @@ pub struct BackFillSync { network_globals: Arc>, } +#[cfg_attr(feature = "disable-backfill", allow(dead_code))] impl BackFillSync { pub fn new( beacon_chain: Arc>, @@ -1192,6 +1195,7 @@ impl BackFillSync { } /// Error kind for attempting to restart the sync from beacon chain parameters. +#[cfg_attr(feature = "disable-backfill", allow(dead_code))] enum ResetEpochError { /// The chain has already completed. SyncCompleted, diff --git a/beacon_node/network/src/sync/block_sidecar_coupling.rs b/beacon_node/network/src/sync/block_sidecar_coupling.rs index 6f563820f7c..7468aae428e 100644 --- a/beacon_node/network/src/sync/block_sidecar_coupling.rs +++ b/beacon_node/network/src/sync/block_sidecar_coupling.rs @@ -196,9 +196,7 @@ impl RangeBlockComponentsRequest { &mut self, spec: &ChainSpec, ) -> Option>, CouplingError>> { - let Some(blocks) = self.blocks_request.to_finished() else { - return None; - }; + let blocks = self.blocks_request.to_finished()?; // Increment the attempt once this function returns the response or errors match &mut self.block_data_request { @@ -206,9 +204,7 @@ impl RangeBlockComponentsRequest { Some(Self::responses_with_blobs(blocks.to_vec(), vec![], spec)) } RangeBlockDataRequest::Blobs(request) => { - let Some(blobs) = request.to_finished() else { - return None; - }; + let blobs = request.to_finished()?; Some(Self::responses_with_blobs( blocks.to_vec(), blobs.to_vec(), @@ -224,9 +220,7 @@ impl RangeBlockComponentsRequest { let mut data_columns = vec![]; let mut column_to_peer_id: HashMap = HashMap::new(); for req in requests.values() { - let Some(data) = req.to_finished() else { - return None; - }; + let data = req.to_finished()?; data_columns.extend(data.clone()) } diff --git a/beacon_node/network/src/sync/custody_backfill_sync/mod.rs b/beacon_node/network/src/sync/custody_backfill_sync/mod.rs index bb2c6799f1d..0b40731f293 100644 --- a/beacon_node/network/src/sync/custody_backfill_sync/mod.rs +++ b/beacon_node/network/src/sync/custody_backfill_sync/mod.rs @@ -125,6 +125,7 @@ pub struct CustodyBackFillSync { network_globals: Arc>, } +#[cfg_attr(feature = "disable-backfill", allow(dead_code))] impl CustodyBackFillSync { pub fn new( beacon_chain: Arc>, @@ -378,9 +379,7 @@ impl CustodyBackFillSync { /// Creates the next required batch from the chain. If there are no more batches required, /// `None` is returned. fn include_next_batch(&mut self) -> Option { - let Some(column_da_boundary) = self.beacon_chain.get_column_da_boundary() else { - return None; - }; + let column_da_boundary = self.beacon_chain.get_column_da_boundary()?; // Skip all batches (Epochs) that don't have missing columns. for epoch in Epoch::range_inclusive_rev(self.to_be_downloaded, column_da_boundary) { diff --git a/beacon_node/network/src/sync/manager.rs b/beacon_node/network/src/sync/manager.rs index 5cd32b2efa4..b9b76ad2318 100644 --- a/beacon_node/network/src/sync/manager.rs +++ b/beacon_node/network/src/sync/manager.rs @@ -33,7 +33,9 @@ //! needs to be searched for (i.e if an attestation references an unknown block) this manager can //! search for the block and subsequently search for parents if needed. -use super::backfill_sync::{BackFillSync, ProcessResult, SyncStart}; +#[cfg(not(feature = "disable-backfill"))] +use super::backfill_sync::SyncStart; +use super::backfill_sync::{BackFillSync, ProcessResult}; use super::block_lookups::BlockLookups; use super::network_context::{ CustodyByRootResult, RangeBlockComponent, RangeRequestId, RpcEvent, SyncNetworkContext, @@ -57,11 +59,13 @@ use beacon_chain::{ use futures::StreamExt; use lighthouse_network::SyncInfo; use lighthouse_network::rpc::RPCError; +use lighthouse_network::rpc::methods::ExecutionProofStatus; use lighthouse_network::service::api_types::{ BlobsByRangeRequestId, BlocksByRangeRequestId, ComponentsByRangeRequestId, CustodyBackFillBatchRequestId, CustodyBackfillBatchId, CustodyRequester, DataColumnsByRangeRequestId, DataColumnsByRangeRequester, DataColumnsByRootRequestId, - DataColumnsByRootRequester, Id, SingleLookupReqId, SyncRequestId, + DataColumnsByRootRequester, ExecutionProofStatusRequestId, Id, SingleLookupReqId, + SyncRequestId, }; use lighthouse_network::types::{NetworkGlobals, SyncState}; use lighthouse_network::{PeerAction, PeerId}; @@ -141,6 +145,15 @@ pub enum SyncMessage { execution_proof: Option>, }, + /// An ExecutionProofStatus response has been received from the RPC (outbound), + /// or a peer has sent us their status in an inbound request body. + /// `request_id` is `None` for the inbound (peer-initiated) case. + RpcExecutionProofStatus { + peer_id: PeerId, + request_id: Option, + status: ExecutionProofStatus, + }, + /// A block with an unknown parent has been received. UnknownParentBlock(PeerId, Arc>, Hash256), @@ -188,6 +201,7 @@ pub enum SyncMessage { /// The type of processing specified for a received block. #[derive(Debug, Clone)] +#[allow(clippy::enum_variant_names)] pub enum BlockProcessType { SingleBlock { id: Id }, SingleBlob { id: Id }, @@ -390,36 +404,18 @@ impl SyncManager { } #[cfg(test)] - pub(crate) fn proof_sync_state(&self) -> super::proof_sync::ProofSyncState { - self.proof_sync.state() - } - - #[cfg(test)] - pub(crate) fn proof_sync_in_flight_count(&self) -> usize { - self.proof_sync.in_flight_count() + pub(crate) fn proof_sync(&self) -> &super::proof_sync::ProofSync { + &self.proof_sync } #[cfg(test)] - pub(crate) fn set_proof_sync_missing( - &mut self, - missing: Vec, - ) { - self.proof_sync.test_missing_proofs = Some(missing); + pub(crate) fn proof_sync_mut(&mut self) -> &mut super::proof_sync::ProofSync { + &mut self.proof_sync } #[cfg(test)] pub(crate) fn start_proof_sync(&mut self) { - self.proof_sync.start(); - } - - #[cfg(test)] - pub(crate) fn pause_proof_sync(&mut self) { - self.proof_sync.pause(); - } - - #[cfg(test)] - pub(crate) fn force_proof_sync_fill_mode(&mut self) { - self.proof_sync.enter_fill_mode_for_testing(); + self.proof_sync.start(&mut self.network); } fn network_globals(&self) -> &NetworkGlobals { @@ -477,6 +473,10 @@ impl SyncManager { } } + if self.network.is_proof_capable_peer(&peer_id) { + self.proof_sync.add_peer(peer_id, &mut self.network); + } + self.update_sync_state(); // Try to make progress on custody requests that are waiting for peers @@ -559,9 +559,15 @@ impl SyncManager { } SyncRequestId::ExecutionProofsByRange(req_id) => { debug!(%peer_id, ?req_id, "Execution proofs by range request failed"); + self.proof_sync.on_range_request_error(&req_id); } SyncRequestId::ExecutionProofsByRoot(req_id) => { debug!(%peer_id, ?req_id, "Execution proofs by root request failed"); + self.proof_sync.on_root_request_error(&req_id); + } + SyncRequestId::ExecutionProofStatus(id) => { + self.proof_sync + .on_peer_execution_proof_status_error(peer_id, id); } } } @@ -582,6 +588,7 @@ impl SyncManager { self.range_sync.peer_disconnect(&mut self.network, peer_id); let _ = self.backfill_sync.peer_disconnected(peer_id); self.block_lookups.peer_disconnected(peer_id); + self.proof_sync.on_proof_capable_peer_disconnected(peer_id); // Regardless of the outcome, we update the sync status. self.update_sync_state(); @@ -671,6 +678,7 @@ impl SyncManager { // If we synced a peer between status messages, most likely the peer has // advanced and will produce a head chain on re-status. Otherwise it will shift // to being synced + #[cfg_attr(feature = "disable-backfill", allow(unused_mut))] let mut sync_state = { let head = self.chain.best_slot(); let current_slot = self.chain.slot().unwrap_or_else(|_| Slot::new(0)); @@ -791,7 +799,7 @@ impl SyncManager { if new_state.is_synced() && !old_state.is_synced() { self.network.subscribe_core_topics(); if self.network_globals().config.enable_execution_proof { - self.proof_sync.start(); + self.proof_sync.start(&mut self.network); } } } @@ -907,6 +915,14 @@ impl SyncManager { } => { self.rpc_execution_proof_received(sync_request_id, peer_id, execution_proof); } + SyncMessage::RpcExecutionProofStatus { + peer_id, + request_id, + status, + } => { + self.proof_sync + .on_peer_execution_proof_status(peer_id, request_id, status); + } SyncMessage::UnknownParentBlock(peer_id, block, block_root) => { let block_slot = block.slot(); let parent_root = block.parent_root(); @@ -1270,11 +1286,9 @@ impl SyncManager { // Stream termination: clean up tracking map entry. match &sync_request_id { SyncRequestId::ExecutionProofsByRange(id) => { - self.network.on_execution_proofs_by_range_terminated(id); self.proof_sync.on_range_request_terminated(id); } SyncRequestId::ExecutionProofsByRoot(id) => { - self.network.on_execution_proofs_by_root_terminated(id); self.proof_sync.on_request_terminated(id); } other => { diff --git a/beacon_node/network/src/sync/network_context.rs b/beacon_node/network/src/sync/network_context.rs index ec59decd9e9..165e2e5bc32 100644 --- a/beacon_node/network/src/sync/network_context.rs +++ b/beacon_node/network/src/sync/network_context.rs @@ -21,10 +21,9 @@ use beacon_chain::block_verification_types::RpcBlock; use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessStatus, EngineState}; use custody::CustodyRequestResult; use fnv::FnvHashMap; -use lighthouse_network::Eth2Enr; use lighthouse_network::rpc::methods::{ - BlobsByRangeRequest, DataColumnsByRangeRequest, ExecutionProofsByRangeRequest, - ExecutionProofsByRootRequest, + BlobsByRangeRequest, DataColumnsByRangeRequest, ExecutionProofStatus, + ExecutionProofsByRangeRequest, ExecutionProofsByRootRequest, }; use lighthouse_network::rpc::{BlocksByRangeRequest, GoodbyeReason, RPCError, RequestType}; pub use lighthouse_network::service::api_types::RangeRequestId; @@ -32,9 +31,10 @@ use lighthouse_network::service::api_types::{ AppRequestId, BlobsByRangeRequestId, BlocksByRangeRequestId, ComponentsByRangeRequestId, CustodyBackFillBatchRequestId, CustodyBackfillBatchId, CustodyId, CustodyRequester, DataColumnsByRangeRequestId, DataColumnsByRangeRequester, DataColumnsByRootRequestId, - DataColumnsByRootRequester, ExecutionProofsByRangeRequestId, ExecutionProofsByRootRequestId, - Id, SingleLookupReqId, SyncRequestId, + DataColumnsByRootRequester, ExecutionProofStatusRequestId, ExecutionProofsByRangeRequestId, + ExecutionProofsByRootRequestId, Id, SingleLookupReqId, SyncRequestId, }; +use lighthouse_network::types::Subnet; use lighthouse_network::{Client, NetworkGlobals, PeerAction, PeerId, ReportSource}; use lighthouse_tracing::{SPAN_OUTGOING_BLOCK_BY_ROOT_REQUEST, SPAN_OUTGOING_RANGE_REQUEST}; use parking_lot::RwLock; @@ -101,6 +101,7 @@ pub type CustodyByRootResult = Result<(DataColumnSidecarList, PeerGroup, Duration), RpcResponseError>; #[derive(Debug)] +#[allow(clippy::enum_variant_names)] pub enum RpcResponseError { RpcError(#[allow(dead_code)] RPCError), VerifyError(LookupVerifyError), @@ -119,6 +120,7 @@ pub enum RpcRequestSendError { /// Type of peer missing that caused a `RpcRequestSendError::NoPeers` #[derive(Debug, PartialEq, Eq)] +#[allow(clippy::enum_variant_names)] pub enum NoPeerError { BlockPeer, CustodyPeer(ColumnIndex), @@ -126,6 +128,26 @@ pub enum NoPeerError { ProofPeer, } +/// Age threshold for considering a cached `ExecutionProofStatus` stale enough to re-query. +pub const EXECUTION_PROOF_STATUS_REFRESH_THRESHOLD: std::time::Duration = + std::time::Duration::from_secs(300); + +/// A peer's `ExecutionProofStatus` plus the time it was received and whether it was verified. +pub struct CachedExecutionProofStatus { + pub status: ExecutionProofStatus, + pub timestamp: std::time::Instant, + /// `true` if `status.block_root` was confirmed against our local chain. + /// `false` if the peer's claimed slot was ahead of our head at cache time (optimistic). + pub verified: bool, +} + +impl CachedExecutionProofStatus { + /// Returns `true` if this entry should be refreshed: either it is unverified or has expired. + pub fn needs_refresh(&self) -> bool { + !self.verified || self.timestamp.elapsed() > EXECUTION_PROOF_STATUS_REFRESH_THRESHOLD + } +} + #[derive(Debug, PartialEq, Eq)] pub enum SendErrorProcessor { SendError, @@ -231,11 +253,6 @@ pub struct SyncNetworkContext { custody_backfill_data_column_batch_requests: FnvHashMap>, - /// Tracking map for active ExecutionProofsByRange requests (request ID → serving peer). - execution_proofs_by_range_requests: FnvHashMap, - /// Tracking map for active ExecutionProofsByRoot requests (request ID → serving peer). - execution_proofs_by_root_requests: FnvHashMap, - /// Whether the ee is online. If it's not, we don't allow access to the /// `beacon_processor_send`. execution_engine_state: EngineState, @@ -313,8 +330,6 @@ impl SyncNetworkContext { custody_by_root_requests: <_>::default(), components_by_range_requests: FnvHashMap::default(), custody_backfill_data_column_batch_requests: FnvHashMap::default(), - execution_proofs_by_range_requests: FnvHashMap::default(), - execution_proofs_by_root_requests: FnvHashMap::default(), network_beacon_processor, chain, fork_context, @@ -345,8 +360,6 @@ impl SyncNetworkContext { // components_by_range_requests is a meta request of various _by_range requests components_by_range_requests: _, custody_backfill_data_column_batch_requests: _, - execution_proofs_by_range_requests, - execution_proofs_by_root_requests, execution_engine_state: _, network_beacon_processor: _, chain: _, @@ -377,26 +390,12 @@ impl SyncNetworkContext { .active_requests_of_peer(peer_id) .into_iter() .map(|req_id| SyncRequestId::DataColumnsByRange(*req_id)); - // Collect execution proof request IDs for this peer. These are soft requests and failures - // are handled gracefully (debug log only), so they don't block sync. - let ep_by_range_ids = execution_proofs_by_range_requests - .iter() - .filter(|(_, p)| *p == peer_id) - .map(|(id, _)| SyncRequestId::ExecutionProofsByRange(*id)) - .collect::>(); - let ep_by_root_ids = execution_proofs_by_root_requests - .iter() - .filter(|(_, p)| *p == peer_id) - .map(|(id, _)| SyncRequestId::ExecutionProofsByRoot(*id)) - .collect::>(); blocks_by_root_ids .chain(blobs_by_root_ids) .chain(data_column_by_root_ids) .chain(blocks_by_range_ids) .chain(blobs_by_range_ids) .chain(data_column_by_range_ids) - .chain(ep_by_range_ids) - .chain(ep_by_root_ids) .collect() } @@ -405,31 +404,15 @@ impl SyncNetworkContext { .custody_peers_for_column(column_index) } - /// Returns the first connected peer whose ENR advertises execution proof support (`ep = true`). - fn find_any_proof_capable_peer(&self) -> Option { - let db = self.network_globals().peers.read(); - db.connected_peer_ids() - .find(|peer_id| { - db.peer_info(peer_id) - .and_then(|info| info.enr()) - .map(|enr| enr.execution_proof_enabled()) - .unwrap_or(false) - }) - .copied() - } - - /// Send a `ExecutionProofsByRange` request to any connected proof-capable peer. + /// Send a `ExecutionProofsByRange` request to the given proof-capable peer. /// - /// Returns `Err(NoPeer)` if no connected peer has `ep = true` in their ENR. Callers - /// treat this as a soft failure — gossip serves as the fallback. + /// Callers should use `find_best_proof_capable_peer` to select the peer first. pub fn request_execution_proofs_by_range( &mut self, + peer_id: PeerId, start_slot: Slot, count: u64, ) -> Result { - let peer_id = self - .find_any_proof_capable_peer() - .ok_or(RpcRequestSendError::NoPeer(NoPeerError::ProofPeer))?; let id = ExecutionProofsByRangeRequestId { id: self.next_id() }; let request = ExecutionProofsByRangeRequest { start_slot: start_slot.as_u64(), @@ -450,20 +433,17 @@ impl SyncNetworkContext { %id, "Sync RPC request sent" ); - self.execution_proofs_by_range_requests.insert(id, peer_id); Ok(id) } - /// Send a `ExecutionProofsByRoot` request for `block_root` to any connected proof-capable peer. + /// Send a `ExecutionProofsByRoot` request for `block_root` to the given proof-capable peer. /// - /// Returns `Err(NoPeer)` if no connected peer has `ep = true` in their ENR. + /// Callers should use `find_best_proof_capable_peer` to select the peer first. pub fn request_execution_proofs_by_root( &mut self, + peer_id: PeerId, block_root: Hash256, ) -> Result { - let peer_id = self - .find_any_proof_capable_peer() - .ok_or(RpcRequestSendError::NoPeer(NoPeerError::ProofPeer))?; let max_request_blocks = self .chain .spec @@ -485,27 +465,52 @@ impl SyncNetworkContext { %id, "Sync RPC request sent" ); - self.execution_proofs_by_root_requests.insert(id, peer_id); Ok(id) } - /// Remove a completed (or terminated) `ExecutionProofsByRange` request from the tracking map. - pub fn on_execution_proofs_by_range_terminated( + /// Send an `ExecutionProofStatus` request to `peer_id`. + /// + /// The request body carries our local execution proof status so the peer can cache it. + /// Returns the newly-allocated request ID on success. + pub fn request_execution_proof_status( &mut self, - id: &ExecutionProofsByRangeRequestId, - ) { - self.execution_proofs_by_range_requests.remove(id); - } - - /// Remove a completed (or terminated) `ExecutionProofsByRoot` request from the tracking map. - pub fn on_execution_proofs_by_root_terminated(&mut self, id: &ExecutionProofsByRootRequestId) { - self.execution_proofs_by_root_requests.remove(id); + peer_id: PeerId, + ) -> Result { + let id = ExecutionProofStatusRequestId { id: self.next_id() }; + let local_status = self.local_execution_proof_status(); + self.network_send + .send(NetworkMessage::SendRequest { + peer_id, + request: RequestType::ExecutionProofStatus(local_status), + app_request_id: AppRequestId::Sync(SyncRequestId::ExecutionProofStatus(id)), + }) + .map_err(|_| RpcRequestSendError::InternalError("network send error".to_owned()))?; + debug!( + method = "ExecutionProofStatus", + peer = %peer_id, + %id, + "Sync RPC request sent" + ); + Ok(id) } pub fn network_globals(&self) -> &NetworkGlobals { &self.network_beacon_processor.network_globals } + pub fn local_execution_proof_status(&self) -> ExecutionProofStatus { + self.network_globals().local_execution_proof_status() + } + + /// Returns `true` if the peer has `execution_proof_enabled()` in their ENR. + pub fn is_proof_capable_peer(&self, peer_id: &PeerId) -> bool { + self.network_globals() + .peers + .read() + .peer_info(peer_id) + .is_some_and(|info| info.on_subnet_metadata(&Subnet::ExecutionProof)) + } + /// Returns the Client type of the peer if known pub fn client_type(&self, peer_id: &PeerId) -> Client { self.network_globals() @@ -555,9 +560,6 @@ impl SyncNetworkContext { // components_by_range_requests is a meta request of various _by_range requests components_by_range_requests: _, custody_backfill_data_column_batch_requests: _, - // execution proof requests are soft, fire-and-forget; not counted for load balancing - execution_proofs_by_range_requests: _, - execution_proofs_by_root_requests: _, execution_engine_state: _, network_beacon_processor: _, chain: _, diff --git a/beacon_node/network/src/sync/proof_sync.rs b/beacon_node/network/src/sync/proof_sync.rs index 8f9701aa578..59021686215 100644 --- a/beacon_node/network/src/sync/proof_sync.rs +++ b/beacon_node/network/src/sync/proof_sync.rs @@ -1,21 +1,38 @@ //! ProofSync: catch-up mechanism for EIP-8025 execution proofs. //! -//! After range sync completes, `ProofSync` issues an `ExecutionProofsByRange` request to -//! bootstrap proofs for the newly-synced window (bootstrap mode), then switches to -//! `FillingByRoot` mode where it issues targeted `ExecutionProofsByRoot` requests for any -//! individual blocks that are still missing proofs. +//! Operates in two states: `Idle` (range sync active, no proof work) and `Syncing` +//! (proof catchup active). In `Syncing`, each poll computes the slot gap between the +//! finalized epoch and the current head and chooses the most efficient strategy: +//! a bulk `ExecutionProofsByRange` request for large gaps, or targeted +//! `ExecutionProofsByRoot` requests when the gap is small. -use super::network_context::{RpcRequestSendError, SyncNetworkContext}; -use beacon_chain::{BeaconChain, BeaconChainTypes}; +use super::network_context::{CachedExecutionProofStatus, SyncNetworkContext}; +use beacon_chain::{BeaconChain, BeaconChainTypes, WhenSlotSkipped}; use execution_layer::MissingProofInfo; use fnv::FnvHashMap; +use lighthouse_network::PeerId; +use lighthouse_network::rpc::methods::ExecutionProofStatus; use lighthouse_network::service::api_types::{ - ExecutionProofsByRangeRequestId, ExecutionProofsByRootRequestId, + ExecutionProofStatusRequestId, ExecutionProofsByRangeRequestId, ExecutionProofsByRootRequestId, }; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; +use std::time::Instant; use tracing::debug; -use types::{EthSpec, Hash256}; +use types::{EthSpec, Hash256, Slot}; + +/// Default slot gap above which a bulk `ExecutionProofsByRange` request is preferred over +/// individual `ExecutionProofsByRoot` requests. +const DEFAULT_RANGE_REQUEST_THRESHOLD: u64 = 16; + +/// Tracks the single in-flight `ExecutionProofsByRange` request. +/// +/// The request ID and serving peer are always set and cleared together, so they are +/// co-located. +pub(crate) struct RangeRequest { + pub(crate) id: ExecutionProofsByRangeRequestId, + pub(crate) peer_id: PeerId, +} /// Maximum number of concurrent `ExecutionProofsByRoot` requests. const DEFAULT_MAX_CONCURRENT: usize = 4; @@ -23,42 +40,38 @@ const DEFAULT_MAX_CONCURRENT: usize = 4; /// Operating mode for the proof sync subsystem. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ProofSyncState { - /// Not running - range sync is active. + /// Range sync is active; proof sync is paused. Idle, - /// Range sync is completed. Next poll will issue an `ExecutionProofsByRange` request. - PendingRangeRequest, - /// An `ExecutionProofsByRange` request is in-flight. Waiting for the stream to drain. - RangeRequestInFlight, - /// Bootstrap complete. Requesting any remaining missing proofs by root on each poll. - /// Terminal active state until range sync restarts, which resets to `Idle`. - FillingByRoot, + /// Proof sync is active. Each poll chooses between a range request (large slot gap) + /// or by-root fill requests (small gap) based on current chain state. + Syncing, } /// Proof sync subsystem for EIP-8025. /// -/// Operates as a state machine with four modes: -/// - `Idle`: no work to do (range sync active or not yet triggered). -/// - `PendingRangeRequest`: range sync is completed; next poll sends the bootstrap range request. -/// - `RangeRequestInFlight`: waiting for the bootstrap range stream to drain. -/// - `FillingByRoot`: terminal active state; issues per-block by-root requests each poll. -/// -/// Re-entering range sync resets state to `Idle` (via ProofSync::pause()), which cancels any in-flight requests and clears state. Proof sync will -/// automatically restart when range sync completes (via ProofSync::start()), which transitions to `PendingRangeRequest`. +/// Operates as a two-state machine: `Idle` while range sync is active, `Syncing` +/// otherwise. In `Syncing`, each poll computes the slot gap between the max(finalized +/// epoch, local verified head) - peer verified head to determine the most efficient request strategy. +/// In-flight by-root and range responses are always processed regardless of state +/// transitions — the proofs are valid independent of sync progress. pub struct ProofSync { /// The beacon chain. chain: Arc>, /// The current state of the proof sync subsystem. state: ProofSyncState, - /// Tracks the in-flight range request ID while in `RangeRequestInFlight` state. - /// `None` in all other states. - range_request_id: Option, - /// In-flight by-root request IDs → `MissingProofInfo` (fill mode). - /// Keeping the full info preserves `existing_proof_types` for awareness of what - /// proof types the remote peer should supply. + /// Tracks the single in-flight `ExecutionProofsByRange` request (ID + serving peer). + range_request: Option, + /// In-flight by-root request IDs → `MissingProofInfo`. in_flight: FnvHashMap, - /// Maximum number of concurrent by-root requests in `FillingByRoot` state. + /// Slot gap above which a `ByRange` request is preferred over `ByRoot` fill requests. + range_request_threshold: u64, + /// Maximum number of concurrent by-root requests. max_concurrent: usize, - /// Injected missing-proof list for unit testing fill-mode behaviour. + /// Cached `ExecutionProofStatus` responses, keyed by peer. + peer_statuses: HashMap, + /// In-flight `ExecutionProofStatus` request IDs, keyed by peer. + status_in_flight: HashMap, + /// Injected missing-proof list for unit testing by-root behaviour. #[cfg(test)] pub test_missing_proofs: Option>, } @@ -67,10 +80,13 @@ impl ProofSync { pub fn new(chain: Arc>) -> Self { Self { state: ProofSyncState::Idle, - range_request_id: None, + range_request: None, chain, in_flight: FnvHashMap::default(), + range_request_threshold: DEFAULT_RANGE_REQUEST_THRESHOLD, max_concurrent: DEFAULT_MAX_CONCURRENT, + peer_statuses: HashMap::default(), + status_in_flight: HashMap::default(), #[cfg(test)] test_missing_proofs: None, } @@ -83,148 +99,297 @@ impl ProofSync { } #[cfg(test)] - pub fn in_flight_count(&self) -> usize { - self.in_flight.len() + pub fn in_flight(&self) -> &FnvHashMap { + &self.in_flight + } + + #[cfg(test)] + pub fn set_state(&mut self, state: ProofSyncState) { + self.state = state; + } + + #[cfg(test)] + pub fn set_range_request_threshold(&mut self, threshold: u64) { + self.range_request_threshold = threshold; } - /// Force-enter `FillingByRoot` state for tests that need to exercise fill-mode - /// behaviour without going through the bootstrap range cycle. #[cfg(test)] - pub fn enter_fill_mode_for_testing(&mut self) { - self.state = ProofSyncState::FillingByRoot; + pub fn range_request(&self) -> Option<&RangeRequest> { + self.range_request.as_ref() } - /// Called by `SyncManager::update_sync_state()` when range sync completes. + #[cfg(test)] + pub fn peer_status(&self, peer_id: &PeerId) -> Option<&CachedExecutionProofStatus> { + self.peer_statuses.get(peer_id) + } + + /// Called by `SyncManager` when range sync completes. /// - /// Transitions from `Idle` to `PendingRangeRequest`. The next `poll()` will send - /// the bootstrap `ExecutionProofsByRange` request. - pub fn start(&mut self) { - debug!("ProofSync: range sync complete, scheduling proof range request"); - self.state = ProofSyncState::PendingRangeRequest; + /// Kicks off peer status refreshes and transitions to `Syncing`. + pub fn start(&mut self, cx: &mut SyncNetworkContext) { + debug!("ProofSync: starting"); + self.refresh_peer_statuses(cx); + self.state = ProofSyncState::Syncing; } - /// Called by `SyncManager::update_sync_state()` when entering range sync. + /// Called by `SyncManager` when range sync re-enters. /// - /// Stops any in-progress proof sync activity and resets to `Idle`. - /// Proof sync will automatically restart when range sync completes. + /// Stops new proof requests from being issued. Any already in-flight responses + /// are still processed as they arrive. pub fn pause(&mut self) { - debug!("ProofSync: pausing and resetting to Idle"); + debug!("ProofSync: pausing"); self.state = ProofSyncState::Idle; - self.range_request_id = None; - self.in_flight.clear(); } /// Drive one polling cycle. /// - /// Resets to `Idle` if the node has re-entered range sync. Otherwise dispatches - /// work according to the current state. + /// In `Syncing`, computes the slot gap and dispatches either a range request + /// (gap > `RANGE_REQUEST_THRESHOLD`) or by-root fill requests (gap ≤ threshold). + /// Waits if a range request is already in-flight or peer status polls are pending. pub fn poll(&mut self, cx: &mut SyncNetworkContext) { - match &self.state { - ProofSyncState::Idle | ProofSyncState::RangeRequestInFlight => {} - ProofSyncState::PendingRangeRequest => { - self.request_proof_range(cx); + if matches!(self.state, ProofSyncState::Idle) { + return; + } + + // If a range request is already in-flight, wait for it to drain. + if self.range_request.is_some() { + return; + } + + // Compute the start slot: the higher of the finalized slot and our own verified proof slot, + // so we don't re-request proofs we've already processed. + let finalized_slot = self + .chain + .canonical_head + .cached_head() + .finalized_checkpoint() + .epoch + .start_slot(T::EthSpec::slots_per_epoch()); + let local_proof_slot = Slot::new(cx.local_execution_proof_status().slot); + let start_slot = finalized_slot.max(local_proof_slot) + 1; + + let Some((peer_id, peer_slot)) = self.best_peer(cx) else { + return; + }; + + let gap = peer_slot + .as_u64() + .checked_add(1) + .and_then(|end| end.checked_sub(start_slot.as_u64())) + .unwrap_or(0); + + if gap > self.range_request_threshold { + match cx.request_execution_proofs_by_range(peer_id, start_slot, gap) { + Ok(id) => { + debug!(%start_slot, %peer_slot, gap, "ProofSync: range request sent"); + self.range_request = Some(RangeRequest { id, peer_id }); + } + Err(e) => { + debug!(error = ?e, "ProofSync: range request error"); + } } - ProofSyncState::FillingByRoot => { - // Terminal active state: remain here until range sync restarts. - // On each poll, issue by-root requests for any missing proofs up to - // the concurrency limit. - #[cfg(not(test))] - let missing = self.chain.missing_execution_proofs(); - #[cfg(test)] - let missing = self - .test_missing_proofs - .clone() - .unwrap_or_else(|| self.chain.missing_execution_proofs()); - let in_flight_roots: HashSet = - self.in_flight.values().map(|i| i.root).collect(); - let available = self.max_concurrent.saturating_sub(self.in_flight.len()); - - for info in missing - .into_iter() - .filter(|info| !in_flight_roots.contains(&info.root)) - .take(available) - { - match cx.request_execution_proofs_by_root(info.root) { - Ok(id) => { - debug!( - block_root = %info.root, - existing_proof_types = ?info.existing_proof_types, - "ProofSync: requesting missing proof" - ); - self.in_flight.insert(id, info); - } - Err(RpcRequestSendError::NoPeer(_)) => { - debug!("ProofSync: no proof-capable peer, will retry next poll"); - break; - } - Err(e) => { - debug!(error = ?e, "ProofSync: failed to send proof request"); - } - } + return; + } + + #[cfg(not(test))] + let missing = self.chain.missing_execution_proofs(); + #[cfg(test)] + let missing = self + .test_missing_proofs + .clone() + .unwrap_or_else(|| self.chain.missing_execution_proofs()); + let in_flight_roots: HashSet = self.in_flight.values().map(|i| i.root).collect(); + let available = self.max_concurrent.saturating_sub(self.in_flight.len()); + for info in missing + .into_iter() + .filter(|info| !in_flight_roots.contains(&info.root)) + .take(available) + { + match cx.request_execution_proofs_by_root(peer_id, info.root) { + Ok(id) => { + debug!( + block_root = %info.root, + existing_proof_types = ?info.existing_proof_types, + "ProofSync: requesting missing proof" + ); + self.in_flight.insert(id, info); + } + Err(e) => { + debug!(error = ?e, "ProofSync: failed to send proof request"); } } } } /// Called when an `ExecutionProofsByRange` RPC stream terminates (response `None`). - /// - /// Transitions from `RangeRequestInFlight` to `FillingByRoot`. pub fn on_range_request_terminated(&mut self, id: &ExecutionProofsByRangeRequestId) { - if matches!(&self.state, ProofSyncState::RangeRequestInFlight) - && self.range_request_id.as_ref() == Some(id) - { - debug!("ProofSync: bootstrap range stream complete, switching to fill mode"); - self.range_request_id = None; - self.state = ProofSyncState::FillingByRoot; + if self.range_request.as_ref().map(|r| &r.id) == Some(id) { + debug!("ProofSync: range stream complete"); + self.range_request = None; + } + } + + /// Called when an `ExecutionProofsByRange` RPC request errors. + pub fn on_range_request_error(&mut self, id: &ExecutionProofsByRangeRequestId) { + if self.range_request.as_ref().map(|r| &r.id) == Some(id) { + debug!("ProofSync: range request failed, will retry next poll"); + self.range_request = None; } } + /// Called when an `ExecutionProofsByRoot` RPC request errors. + pub fn on_root_request_error(&mut self, id: &ExecutionProofsByRootRequestId) { + self.in_flight.remove(id); + } + /// Called when an `ExecutionProofsByRoot` RPC stream terminates (response `None`). pub fn on_request_terminated(&mut self, id: &ExecutionProofsByRootRequestId) { self.in_flight.remove(id); } - /// Issue an `ExecutionProofsByRange` bootstrap request covering finalized+1 through head. + /// Called when a proof-capable peer connects. /// - /// Transitions to `RangeRequestInFlight` on success, stays `PendingRangeRequest` if no - /// proof-capable peer is available. - fn request_proof_range(&mut self, cx: &mut SyncNetworkContext) { - let finalized_slot = self - .chain - .canonical_head - .cached_head() - .finalized_checkpoint() - .epoch - .start_slot(T::EthSpec::slots_per_epoch()); - let start_slot = finalized_slot + 1; - // Use the current slot clock rather than the head block slot so that the range - // covers any slots after the head block that the EL may have processed. - let current_slot = self.chain.slot().unwrap_or_else(|_| self.chain.best_slot()); - if current_slot < start_slot { - debug!("ProofSync: current slot is behind start_slot, switching directly to fill mode"); - self.state = ProofSyncState::FillingByRoot; - return; - } - let count = current_slot.as_u64() - start_slot.as_u64() + 1; - - match cx.request_execution_proofs_by_range(start_slot, count) { + /// Always issues a fresh `ExecutionProofStatus` request, overwriting any stale + /// in-flight entry from a prior connection. + pub fn add_peer(&mut self, peer_id: PeerId, cx: &mut SyncNetworkContext) { + match cx.request_execution_proof_status(peer_id) { Ok(id) => { - debug!( - %start_slot, - %current_slot, - count, - "ProofSync: bootstrap range request sent" - ); - self.range_request_id = Some(id); - self.state = ProofSyncState::RangeRequestInFlight; - } - Err(RpcRequestSendError::NoPeer(_)) => { - debug!("ProofSync: no proof-capable peer for range request, will retry next poll"); - // State stays PendingRangeRequest. + debug!(%peer_id, %id, "ProofSync: queried peer execution proof status"); + self.status_in_flight.insert(peer_id, id); } Err(e) => { - debug!(error = ?e, "ProofSync: range request error"); + debug!(error = ?e, %peer_id, "ProofSync: failed to query peer status on connect"); } } } + + /// Called when a proof-capable peer disconnects. + pub fn on_proof_capable_peer_disconnected(&mut self, peer_id: &PeerId) { + self.peer_statuses.remove(peer_id); + self.status_in_flight.remove(peer_id); + // If this peer was serving our range request, clear it so the next poll retries. + if self + .range_request + .as_ref() + .map(|r| &r.peer_id) + .filter(|p| *p == peer_id) + .is_some() + { + self.range_request = None; + } + } + + /// Called when an `ExecutionProofStatus` arrives from a peer. + /// + /// `request_id` is `Some` for outbound (we initiated) responses and `None` for inbound + /// (peer-initiated) requests. + pub fn on_peer_execution_proof_status( + &mut self, + peer_id: PeerId, + _request_id: Option, + status: ExecutionProofStatus, + ) { + debug!( + %peer_id, + slot = status.slot, + block_root = %status.block_root, + "ProofSync: received ExecutionProofStatus" + ); + + let best_slot = self.chain.best_slot(); + let verified = if status.slot <= best_slot.as_u64() { + match self + .chain + .block_root_at_slot(Slot::new(status.slot), WhenSlotSkipped::None) + { + Ok(Some(root)) if root == status.block_root => true, + _ => { + debug!( + %peer_id, + slot = status.slot, + "ProofSync: peer block root mismatch, ignoring status" + ); + self.on_peer_status_failed(peer_id); + return; + } + } + } else { + false + }; + + self.status_in_flight.remove(&peer_id); + self.peer_statuses.insert( + peer_id, + CachedExecutionProofStatus { + status, + timestamp: Instant::now(), + verified, + }, + ); + } + + /// Called when an `ExecutionProofStatus` request errors. + pub fn on_peer_execution_proof_status_error( + &mut self, + peer_id: PeerId, + request_id: ExecutionProofStatusRequestId, + ) { + debug!(%peer_id, %request_id, "ProofSync: ExecutionProofStatus request failed (soft)"); + self.on_peer_status_failed(peer_id); + } + + /// Clears the in-flight status entry and resets the peer's timestamp to defer re-polling. + /// Inserts a zero-slot placeholder if no prior entry exists. + fn on_peer_status_failed(&mut self, peer_id: PeerId) { + self.status_in_flight.remove(&peer_id); + self.peer_statuses + .entry(peer_id) + .and_modify(|entry| entry.timestamp = Instant::now()) + .or_insert_with(|| CachedExecutionProofStatus { + status: ExecutionProofStatus { + slot: 0, + block_root: Hash256::ZERO, + }, + timestamp: Instant::now(), + verified: false, + }); + } + + /// Triggers refresh requests for stale or unverified peer entries. + fn refresh_peer_statuses(&mut self, cx: &mut SyncNetworkContext) { + for (peer_id, status) in self.peer_statuses.iter() { + if status.needs_refresh() && !self.status_in_flight.contains_key(peer_id) { + match cx.request_execution_proof_status(*peer_id) { + Ok(id) => { + self.status_in_flight.insert(*peer_id, id); + } + Err(e) => { + debug!(error = ?e, %peer_id, "ProofSync: failed to refresh status"); + } + } + } + } + } + + /// Triggers refresh requests for stale peer entries, then returns the peer with the + /// highest announced slot if all outstanding status polls have resolved. + /// + /// Verified peers are preferred (their slot is confirmed on-chain), but unverified + /// peers (whose announced slot is ahead of our head) are also eligible — the proofs + /// they serve are validated independently on receipt. + fn best_peer(&mut self, cx: &mut SyncNetworkContext) -> Option<(PeerId, Slot)> { + self.refresh_peer_statuses(cx); + + let result = self + .peer_statuses + .iter() + .max_by_key(|(_, c)| (c.verified, c.status.slot)) + .map(|(peer_id, c)| (*peer_id, Slot::new(c.status.slot))); + + if result.is_none() { + debug!("ProofSync: no proof-capable peer, will retry next poll"); + } + + result + } } diff --git a/beacon_node/network/src/sync/range_data_column_batch_request.rs b/beacon_node/network/src/sync/range_data_column_batch_request.rs index b912a6badc9..52d7b63a031 100644 --- a/beacon_node/network/src/sync/range_data_column_batch_request.rs +++ b/beacon_node/network/src/sync/range_data_column_batch_request.rs @@ -71,9 +71,7 @@ impl RangeDataColumnBatchRequest { let mut column_to_peer_id: HashMap = HashMap::new(); for req in self.requests.values() { - let Some(columns) = req.to_finished() else { - return None; - }; + let columns = req.to_finished()?; for column in columns { received_columns_for_slot diff --git a/beacon_node/network/src/sync/tests/lookups.rs b/beacon_node/network/src/sync/tests/lookups.rs index d071491da00..fa81bef34d2 100644 --- a/beacon_node/network/src/sync/tests/lookups.rs +++ b/beacon_node/network/src/sync/tests/lookups.rs @@ -109,6 +109,18 @@ impl TestRig { init_tracing(); + let network_globals = beacon_processor.network_globals.clone(); + let mut sync_manager = SyncManager::new( + chain, + network_tx, + beacon_processor.into(), + // Pass empty recv not tied to any tx + mpsc::unbounded_channel().1, + fork_context, + ); + // In tests any non-zero gap triggers a range request, keeping slot advancement minimal. + sync_manager.proof_sync_mut().set_range_request_threshold(0); + TestRig { beacon_processor_rx, beacon_processor_rx_queue: vec![], @@ -117,15 +129,8 @@ impl TestRig { sync_rx, rng_08, rng, - network_globals: beacon_processor.network_globals.clone(), - sync_manager: SyncManager::new( - chain, - network_tx, - beacon_processor.into(), - // Pass empty recv not tied to any tx - mpsc::unbounded_channel().1, - fork_context, - ), + network_globals, + sync_manager, harness, fork_name, spec, @@ -1979,9 +1984,7 @@ mod deneb_only { impl DenebTester { fn new(request_trigger: RequestTrigger) -> Option { - let Some(mut rig) = TestRig::test_setup_after_deneb_before_fulu() else { - return None; - }; + let mut rig = TestRig::test_setup_after_deneb_before_fulu()?; let (block, blobs) = rig.rand_block_and_blobs(NumBlobs::Random); let mut block = Arc::new(block); let mut blobs = blobs.into_iter().map(Arc::new).collect::>(); diff --git a/beacon_node/network/src/sync/tests/range.rs b/beacon_node/network/src/sync/tests/range.rs index 1608532415e..cbbbe894e0a 100644 --- a/beacon_node/network/src/sync/tests/range.rs +++ b/beacon_node/network/src/sync/tests/range.rs @@ -14,12 +14,13 @@ use bls::SignatureBytes; use execution_layer::MissingProofInfo; use lighthouse_network::rpc::RequestType; use lighthouse_network::rpc::methods::{ - BlobsByRangeRequest, DataColumnsByRangeRequest, OldBlocksByRangeRequest, + BlobsByRangeRequest, DataColumnsByRangeRequest, ExecutionProofStatus, OldBlocksByRangeRequest, OldBlocksByRangeRequestV2, StatusMessageV2, }; use lighthouse_network::service::api_types::{ AppRequestId, BlobsByRangeRequestId, BlocksByRangeRequestId, DataColumnsByRangeRequestId, - ExecutionProofsByRangeRequestId, ExecutionProofsByRootRequestId, SyncRequestId, + ExecutionProofStatusRequestId, ExecutionProofsByRangeRequestId, ExecutionProofsByRootRequestId, + SyncRequestId, }; use lighthouse_network::{PeerId, SyncInfo}; use std::sync::Arc; @@ -328,16 +329,23 @@ impl TestRig { } /// Assert an `ExecutionProofsByRange` RPC was sent to the network. - /// Returns `(request_id, peer_id)`. + /// Returns `(request_id, peer_id, start_slot)`. fn find_execution_proofs_by_range_request( &mut self, ) -> (ExecutionProofsByRangeRequestId, PeerId) { + self.find_execution_proofs_by_range_request_with_slot().0 + } + + /// Assert an `ExecutionProofsByRange` RPC was sent; returns `((id, peer_id), start_slot)`. + fn find_execution_proofs_by_range_request_with_slot( + &mut self, + ) -> ((ExecutionProofsByRangeRequestId, PeerId), Slot) { self.pop_received_network_event(|ev| match ev { NetworkMessage::SendRequest { peer_id, - request: RequestType::ExecutionProofsByRange(_), + request: RequestType::ExecutionProofsByRange(req), app_request_id: AppRequestId::Sync(SyncRequestId::ExecutionProofsByRange(id)), - } => Some((*id, *peer_id)), + } => Some(((*id, *peer_id), Slot::new(req.start_slot))), _ => None, }) .unwrap_or_else(|e| panic!("Expected ExecutionProofsByRange request: {e:?}")) @@ -403,6 +411,65 @@ impl TestRig { }); } + /// Find an outbound `ExecutionProofStatus` RPC request; returns `(request_id, peer_id)`. + fn find_execution_proof_status_request(&mut self) -> (ExecutionProofStatusRequestId, PeerId) { + self.pop_received_network_event(|ev| match ev { + NetworkMessage::SendRequest { + peer_id, + request: RequestType::ExecutionProofStatus(_), + app_request_id: AppRequestId::Sync(SyncRequestId::ExecutionProofStatus(id)), + } => Some((*id, *peer_id)), + _ => None, + }) + .unwrap_or_else(|e| panic!("Expected ExecutionProofStatus request: {e:?}")) + } + + /// Drain all pending `ExecutionProofStatus` outbound requests from the network queue. + /// + /// Called after `start_proof_sync()` to prevent status requests from leaking into + /// assertions in the caller. + fn drain_execution_proof_status_requests(&mut self) { + self.drain_network_rx(); + self.network_rx_queue.retain(|ev| { + !matches!( + ev, + NetworkMessage::SendRequest { + request: RequestType::ExecutionProofStatus(_), + .. + } + ) + }); + } + + /// Connect a proof-capable peer and deliver an `ExecutionProofStatus` for `peer_slot`. + /// + /// For slots within our head the block root is looked up so the entry is verified. + /// For slots ahead of our head the entry is cached as unverified (optimistic) but + /// still eligible for peer selection. + fn new_proof_peer_with_status(&mut self, peer_slot: u64) -> PeerId { + let peer_id = self.new_connected_proof_capable_peer(); + let best_slot = self.harness.chain.best_slot(); + let block_root = if Slot::new(peer_slot) <= best_slot { + self.harness + .chain + .block_root_at_slot(Slot::new(peer_slot), beacon_chain::WhenSlotSkipped::None) + .ok() + .flatten() + .unwrap_or_else(Hash256::random) + } else { + Hash256::random() + }; + self.send_sync_message(SyncMessage::RpcExecutionProofStatus { + peer_id, + request_id: None, + status: ExecutionProofStatus { + slot: peer_slot, + block_root, + }, + }); + peer_id + } + fn find_and_complete_processing_chain_segment(&mut self, id: ChainSegmentProcessId) { self.pop_received_processor_event(|ev| { (ev.work_type() == WorkType::ChainSegment).then_some(()) @@ -685,31 +752,11 @@ fn finalized_sync_not_enough_custody_peers_on_start() { // --- ProofSync state-machine tests --- // These tests exercise the `ProofSync` state machine directly, covering its full lifecycle: -// Bootstrap (Idle → PendingRangeRequest → RangeRequestInFlight → FillingByRoot), +// Idle → Syncing (range request for large gaps, by-root fill for small gaps), // pause/resume semantics, concurrency cap, in-flight deduplication, and response forwarding. - -/// Drive ProofSync through the full bootstrap cycle: -/// start() → PendingRangeRequest → poll() → RangeRequestInFlight → terminate → FillingByRoot. -/// Returns the `(req_id, peer_id)` of the range request that was terminated. -/// -/// Advances the harness slot clock by 1 so that `chain.slot()` > `finalized_start_slot`, -/// which is required for the range request to be sent (otherwise the genesis check triggers). -fn bootstrap_proof_sync_to_fill_mode( - rig: &mut TestRig, -) -> (ExecutionProofsByRangeRequestId, PeerId) { - // Advance the slot clock so current_slot >= start_slot (genesis check does not fire). - rig.harness.advance_slot(); - rig.sync_manager.start_proof_sync(); - rig.sync_manager.poll_proof_sync(); - let (req_id, peer_id) = rig.find_execution_proofs_by_range_request(); - rig.terminate_execution_proofs_by_range(req_id, peer_id); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::FillingByRoot, - "Expected FillingByRoot after range stream termination" - ); - (req_id, peer_id) -} +// +// In tests, range_request_threshold = 0, so any non-zero slot gap triggers a range request. +// At genesis (slot 0, gap = 0) the poll goes directly to by-root fill requests. /// Build a `MissingProofInfo` with a fresh random root for test seeding. fn missing_proof(root: Hash256) -> MissingProofInfo { @@ -732,185 +779,174 @@ fn make_signed_proof() -> Arc { #[test] fn test_proof_sync_starts_in_idle() { let mut rig = TestRig::test_setup(); - assert_eq!(rig.sync_manager.proof_sync_state(), ProofSyncState::Idle); + assert_eq!(rig.sync_manager.proof_sync().state(), ProofSyncState::Idle); rig.sync_manager.poll_proof_sync(); rig.expect_empty_network(); } -/// Test 2: After `start()`, the next `poll()` sends an `ExecutionProofsByRange` RPC and -/// transitions to `RangeRequestInFlight`. +/// Test 2: After `start()`, the next `poll()` sends an `ExecutionProofsByRange` RPC. +/// (slot gap = 1 > range_request_threshold = 0 in tests → range request). #[test] fn test_proof_sync_pending_range_issues_request_on_poll() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); - rig.harness.advance_slot(); // current_slot must be >= start_slot for range request + let _proof_peer = rig.new_proof_peer_with_status(1); + rig.harness.advance_slot(); rig.sync_manager.start_proof_sync(); assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::PendingRangeRequest, - "Expected PendingRangeRequest after start()" + rig.sync_manager.proof_sync().state(), + ProofSyncState::Syncing ); rig.sync_manager.poll_proof_sync(); let _ = rig.find_execution_proofs_by_range_request(); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::RangeRequestInFlight, - "Expected RangeRequestInFlight after polling" + assert!( + rig.sync_manager.proof_sync().range_request().is_some(), + "Range request should be in-flight after poll" ); } -/// Test 3: With no proof-capable peer, `poll()` in PendingRangeRequest stays in that state -/// and emits no request (soft failure). Adding a peer and polling again sends the request. +/// Test 3: With no proof-capable peer, `poll()` emits no request (soft failure). +/// Adding a peer and polling again sends the range request. #[test] fn test_proof_sync_no_peer_stays_pending() { let mut rig = TestRig::test_setup(); - rig.harness.advance_slot(); // current_slot must be >= start_slot for range request + rig.harness.advance_slot(); rig.sync_manager.start_proof_sync(); - // No proof-capable peer yet — poll should be a no-op. rig.sync_manager.poll_proof_sync(); rig.expect_no_execution_proof_range_request(); assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::PendingRangeRequest, - "Should stay PendingRangeRequest when no proof-capable peer" + rig.sync_manager.proof_sync().state(), + ProofSyncState::Syncing ); + assert!(rig.sync_manager.proof_sync().range_request().is_none()); - // Now add a proof-capable peer; the next poll should send the request. - let _proof_peer = rig.new_connected_proof_capable_peer(); + // Adding a proof-capable peer; the next poll sends the request. + let _proof_peer = rig.new_proof_peer_with_status(1); rig.sync_manager.poll_proof_sync(); let _ = rig.find_execution_proofs_by_range_request(); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::RangeRequestInFlight - ); + assert!(rig.sync_manager.proof_sync().range_request().is_some()); } -/// Test 4: In `RangeRequestInFlight`, `poll()` must not send any requests. +/// Test 4: While a range request is in-flight, `poll()` must not send any new requests. #[test] fn test_proof_sync_in_flight_poll_is_noop() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(1); rig.harness.advance_slot(); rig.sync_manager.start_proof_sync(); rig.sync_manager.poll_proof_sync(); let _ = rig.find_execution_proofs_by_range_request(); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::RangeRequestInFlight - ); + rig.drain_execution_proof_status_requests(); + assert!(rig.sync_manager.proof_sync().range_request().is_some()); - // A second poll while in-flight should produce nothing. + // A second poll while a range request is in-flight should produce nothing. rig.sync_manager.poll_proof_sync(); rig.expect_empty_network(); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::RangeRequestInFlight - ); + assert!(rig.sync_manager.proof_sync().range_request().is_some()); } -/// Test 5: Stream termination with the correct ID transitions from `RangeRequestInFlight` -/// to `FillingByRoot`. +/// Test 5: Stream termination with the correct ID clears the in-flight range request. +/// The next poll will then issue by-root fill requests (gap is now 0 at genesis). #[test] fn test_proof_sync_range_termination_enters_fill_mode() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(1); rig.harness.advance_slot(); rig.sync_manager.start_proof_sync(); rig.sync_manager.poll_proof_sync(); let (req_id, peer_id) = rig.find_execution_proofs_by_range_request(); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::RangeRequestInFlight - ); + assert!(rig.sync_manager.proof_sync().range_request().is_some()); rig.terminate_execution_proofs_by_range(req_id, peer_id); assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::FillingByRoot, - "Should enter FillingByRoot after correct range termination" + rig.sync_manager.proof_sync().state(), + ProofSyncState::Syncing + ); + assert!( + rig.sync_manager.proof_sync().range_request().is_none(), + "Range request should be cleared after stream termination" ); } -/// Test 6: Stream termination with a wrong ID is ignored; state stays `RangeRequestInFlight`. +/// Test 6: Stream termination with a wrong ID is ignored; the range request stays in-flight. #[test] fn test_proof_sync_wrong_id_termination_ignored() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(1); rig.harness.advance_slot(); rig.sync_manager.start_proof_sync(); rig.sync_manager.poll_proof_sync(); let (_req_id, peer_id) = rig.find_execution_proofs_by_range_request(); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::RangeRequestInFlight - ); + assert!(rig.sync_manager.proof_sync().range_request().is_some()); - // Terminate with a different (fake) ID. + // Terminate with a different (fake) ID — should be ignored. let fake_id = ExecutionProofsByRangeRequestId { id: 9999 }; rig.terminate_execution_proofs_by_range(fake_id, peer_id); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::RangeRequestInFlight, - "Wrong ID should not trigger transition" + assert!( + rig.sync_manager.proof_sync().range_request().is_some(), + "Wrong ID should not clear the in-flight range request" ); } -/// Test 7: In `FillingByRoot` with no missing proofs, `poll()` is a no-op. +/// Test 7: With no missing proofs, `poll()` in `Syncing` is a no-op. #[test] fn test_proof_sync_fill_mode_no_missing_proofs() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(0); - // Bootstrap to FillingByRoot; test_missing_proofs stays None → returns empty. - let _ = bootstrap_proof_sync_to_fill_mode(&mut rig); + // At genesis (gap = 0) start() transitions directly to by-root fill behaviour. + rig.sync_manager.start_proof_sync(); + rig.drain_execution_proof_status_requests(); + // test_missing_proofs is None → chain returns empty → no requests sent. rig.sync_manager.poll_proof_sync(); rig.expect_empty_network(); assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::FillingByRoot + rig.sync_manager.proof_sync().state(), + ProofSyncState::Syncing ); } -/// Test 8: In `FillingByRoot` with seeded missing proofs, `poll()` sends one -/// `ExecutionProofsByRoot` request per missing proof. +/// Test 8: With seeded missing proofs, `poll()` sends one `ExecutionProofsByRoot` +/// request per missing proof. #[test] fn test_proof_sync_fill_mode_issues_by_root_requests() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(0); - let _ = bootstrap_proof_sync_to_fill_mode(&mut rig); + rig.sync_manager.start_proof_sync(); + rig.drain_execution_proof_status_requests(); let missing = vec![ missing_proof(Hash256::random()), missing_proof(Hash256::random()), ]; - rig.sync_manager.set_proof_sync_missing(missing); + rig.sync_manager.proof_sync_mut().test_missing_proofs = Some(missing); rig.sync_manager.poll_proof_sync(); let _ = rig.find_execution_proofs_by_root_request(); let _ = rig.find_execution_proofs_by_root_request(); - assert_eq!(rig.sync_manager.proof_sync_in_flight_count(), 2); + assert_eq!(rig.sync_manager.proof_sync().in_flight().len(), 2); } -/// Test 9: `poll()` in `FillingByRoot` must not exceed `DEFAULT_MAX_CONCURRENT = 4` -/// in-flight requests even when more missing proofs are present. +/// Test 9: `poll()` must not exceed `DEFAULT_MAX_CONCURRENT = 4` in-flight requests +/// even when more missing proofs are present. #[test] fn test_proof_sync_fill_mode_respects_max_concurrent() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(0); - let _ = bootstrap_proof_sync_to_fill_mode(&mut rig); + rig.sync_manager.start_proof_sync(); + rig.drain_execution_proof_status_requests(); // Seed 6 distinct missing proofs; only 4 should be requested. let missing: Vec = (0..6).map(|_| missing_proof(Hash256::random())).collect(); - rig.sync_manager.set_proof_sync_missing(missing); + rig.sync_manager.proof_sync_mut().test_missing_proofs = Some(missing); rig.sync_manager.poll_proof_sync(); // Consume exactly 4 requests. @@ -918,7 +954,7 @@ fn test_proof_sync_fill_mode_respects_max_concurrent() { let _ = rig.find_execution_proofs_by_root_request(); } assert_eq!( - rig.sync_manager.proof_sync_in_flight_count(), + rig.sync_manager.proof_sync().in_flight().len(), 4, "Should have exactly 4 in-flight requests (max_concurrent)" ); @@ -930,53 +966,52 @@ fn test_proof_sync_fill_mode_respects_max_concurrent() { #[test] fn test_proof_sync_fill_mode_skips_in_flight_roots() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(0); - let _ = bootstrap_proof_sync_to_fill_mode(&mut rig); + rig.sync_manager.start_proof_sync(); + rig.drain_execution_proof_status_requests(); let missing = vec![ missing_proof(Hash256::random()), missing_proof(Hash256::random()), ]; - rig.sync_manager.set_proof_sync_missing(missing); + rig.sync_manager.proof_sync_mut().test_missing_proofs = Some(missing); // First poll: 2 requests sent, in_flight = 2. rig.sync_manager.poll_proof_sync(); let _ = rig.find_execution_proofs_by_root_request(); let _ = rig.find_execution_proofs_by_root_request(); - assert_eq!(rig.sync_manager.proof_sync_in_flight_count(), 2); + assert_eq!(rig.sync_manager.proof_sync().in_flight().len(), 2); // Second poll with same missing list: roots already in-flight, no new requests. rig.sync_manager.poll_proof_sync(); rig.expect_empty_network(); assert_eq!( - rig.sync_manager.proof_sync_in_flight_count(), + rig.sync_manager.proof_sync().in_flight().len(), 2, "In-flight count should be unchanged after second poll" ); } -/// Test 11: `NoPeer` from `request_execution_proofs_by_root` must stop iteration -/// and leave in_flight at 0. -/// -/// Uses `force_proof_sync_fill_mode` to skip the bootstrap cycle so there is no -/// proof-capable peer in the peerDB when the fill poll fires. +/// Test 11: With no proof-capable peer, `poll()` in `Syncing` emits no requests. #[test] fn test_proof_sync_fill_mode_no_peer_breaks() { let mut rig = TestRig::test_setup(); - // No proof-capable peer — find_any_proof_capable_peer returns None → NoPeer. - rig.sync_manager.force_proof_sync_fill_mode(); + // Force Syncing with no proof-capable peer — best_proof_peer() returns None. + rig.sync_manager + .proof_sync_mut() + .set_state(ProofSyncState::Syncing); let missing = vec![ missing_proof(Hash256::random()), missing_proof(Hash256::random()), ]; - rig.sync_manager.set_proof_sync_missing(missing); + rig.sync_manager.proof_sync_mut().test_missing_proofs = Some(missing); rig.sync_manager.poll_proof_sync(); rig.expect_empty_network(); assert_eq!( - rig.sync_manager.proof_sync_in_flight_count(), + rig.sync_manager.proof_sync().in_flight().len(), 0, "NoPeer should break iteration leaving in_flight empty" ); @@ -986,112 +1021,108 @@ fn test_proof_sync_fill_mode_no_peer_breaks() { #[test] fn test_proof_sync_on_request_terminated_clears_in_flight() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(0); - let _ = bootstrap_proof_sync_to_fill_mode(&mut rig); + rig.sync_manager.start_proof_sync(); + rig.drain_execution_proof_status_requests(); let missing = vec![missing_proof(Hash256::random())]; - rig.sync_manager.set_proof_sync_missing(missing); + rig.sync_manager.proof_sync_mut().test_missing_proofs = Some(missing); rig.sync_manager.poll_proof_sync(); let (req_id, peer_id) = rig.find_execution_proofs_by_root_request(); - assert_eq!(rig.sync_manager.proof_sync_in_flight_count(), 1); + assert_eq!(rig.sync_manager.proof_sync().in_flight().len(), 1); rig.terminate_execution_proofs_by_root(req_id, peer_id); assert_eq!( - rig.sync_manager.proof_sync_in_flight_count(), + rig.sync_manager.proof_sync().in_flight().len(), 0, "in_flight should be empty after termination" ); } -/// Test 13: `pause()` clears `in_flight`, clears `range_request_id`, and resets to `Idle`. +/// Test 13: `pause()` resets state to `Idle` but leaves in-flight by-root requests open +/// so that any responses that arrive can still be processed. #[test] fn test_proof_sync_pause_resets_to_idle() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(0); - let _ = bootstrap_proof_sync_to_fill_mode(&mut rig); + rig.sync_manager.start_proof_sync(); + rig.drain_execution_proof_status_requests(); // Seed some in-flight requests. let missing = vec![ missing_proof(Hash256::random()), missing_proof(Hash256::random()), ]; - rig.sync_manager.set_proof_sync_missing(missing); + rig.sync_manager.proof_sync_mut().test_missing_proofs = Some(missing); rig.sync_manager.poll_proof_sync(); let _ = rig.find_execution_proofs_by_root_request(); let _ = rig.find_execution_proofs_by_root_request(); - assert!(rig.sync_manager.proof_sync_in_flight_count() > 0); - - // Pause resets everything. - rig.sync_manager.pause_proof_sync(); - assert_eq!(rig.sync_manager.proof_sync_state(), ProofSyncState::Idle); - assert_eq!(rig.sync_manager.proof_sync_in_flight_count(), 0); + assert!(!rig.sync_manager.proof_sync().in_flight().is_empty()); + + // Pause resets state but preserves in-flight by-root entries. + rig.sync_manager.proof_sync_mut().pause(); + assert_eq!(rig.sync_manager.proof_sync().state(), ProofSyncState::Idle); + assert!( + !rig.sync_manager.proof_sync().in_flight().is_empty(), + "in-flight by-root requests are preserved across pause so responses can still land" + ); - // Polling in Idle emits nothing. + // Polling in Idle emits nothing new. rig.sync_manager.poll_proof_sync(); rig.expect_empty_network(); } -/// Test 14: Re-entering range sync (pause) then completing again restarts the full bootstrap. +/// Test 14: Re-entering range sync (pause) then completing again issues a fresh range request. #[test] fn test_proof_sync_re_enter_range_resets_then_restarts() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); - rig.harness.advance_slot(); // current_slot must be >= start_slot for range request + let _proof_peer = rig.new_proof_peer_with_status(1); + rig.harness.advance_slot(); - // First bootstrap cycle. + // First range request cycle. rig.sync_manager.start_proof_sync(); rig.sync_manager.poll_proof_sync(); let (req_id, peer_id) = rig.find_execution_proofs_by_range_request(); rig.terminate_execution_proofs_by_range(req_id, peer_id); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::FillingByRoot - ); + assert!(rig.sync_manager.proof_sync().range_request().is_none()); // Re-entering range sync pauses ProofSync. - rig.sync_manager.pause_proof_sync(); - assert_eq!(rig.sync_manager.proof_sync_state(), ProofSyncState::Idle); + rig.sync_manager.proof_sync_mut().pause(); + assert_eq!(rig.sync_manager.proof_sync().state(), ProofSyncState::Idle); - // Range sync completes again → start() → PendingRangeRequest. + // Range sync completes again → start() → Syncing. rig.sync_manager.start_proof_sync(); assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::PendingRangeRequest + rig.sync_manager.proof_sync().state(), + ProofSyncState::Syncing ); - // New poll sends a fresh range request. + // New poll sends a fresh range request (slot gap still > 0). rig.sync_manager.poll_proof_sync(); let _ = rig.find_execution_proofs_by_range_request(); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::RangeRequestInFlight - ); + assert!(rig.sync_manager.proof_sync().range_request().is_some()); } -/// Test 15: When `current_slot < start_slot` (slot clock behind finalized + 1), skip the -/// range request and transition directly to `FillingByRoot`. -/// -/// At genesis the slot clock is at slot 0, finalized epoch is 0, -/// so `start_slot = 1` and `current_slot (0) < start_slot (1)` → skip range request. +/// Test 15: At genesis (slot gap = 0 ≤ range_request_threshold = 0), no range request +/// is issued — `poll()` goes directly to the by-root fill path. #[test] fn test_proof_sync_count_zero_skips_to_fill() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(0); - // Chain starts at slot 0, finalized_epoch = 0 → count == 0. rig.sync_manager.start_proof_sync(); + rig.drain_execution_proof_status_requests(); rig.sync_manager.poll_proof_sync(); - // No range request should have been issued. rig.expect_no_execution_proof_range_request(); assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::FillingByRoot, - "Should skip directly to FillingByRoot when count == 0" + rig.sync_manager.proof_sync().state(), + ProofSyncState::Syncing ); + assert!(rig.sync_manager.proof_sync().range_request().is_none()); } /// Test 16: A proof arriving on an `ExecutionProofsByRange` stream must be forwarded @@ -1099,16 +1130,13 @@ fn test_proof_sync_count_zero_skips_to_fill() { #[test] fn test_proof_sync_range_response_forwarded_to_processor() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(1); rig.harness.advance_slot(); rig.sync_manager.start_proof_sync(); rig.sync_manager.poll_proof_sync(); let (req_id, peer_id) = rig.find_execution_proofs_by_range_request(); - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::RangeRequestInFlight - ); + assert!(rig.sync_manager.proof_sync().range_request().is_some()); // Send a proof (non-termination) response. rig.send_sync_message(SyncMessage::RpcExecutionProof { @@ -1122,11 +1150,8 @@ fn test_proof_sync_range_response_forwarded_to_processor() { }) .unwrap_or_else(|e| panic!("Expected GossipExecutionProof work event: {e:?}")); - // State remains RangeRequestInFlight (stream not yet terminated). - assert_eq!( - rig.sync_manager.proof_sync_state(), - ProofSyncState::RangeRequestInFlight - ); + // Range request is still in-flight (stream not yet terminated). + assert!(rig.sync_manager.proof_sync().range_request().is_some()); } /// Test 17: A proof arriving on an `ExecutionProofsByRoot` stream must be forwarded @@ -1134,12 +1159,14 @@ fn test_proof_sync_range_response_forwarded_to_processor() { #[test] fn test_proof_sync_root_response_forwarded_to_processor() { let mut rig = TestRig::test_setup(); - let _proof_peer = rig.new_connected_proof_capable_peer(); + let _proof_peer = rig.new_proof_peer_with_status(0); - let _ = bootstrap_proof_sync_to_fill_mode(&mut rig); + // At genesis (gap = 0) poll goes directly to by-root fill. + rig.sync_manager.start_proof_sync(); + rig.drain_execution_proof_status_requests(); let missing = vec![missing_proof(Hash256::random())]; - rig.sync_manager.set_proof_sync_missing(missing); + rig.sync_manager.proof_sync_mut().test_missing_proofs = Some(missing); rig.sync_manager.poll_proof_sync(); let (req_id, peer_id) = rig.find_execution_proofs_by_root_request(); @@ -1156,3 +1183,282 @@ fn test_proof_sync_root_response_forwarded_to_processor() { }) .unwrap_or_else(|e| panic!("Expected GossipExecutionProof work event: {e:?}")); } + +/// Test 18: A peer that claims an implausible block root (slot we have, wrong root) +/// must be ignored — an implausible status must not overwrite a valid cached entry. +#[test] +fn test_implausible_block_root_ignored() { + let mut rig = TestRig::test_setup(); + let proof_peer = rig.new_connected_proof_capable_peer(); + + // Send a valid inbound status at slot 0 with the correct genesis block root. + let genesis_root = rig + .harness + .chain + .canonical_head + .cached_head() + .head_block_root(); + rig.send_sync_message(SyncMessage::RpcExecutionProofStatus { + peer_id: proof_peer, + request_id: None, + status: ExecutionProofStatus { + slot: 0, + block_root: genesis_root, + }, + }); + + assert!( + rig.sync_manager + .proof_sync() + .peer_status(&proof_peer) + .is_some(), + "Peer should be cached after valid status" + ); + assert_eq!( + rig.sync_manager + .proof_sync() + .peer_status(&proof_peer) + .map(|s| s.verified), + Some(true), + "Cached entry should be verified" + ); + + // Send an inbound status with a wrong block root for a slot we have. + rig.send_sync_message(SyncMessage::RpcExecutionProofStatus { + peer_id: proof_peer, + request_id: None, + status: ExecutionProofStatus { + slot: 0, + block_root: Hash256::random(), + }, + }); + + // The implausible status must be dropped; the original valid entry should remain. + assert!( + rig.sync_manager + .proof_sync() + .peer_status(&proof_peer) + .is_some(), + "Valid cached entry must survive an implausible status update" + ); + assert_eq!( + rig.sync_manager + .proof_sync() + .peer_status(&proof_peer) + .map(|s| s.verified), + Some(true), + "Cached entry should still be verified after implausible status" + ); +} + +/// Test 19: A peer that claims a slot ahead of our head is cached optimistically +/// with `verified = false`. +#[test] +fn test_optimistic_caching_for_ahead_peer() { + let mut rig = TestRig::test_setup(); + let proof_peer = rig.new_connected_proof_capable_peer(); + + // best_slot() == 0 at genesis; slot 999 is well ahead of our head. + rig.send_sync_message(SyncMessage::RpcExecutionProofStatus { + peer_id: proof_peer, + request_id: None, // inbound (peer-initiated) + status: ExecutionProofStatus { + slot: 999, + block_root: Hash256::random(), + }, + }); + + assert!( + rig.sync_manager + .proof_sync() + .peer_status(&proof_peer) + .is_some(), + "Ahead-of-head peer should be cached optimistically" + ); + assert_eq!( + rig.sync_manager + .proof_sync() + .peer_status(&proof_peer) + .map(|s| s.verified), + Some(false), + "Ahead-of-head peer must be cached as unverified" + ); +} + +/// Test 20: `start()` re-requests status for a peer whose cached entry is unverified. +/// +/// `needs_refresh()` returns `true` for unverified entries, so a subsequent `start()` +/// should issue a fresh `ExecutionProofStatus` request for that peer. +#[test] +fn test_start_refreshes_unverified_entries() { + let mut rig = TestRig::test_setup(); + let proof_peer = rig.new_connected_proof_capable_peer(); + + // Override the peer's initial (verified) cache entry with an optimistic one by sending + // an inbound status with a slot beyond our head — this makes the entry unverified. + rig.send_sync_message(SyncMessage::RpcExecutionProofStatus { + peer_id: proof_peer, + request_id: None, + status: ExecutionProofStatus { + slot: 999, + block_root: Hash256::random(), + }, + }); + assert_eq!( + rig.sync_manager + .proof_sync() + .peer_status(&proof_peer) + .map(|s| s.verified), + Some(false), + "Entry should be unverified after optimistic caching" + ); + + // First start(): peer is unverified → needs_refresh=true → sends a status request. + rig.sync_manager.start_proof_sync(); + let (id1, _) = rig.find_execution_proof_status_request(); + + // Respond with another optimistic status so the entry stays unverified. + rig.send_sync_message(SyncMessage::RpcExecutionProofStatus { + peer_id: proof_peer, + request_id: Some(id1), + status: ExecutionProofStatus { + slot: 999, + block_root: Hash256::random(), + }, + }); + assert_eq!( + rig.sync_manager + .proof_sync() + .peer_status(&proof_peer) + .map(|s| s.verified), + Some(false), + "Entry should still be unverified" + ); + + // Pause and restart — start() must re-request because the entry is still unverified. + rig.sync_manager.proof_sync_mut().pause(); + rig.sync_manager.start_proof_sync(); + + // If this succeeds, start() issued a fresh status request for the unverified peer. + let (_id2, peer2) = rig.find_execution_proof_status_request(); + assert_eq!( + peer2, proof_peer, + "New status request should target the same proof peer" + ); +} + +/// Test 21: An inbound `ExecutionProofStatus` (peer-initiated, `request_id = None`) +/// populates the proof-sync peer cache exactly as an outbound response does. +#[test] +fn test_inbound_status_populates_cache() { + let mut rig = TestRig::test_setup(); + let proof_peer = rig.new_connected_proof_capable_peer(); + + // Simulate a peer-initiated status exchange: the peer sends us their status. + rig.send_sync_message(SyncMessage::RpcExecutionProofStatus { + peer_id: proof_peer, + request_id: None, + status: ExecutionProofStatus { + slot: 42, + block_root: Hash256::random(), + }, + }); + + assert!( + rig.sync_manager + .proof_sync() + .peer_status(&proof_peer) + .is_some(), + "Inbound status must populate the cache" + ); + // Slot 42 > best_slot(0) → optimistically cached as unverified. + assert_eq!( + rig.sync_manager + .proof_sync() + .peer_status(&proof_peer) + .map(|s| s.verified), + Some(false), + "Slot ahead of head must be cached as unverified" + ); +} + +/// Test 22: `local_execution_proof_status` can be set and read back via `network_globals`. +/// +/// This verifies the `NetworkGlobals` getter/setter used by the proof-verified callback path +/// (in `gossip_methods.rs`) and consumed by `ProofSync.poll()`. +#[test] +fn test_local_execution_proof_status_read_write() { + let rig = TestRig::test_setup(); + + // Default should be zero slot. + let initial = rig.network_globals.local_execution_proof_status(); + assert_eq!(initial.slot, 0); + + // Set a new status and read it back. + let block_root = Hash256::repeat_byte(0xab); + rig.network_globals + .set_local_execution_proof_status(ExecutionProofStatus { + slot: 42, + block_root, + }); + + let updated = rig.network_globals.local_execution_proof_status(); + assert_eq!(updated.slot, 42); + assert_eq!(updated.block_root, block_root); +} + +/// Test 23: `ProofSync.poll()` uses `local_execution_proof_status` as the lower bound +/// for `start_slot`, so proofs already verified locally are never re-requested. +/// +/// Setup: peer announces slot 10, local proof status is at slot 7. +/// Expected: range request start_slot = max(finalized_slot, local_proof_slot) + 1 = 8. +#[test] +fn test_proof_sync_start_slot_respects_local_proof_status() { + let mut rig = TestRig::test_setup(); + + // Peer has proofs up to slot 10. + let _proof_peer = rig.new_proof_peer_with_status(10); + rig.harness.advance_slot(); + + // Simulate that we have already verified proofs up to slot 7 locally. + rig.network_globals + .set_local_execution_proof_status(ExecutionProofStatus { + slot: 7, + block_root: Hash256::repeat_byte(0xcc), + }); + + rig.sync_manager.start_proof_sync(); + rig.sync_manager.poll_proof_sync(); + + let ((_req_id, _peer_id), start_slot) = rig.find_execution_proofs_by_range_request_with_slot(); + + // start_slot must be at least local_proof_slot + 1 = 8. + assert!( + start_slot.as_u64() >= 8, + "start_slot {start_slot} should be >= 8 (local_proof_slot + 1)" + ); +} + +/// Test 24: When `local_execution_proof_status` is updated to a slot beyond the peer's +/// announced slot, `ProofSync.poll()` computes a zero gap and issues no range request. +#[test] +fn test_proof_sync_no_request_when_local_status_ahead_of_peer() { + let mut rig = TestRig::test_setup(); + + // Peer only has proofs up to slot 5. + let _proof_peer = rig.new_proof_peer_with_status(5); + rig.harness.advance_slot(); + + // Local proof status is already at slot 5 (equal to peer) — gap = 0. + rig.network_globals + .set_local_execution_proof_status(ExecutionProofStatus { + slot: 5, + block_root: Hash256::repeat_byte(0xdd), + }); + + rig.sync_manager.start_proof_sync(); + rig.sync_manager.poll_proof_sync(); + + // No range request should be emitted since start_slot (6) > peer_slot (5). + rig.expect_no_execution_proof_range_request(); +} diff --git a/beacon_node/store/src/lib.rs b/beacon_node/store/src/lib.rs index ae5b2e1e571..127dc3e1317 100644 --- a/beacon_node/store/src/lib.rs +++ b/beacon_node/store/src/lib.rs @@ -379,6 +379,9 @@ pub enum DBColumn { /// The dummy table is used to force the db to sync #[strum(serialize = "dmy")] Dummy, + /// For persisting ProofEngine state (EIP-8025). + #[strum(serialize = "prf")] + ProofEngine, } /// A block from the database, which might have an execution payload or not. @@ -421,7 +424,8 @@ impl DBColumn { | Self::BeaconRestorePoint | Self::DhtEnrs | Self::CustodyContext - | Self::OptimisticTransitionBlock => 32, + | Self::OptimisticTransitionBlock + | Self::ProofEngine => 32, Self::BeaconBlockRoots | Self::BeaconDataColumnCustodyInfo | Self::BeaconBlockRootsChunked diff --git a/beacon_node/store/src/metadata.rs b/beacon_node/store/src/metadata.rs index cf494684515..215cdb2b64d 100644 --- a/beacon_node/store/src/metadata.rs +++ b/beacon_node/store/src/metadata.rs @@ -4,7 +4,7 @@ use ssz::{Decode, Encode}; use ssz_derive::{Decode, Encode}; use types::{Hash256, Slot}; -pub const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(28); +pub const CURRENT_SCHEMA_VERSION: SchemaVersion = SchemaVersion(29); // All the keys that get stored under the `BeaconMeta` column. // diff --git a/book/src/help_bn.md b/book/src/help_bn.md index 5f3c43a7e42..e717067a0e2 100644 --- a/book/src/help_bn.md +++ b/book/src/help_bn.md @@ -5,7 +5,7 @@ The primary component which connects to the Ethereum 2.0 P2P network and downloads, verifies and stores blocks. Provides a HTTP API for querying the beacon chain and publishing messages to the network. -Usage: lighthouse beacon_node [OPTIONS] --execution-endpoint +Usage: lighthouse beacon_node [OPTIONS] Options: --auto-compact-db @@ -125,6 +125,8 @@ Options: --execution-endpoint Server endpoint for an execution layer JWT-authenticated HTTP JSON-RPC connection. Uses the same endpoint to populate the deposit cache. + Optional - at least one of --execution-endpoint or + --proof-engine-endpoint must be provided. --execution-jwt File path which contains the hex-encoded JWT secret for the execution endpoint provided in the --execution-endpoint flag. @@ -304,6 +306,10 @@ Options: which don't improve their payload after the first call, and high values are useful for ensuring the EL is given ample notice. Default: 1/3 of a slot. + --proof-engine-endpoint + Server endpoint for an EIP-8025 proof engine HTTP JSON-RPC connection. + Does not require JWT authentication. Optional - at least one of + --execution-endpoint or --proof-engine-endpoint must be provided. --proposer-reorg-cutoff Maximum delay after the start of the slot at which to propose a reorging block. Lower values can prevent failed reorgs by ensuring the diff --git a/book/src/help_vc.md b/book/src/help_vc.md index 2a9936d1d2f..5ee33774d61 100644 --- a/book/src/help_vc.md +++ b/book/src/help_vc.md @@ -115,6 +115,14 @@ Options: --network Name of the Eth2 chain Lighthouse will sync and follow. [possible values: mainnet, gnosis, chiado, sepolia, holesky, hoodi] + --proof-engine-endpoint + URL of the proof engine HTTP JSON-RPC endpoint for EIP-8025 execution + proofs. When set, the validator client will proactively monitor for + new blocks and request execution proofs from this endpoint. + --proof-types + Comma-separated list of proof type identifiers to request from the + proof engine (e.g., 0,1,2). If not specified, defaults to all + available types. --proposer-nodes Comma-separated addresses to one or more beacon node HTTP APIs. These specify nodes that are used to send beacon block proposals. A failure diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 52fcee1184d..2db5967d846 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -980,38 +980,6 @@ pub struct SseBlock { pub execution_optimistic: bool, } -#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)] -#[serde(bound = "E: EthSpec")] -pub struct SseBlockFull { - pub slot: Slot, - pub block: BeaconBlock, - pub execution_optimistic: bool, -} - -#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)] -struct SseBlockFullGeneric { - pub slot: Slot, - pub block: T, - pub execution_optimistic: bool, -} - -type VersionedSseBlockFull = ForkVersionedResponse>; - -impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for SseBlockFull { - fn context_deserialize(deserializer: D, context: ForkName) -> Result - where - D: Deserializer<'de>, - { - let helper = SseBlockFullGeneric::::deserialize(deserializer)?; - Ok(SseBlockFull { - slot: helper.slot, - block: BeaconBlock::context_deserialize(helper.block, context) - .map_err(serde::de::Error::custom)?, - execution_optimistic: helper.execution_optimistic, - }) - } -} - #[derive(PartialEq, Debug, Serialize, Deserialize, Clone)] pub struct SseBlobSidecar { pub block_root: Hash256, @@ -1206,13 +1174,23 @@ impl<'de> ContextDeserialize<'de, ForkName> for SseExtendedPayloadAttributes { } } +/// SSE event payload for a validated execution proof (EIP-8025). +/// +/// Emitted by the beacon node when an `ExecutionProof` passes verification, +/// allowing validator clients to resign the proof with their own key. +#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)] +pub struct SseExecutionProofValidated { + pub execution_proof: ExecutionProof, + #[serde(with = "serde_utils::quoted_u64")] + pub epoch: u64, +} + #[derive(PartialEq, Debug, Serialize, Clone)] #[serde(bound = "E: EthSpec", untagged)] pub enum EventKind { Attestation(Box>), SingleAttestation(Box), Block(SseBlock), - BlockFull(Box>), BlobSidecar(SseBlobSidecar), DataColumnSidecar(SseDataColumnSidecar), FinalizedCheckpoint(SseFinalizedCheckpoint), @@ -1230,6 +1208,7 @@ pub enum EventKind { AttesterSlashing(Box>), BlsToExecutionChange(Box), BlockGossip(Box), + ExecutionProofValidated(SseExecutionProofValidated), } impl EventKind { @@ -1237,7 +1216,6 @@ impl EventKind { match self { EventKind::Head(_) => "head", EventKind::Block(_) => "block", - EventKind::BlockFull(_) => "block_full", EventKind::BlobSidecar(_) => "blob_sidecar", EventKind::DataColumnSidecar(_) => "data_column_sidecar", EventKind::Attestation(_) => "attestation", @@ -1256,6 +1234,7 @@ impl EventKind { EventKind::AttesterSlashing(_) => "attester_slashing", EventKind::BlsToExecutionChange(_) => "bls_to_execution_change", EventKind::BlockGossip(_) => "block_gossip", + EventKind::ExecutionProofValidated(_) => "execution_proof_validated", } } @@ -1272,9 +1251,6 @@ impl EventKind { "block" => Ok(EventKind::Block(serde_json::from_str(data).map_err( |e| ServerError::InvalidServerSentEvent(format!("Block: {:?}", e)), )?)), - "block_full" => Ok(EventKind::BlockFull(serde_json::from_str(data).map_err( - |e| ServerError::InvalidServerSentEvent(format!("Block Full: {:?}", e)), - )?)), "blob_sidecar" => Ok(EventKind::BlobSidecar(serde_json::from_str(data).map_err( |e| ServerError::InvalidServerSentEvent(format!("Blob Sidecar: {:?}", e)), )?)), @@ -1352,6 +1328,14 @@ impl EventKind { "block_gossip" => Ok(EventKind::BlockGossip(serde_json::from_str(data).map_err( |e| ServerError::InvalidServerSentEvent(format!("Block Gossip: {:?}", e)), )?)), + "execution_proof_validated" => Ok(EventKind::ExecutionProofValidated( + serde_json::from_str(data).map_err(|e| { + ServerError::InvalidServerSentEvent(format!( + "Execution Proof Validated: {:?}", + e + )) + })?, + )), _ => Err(ServerError::InvalidServerSentEvent( "Could not parse event tag".to_string(), )), @@ -1371,7 +1355,6 @@ pub struct EventQuery { pub enum EventTopic { Head, Block, - BlockFull, BlobSidecar, DataColumnSidecar, Attestation, @@ -1390,6 +1373,7 @@ pub enum EventTopic { ProposerSlashing, BlsToExecutionChange, BlockGossip, + ExecutionProofValidated, } impl FromStr for EventTopic { @@ -1399,7 +1383,6 @@ impl FromStr for EventTopic { match s { "head" => Ok(EventTopic::Head), "block" => Ok(EventTopic::Block), - "block_full" => Ok(EventTopic::BlockFull), "blob_sidecar" => Ok(EventTopic::BlobSidecar), "data_column_sidecar" => Ok(EventTopic::DataColumnSidecar), "attestation" => Ok(EventTopic::Attestation), @@ -1418,6 +1401,7 @@ impl FromStr for EventTopic { "proposer_slashing" => Ok(EventTopic::ProposerSlashing), "bls_to_execution_change" => Ok(EventTopic::BlsToExecutionChange), "block_gossip" => Ok(EventTopic::BlockGossip), + "execution_proof_validated" => Ok(EventTopic::ExecutionProofValidated), _ => Err("event topic cannot be parsed.".to_string()), } } @@ -1428,7 +1412,6 @@ impl fmt::Display for EventTopic { match self { EventTopic::Head => write!(f, "head"), EventTopic::Block => write!(f, "block"), - EventTopic::BlockFull => write!(f, "block_full"), EventTopic::BlobSidecar => write!(f, "blob_sidecar"), EventTopic::DataColumnSidecar => write!(f, "data_column_sidecar"), EventTopic::Attestation => write!(f, "attestation"), @@ -1447,6 +1430,7 @@ impl fmt::Display for EventTopic { EventTopic::ProposerSlashing => write!(f, "proposer_slashing"), EventTopic::BlsToExecutionChange => write!(f, "bls_to_execution_change"), EventTopic::BlockGossip => write!(f, "block_gossip"), + EventTopic::ExecutionProofValidated => write!(f, "execution_proof_validated"), } } } @@ -2565,31 +2549,4 @@ mod test { let roundtrip = O::context_deserialize::(deserializer, fork_name).unwrap(); assert_eq!(original, roundtrip); } - - #[test] - fn test_versioned_sse_block_full_round_trip() { - let rng = &mut XorShiftRng::from_seed([42; 16]); - for fork_name in ForkName::list_all() { - let beacon_block = map_fork_name!(fork_name, BeaconBlock, <_>::random_for_test(rng)); - let slot = Slot::random_for_test(rng); - - let versioned_response = VersionedSseBlockFull:: { - version: fork_name, - metadata: EmptyMetadata {}, - data: SseBlockFull { - slot, - block: beacon_block, - execution_optimistic: true, - }, - }; - - let json_str = serde_json::to_string(&versioned_response).unwrap(); - let deserialized: VersionedSseBlockFull = - serde_json::from_str(&json_str).unwrap(); - - assert_eq!(versioned_response, deserialized); - assert!(deserialized.data.execution_optimistic); - println!("fork_name: {:?} PASSED", fork_name); - } - } } diff --git a/common/logging/Cargo.toml b/common/logging/Cargo.toml index 41c82dbd61b..75702669db0 100644 --- a/common/logging/Cargo.toml +++ b/common/logging/Cargo.toml @@ -5,7 +5,7 @@ authors = ["blacktemplar "] edition = { workspace = true } [features] -test_logger = [] # Print log output to stderr when running tests instead of dropping it +test_logger = [] [dependencies] chrono = { version = "0.4", default-features = false, features = ["clock", "std"] } @@ -13,7 +13,7 @@ logroller = { workspace = true } metrics = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -tokio = { workspace = true, features = [ "time" ] } +tokio = { workspace = true, features = ["time"] } tracing = { workspace = true } tracing-appender = { workspace = true } tracing-core = { workspace = true } diff --git a/consensus/fork_choice/tests/tests.rs b/consensus/fork_choice/tests/tests.rs index d3a84ee85be..46ac008b900 100644 --- a/consensus/fork_choice/tests/tests.rs +++ b/consensus/fork_choice/tests/tests.rs @@ -117,8 +117,7 @@ impl ForkChoiceTest { let mut shutdown_receiver = mutex.lock(); shutdown_receiver.close(); - let msg = shutdown_receiver.try_next().unwrap(); - msg.is_some() + shutdown_receiver.try_recv().is_ok() } /// Assert there was a shutdown signal sent by the beacon chain. diff --git a/consensus/proto_array/src/fork_choice_test_definition/execution_status.rs b/consensus/proto_array/src/fork_choice_test_definition/execution_status.rs index aa26a843069..c8cd427ac60 100644 --- a/consensus/proto_array/src/fork_choice_test_definition/execution_status.rs +++ b/consensus/proto_array/src/fork_choice_test_definition/execution_status.rs @@ -1,5 +1,6 @@ use super::*; +#[allow(clippy::vec_init_then_push)] pub fn get_execution_status_test_definition_01() -> ForkChoiceTestDefinition { let balances = vec![1; 2]; let mut ops = vec![]; @@ -402,6 +403,7 @@ pub fn get_execution_status_test_definition_01() -> ForkChoiceTestDefinition { } } +#[allow(clippy::vec_init_then_push)] pub fn get_execution_status_test_definition_02() -> ForkChoiceTestDefinition { let balances = vec![1; 2]; let mut ops = vec![]; @@ -766,6 +768,7 @@ pub fn get_execution_status_test_definition_02() -> ForkChoiceTestDefinition { } } +#[allow(clippy::vec_init_then_push)] pub fn get_execution_status_test_definition_03() -> ForkChoiceTestDefinition { let balances = vec![1_000; 2_000]; let mut ops = vec![]; diff --git a/consensus/proto_array/src/fork_choice_test_definition/ffg_updates.rs b/consensus/proto_array/src/fork_choice_test_definition/ffg_updates.rs index 3b31616145d..6aa45da5707 100644 --- a/consensus/proto_array/src/fork_choice_test_definition/ffg_updates.rs +++ b/consensus/proto_array/src/fork_choice_test_definition/ffg_updates.rs @@ -1,5 +1,6 @@ use super::*; +#[allow(clippy::vec_init_then_push)] pub fn get_ffg_case_01_test_definition() -> ForkChoiceTestDefinition { let balances = vec![1; 2]; let mut ops = vec![]; @@ -104,6 +105,7 @@ pub fn get_ffg_case_01_test_definition() -> ForkChoiceTestDefinition { } } +#[allow(clippy::vec_init_then_push)] pub fn get_ffg_case_02_test_definition() -> ForkChoiceTestDefinition { let balances = vec![1; 2]; let mut ops = vec![]; diff --git a/consensus/proto_array/src/fork_choice_test_definition/votes.rs b/consensus/proto_array/src/fork_choice_test_definition/votes.rs index 01994fff9b2..01eb12e85e5 100644 --- a/consensus/proto_array/src/fork_choice_test_definition/votes.rs +++ b/consensus/proto_array/src/fork_choice_test_definition/votes.rs @@ -1,5 +1,6 @@ use super::*; +#[allow(clippy::vec_init_then_push)] pub fn get_votes_test_definition() -> ForkChoiceTestDefinition { let mut balances = vec![1; 2]; let mut ops = vec![]; diff --git a/consensus/types/Cargo.toml b/consensus/types/Cargo.toml index 78c6f871cb4..8db97f83632 100644 --- a/consensus/types/Cargo.toml +++ b/consensus/types/Cargo.toml @@ -1,10 +1,7 @@ [package] name = "types" version = "0.2.1" -authors = [ - "Paul Hauner ", - "Age Manning ", -] +authors = ["Paul Hauner ", "Age Manning "] edition = { workspace = true } [features] diff --git a/consensus/types/src/core/chain_spec.rs b/consensus/types/src/core/chain_spec.rs index 65a0b5a49be..d3f585199c1 100644 --- a/consensus/types/src/core/chain_spec.rs +++ b/consensus/types/src/core/chain_spec.rs @@ -820,7 +820,9 @@ impl ChainSpec { let blob_retention_epoch = current_epoch.saturating_sub(self.min_epochs_for_blob_sidecars_requests); match self.fulu_fork_epoch { - Some(fulu_fork_epoch) if self.min_epochs_for_data_column_sidecars_requests == 0 => None, + Some(_fulu_fork_epoch) if self.min_epochs_for_data_column_sidecars_requests == 0 => { + None + } Some(fulu_fork_epoch) if blob_retention_epoch > fulu_fork_epoch => Some( current_epoch.saturating_sub(self.min_epochs_for_data_column_sidecars_requests), ), diff --git a/consensus/types/src/execution/eip8025.rs b/consensus/types/src/execution/eip8025.rs index 1fcfc2c91b5..f5bd7e21e02 100644 --- a/consensus/types/src/execution/eip8025.rs +++ b/consensus/types/src/execution/eip8025.rs @@ -181,6 +181,11 @@ impl SignedExecutionProof { &self.message } + /// Returns the proof data of the underlying execution proof. + pub fn proof_data(&self) -> &ProofData { + &self.message.proof_data + } + /// Returns the new payload request root this proof validates. pub fn request_root(&self) -> Hash256 { self.message.public_input.new_payload_request_root diff --git a/deny.toml b/deny.toml index 54ede06429c..ecde322a98a 100644 --- a/deny.toml +++ b/deny.toml @@ -6,10 +6,6 @@ multiple-versions = "allow" deny = [ { crate = "ethers", reason = "legacy Ethereum crate, use alloy instead" }, - { crate = "ethereum-types", reason = "legacy Ethereum crate, use alloy-primitives instead" }, - { crate = "protobuf", reason = "use quick-protobuf instead" }, - { crate = "derivative", reason = "use educe or derive_more instead" }, - { crate = "ark-ff", reason = "present in Cargo.lock but not needed by Lighthouse" }, { crate = "strum", deny-multiple-versions = true, reason = "takes a long time to compile" }, { crate = "reqwest", deny-multiple-versions = true, reason = "takes a long time to compile" }, { crate = "aes", deny-multiple-versions = true, reason = "takes a long time to compile" }, @@ -17,6 +13,14 @@ deny = [ { crate = "pbkdf2", deny-multiple-versions = true, reason = "takes a long time to compile" }, { crate = "scrypt", deny-multiple-versions = true, reason = "takes a long time to compile" }, ] +# Crates banned upstream but required by zkboost/ethrex transitive dependencies +skip = [ + { crate = "ethereum-types@0.15.1", reason = "transitive dep of ethrex (zkboost)" }, + { crate = "protobuf@2.28.0", reason = "transitive dep via prometheus (zkboost)" }, + { crate = "protobuf@3.7.2", reason = "transitive dep of ethrex (zkboost)" }, + { crate = "derivative@2.2.0", reason = "transitive dep of ethrex (zkboost)" }, + { crate = "ark-ff", reason = "transitive dep of ethrex-levm (zkboost)" }, +] [sources] unknown-registry = "deny" @@ -24,4 +28,4 @@ unknown-git = "warn" allow-registry = ["https://github.com/rust-lang/crates.io-index"] [sources.allow-org] -github = ["sigp"] +github = ["sigp", "lambdaclass", "eth-act", "paradigmxyz", "frisitano"] diff --git a/lcli/Dockerfile b/lcli/Dockerfile index f1e4bd8ee04..959519fe8a1 100644 --- a/lcli/Dockerfile +++ b/lcli/Dockerfile @@ -1,7 +1,7 @@ # `lcli` requires the full project to be in scope, so this should be built either: # - from the `lighthouse` dir with the command: `docker build -f ./lcli/Dockerflie .` # - from the current directory with the command: `docker build -f ./Dockerfile ../` -FROM rust:1.88.0-bullseye AS builder +FROM rust:1.91.0-bullseye AS builder RUN apt-get update && apt-get -y upgrade && apt-get install -y cmake libclang-dev COPY . lighthouse ARG FEATURES diff --git a/lighthouse/Cargo.toml b/lighthouse/Cargo.toml index ebe00c9be59..7d070aecd27 100644 --- a/lighthouse/Cargo.toml +++ b/lighthouse/Cargo.toml @@ -4,7 +4,7 @@ version = { workspace = true } authors = ["Sigma Prime "] edition = { workspace = true } autotests = false -rust-version = "1.88.0" +rust-version = "1.91.0" # Prevent cargo-udeps from flagging the dummy package `target_check`, which exists only # to assert properties of the compilation target. diff --git a/lighthouse/environment/Cargo.toml b/lighthouse/environment/Cargo.toml index c5d831e1e10..669bd0db278 100644 --- a/lighthouse/environment/Cargo.toml +++ b/lighthouse/environment/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.2" authors = ["Paul Hauner "] edition = { workspace = true } +[features] +test-utils = [] + [dependencies] async-channel = { workspace = true } clap = { workspace = true } @@ -23,6 +26,3 @@ types = { workspace = true } [target.'cfg(not(target_family = "unix"))'.dependencies] ctrlc = { version = "3.1.6", features = ["termination"] } - -[features] -test-utils = [] diff --git a/scripts/local_testnet/network_params_eip8025.yaml b/scripts/local_testnet/network_params_eip8025.yaml new file mode 100644 index 00000000000..cd70704d0ea --- /dev/null +++ b/scripts/local_testnet/network_params_eip8025.yaml @@ -0,0 +1,39 @@ +# EIP-8025 multi-node testnet configuration. +# +# Uses MockProofNodeClient via the http://mock/{n}/ URL pattern. +# See start_eip8025_testnet.sh for usage. +# +# Full configuration reference: https://github.com/ethpandaops/ethereum-package#configuration +participants: + # Supernode participants with proof engine enabled + - cl_type: lighthouse + cl_image: lighthouse:local + el_type: geth + el_image: ethereum/client-go:latest + supernode: true + cl_extra_params: + - --target-peers=3 + - --proof-engine-endpoint=http://mock/0/ + vc_extra_params: + - --proof-engine-endpoint=http://mock/0/ + count: 2 + # Non-supernode participants with proof engine enabled + - cl_type: lighthouse + cl_image: lighthouse:local + el_type: geth + el_image: ethereum/client-go:latest + supernode: false + cl_extra_params: + - --target-peers=3 + - --proof-engine-endpoint=http://mock/0/ + vc_extra_params: + - --proof-engine-endpoint=http://mock/0/ + count: 2 +network_params: + fulu_fork_epoch: 0 + seconds_per_slot: 6 +snooper_enabled: false +global_log_level: debug +additional_services: + - dora + - prometheus_grafana diff --git a/scripts/local_testnet/start_eip8025_testnet.sh b/scripts/local_testnet/start_eip8025_testnet.sh new file mode 100755 index 00000000000..21cc60ebace --- /dev/null +++ b/scripts/local_testnet/start_eip8025_testnet.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash + +# Start a local EIP-8025 testnet with mock proof engines using Kurtosis. +# +# Requires: docker, kurtosis, yq +# +# This script builds Lighthouse and launches a Kurtosis enclave using +# network_params_eip8025.yaml. Mock proof engines are enabled via the +# http://mock/0/ URL pattern (no special build feature required). + +set -Eeuo pipefail + +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +ROOT_DIR="$SCRIPT_DIR/../.." +ENCLAVE_NAME=eip8025-testnet +NETWORK_PARAMS_FILE=$SCRIPT_DIR/network_params_eip8025.yaml +ETHEREUM_PKG_VERSION=main + +BUILD_IMAGE=true +KEEP_ENCLAVE=false + +# Get options +while getopts "e:n:bkh" flag; do + case "${flag}" in + e) ENCLAVE_NAME=${OPTARG};; + n) NETWORK_PARAMS_FILE=${OPTARG};; + b) BUILD_IMAGE=false;; + k) KEEP_ENCLAVE=true;; + h) + echo "Start a local EIP-8025 testnet with Kurtosis." + echo + echo "usage: $0 " + echo + echo "Options:" + echo " -e: enclave name default: $ENCLAVE_NAME" + echo " -n: kurtosis network params file path default: $NETWORK_PARAMS_FILE" + echo " -b: skip building Lighthouse docker image" + echo " -k: keep existing enclave (don't destroy first)" + echo " -h: this help" + exit + ;; + esac +done + +LH_IMAGE_NAME=$(yq eval ".participants[0].cl_image" "$NETWORK_PARAMS_FILE") + +for cmd in docker kurtosis yq; do + if ! command -v "$cmd" &> /dev/null; then + echo "$cmd is not installed. Please install $cmd and try again." + exit 1 + fi +done + +if [ "$KEEP_ENCLAVE" = false ]; then + kurtosis enclave rm -f "$ENCLAVE_NAME" 2>/dev/null || true +fi + +if [ "$BUILD_IMAGE" = true ]; then + echo "Building Lighthouse Docker image." + docker build \ + --build-arg FEATURES=portable,spec-minimal \ + -f "$ROOT_DIR/Dockerfile" \ + -t "$LH_IMAGE_NAME" \ + "$ROOT_DIR" +else + echo "Skipping Lighthouse Docker image build." +fi + +echo "Starting EIP-8025 testnet enclave: $ENCLAVE_NAME" +kurtosis run --enclave "$ENCLAVE_NAME" \ + "github.com/ethpandaops/ethereum-package@$ETHEREUM_PKG_VERSION" \ + --args-file "$NETWORK_PARAMS_FILE" + +echo "EIP-8025 testnet started!" +echo +echo "Useful commands:" +echo " kurtosis enclave inspect $ENCLAVE_NAME" +echo " kurtosis service logs $ENCLAVE_NAME cl-1-lighthouse-geth" +echo " kurtosis enclave rm -f $ENCLAVE_NAME" diff --git a/slasher/service/src/service.rs b/slasher/service/src/service.rs index c0e6a8a0cd8..c1a5f1c4e1d 100644 --- a/slasher/service/src/service.rs +++ b/slasher/service/src/service.rs @@ -171,6 +171,7 @@ impl SlasherService { Self::process_proposer_slashings(beacon_chain, slasher, network_sender); } + #[allow(clippy::result_large_err)] fn process_attester_slashings( beacon_chain: &BeaconChain, slasher: &Slasher, @@ -223,6 +224,7 @@ impl SlasherService { } } + #[allow(clippy::result_large_err)] fn process_proposer_slashings( beacon_chain: &BeaconChain, slasher: &Slasher, diff --git a/testing/node_test_rig/Cargo.toml b/testing/node_test_rig/Cargo.toml index 4eef3e25dc9..ba1b99ffe45 100644 --- a/testing/node_test_rig/Cargo.toml +++ b/testing/node_test_rig/Cargo.toml @@ -8,21 +8,26 @@ edition = { workspace = true } beacon_node = { workspace = true } beacon_node_fallback = { workspace = true } bls = { workspace = true } +bytes = { workspace = true } environment = { workspace = true } eth2 = { workspace = true, features = ["events"] } +ethereum_ssz = { workspace = true } +ethereum_ssz_derive = { workspace = true } execution_layer = { workspace = true } +futures = { workspace = true } hex = { workspace = true } -mockito = { workspace = true } parking_lot = { workspace = true } reqwest = { workspace = true } sensitive_url = { workspace = true } +serde = { workspace = true } serde_json = { workspace = true } ssz_types = { workspace = true } task_executor = { workspace = true } tempfile = { workspace = true } tokio = { workspace = true } +tokio-stream = { workspace = true } tracing = { workspace = true } -tree_hash = { workspace = true} +tree_hash = { workspace = true } types = { workspace = true } validator_client = { workspace = true } validator_dir = { workspace = true, features = ["insecure_keys"] } diff --git a/testing/node_test_rig/src/lib.rs b/testing/node_test_rig/src/lib.rs index b167fb2dd02..b6ed74b879e 100644 --- a/testing/node_test_rig/src/lib.rs +++ b/testing/node_test_rig/src/lib.rs @@ -25,11 +25,6 @@ pub use execution_layer::test_utils::{ }; pub use validator_client::{ApiSecret, Config as ValidatorConfig}; -mod mock_proof_engine_server; -pub use mock_proof_engine_server::{ - MockProofEngineConfig, MockProofEngineServer, ProofEngineServerConfig, ProofRequestRecord, -}; - /// The global timeout for HTTP requests to the beacon node. const HTTP_TIMEOUT: Duration = Duration::from_secs(8); /// The timeout for a beacon node to start up. @@ -278,28 +273,3 @@ impl LocalExecutionNode { } } } - -/// Provides a mock proof engine that is running in the current process. -/// -/// Intended for use in testing and simulation. Not for production. -pub struct LocalProofEngine { - pub server: MockProofEngineServer, - pub datadir: TempDir, -} - -impl LocalProofEngine { - pub async fn new(context: RuntimeContext, config: MockProofEngineConfig) -> Self { - let datadir = TempBuilder::new() - .prefix("lighthouse_proof_engine") - .tempdir() - .expect("should create temp directory for proof engine"); - - let server = MockProofEngineServer::new(config, context.executor.clone()).await; - - Self { server, datadir } - } - - pub fn set_validator_client(&mut self, client: ValidatorClientHttpClient) { - self.server.set_validator_callback(client.into()); - } -} diff --git a/testing/node_test_rig/src/mock_proof_engine_server.rs b/testing/node_test_rig/src/mock_proof_engine_server.rs deleted file mode 100644 index 0534ce9a1ff..00000000000 --- a/testing/node_test_rig/src/mock_proof_engine_server.rs +++ /dev/null @@ -1,420 +0,0 @@ -//! Mock proof engine server for testing EIP-8025 execution proofs. -//! -//! Provides an HTTP JSON-RPC server that simulates an external proof engine backend -//! for integration testing. Uses mockito to mock the HTTP endpoints. - -// TODO: Move this module into the execution_layer crate - -use super::ValidatorClientHttpClient; -use eth2::lighthouse_vc::types::SignExecutionProofRequest; -use execution_layer::NewPayloadRequestFulu; -use execution_layer::json_structures::JsonExecutionPayloadFulu; -use mockito::{Matcher, Mock, Server, ServerGuard}; -use parking_lot::{Mutex, RwLock}; -use sensitive_url::SensitiveUrl; -use serde_json::json; -use ssz_types::VariableList; -use std::sync::Arc; -use std::time::Duration; -use task_executor::TaskExecutor; -use tree_hash::TreeHash; -use types::execution::eip8025::{ - ExecutionProof, ProofAttributes, ProofGenId, ProofType, PublicInput, -}; -use types::{EthSpec, ExecutionPayloadFulu, ExecutionRequests, Hash256, VersionedHash}; - -/// Configuration for a mock proof engine. -#[derive(Clone)] -pub struct MockProofEngineConfig { - pub server_config: ProofEngineServerConfig, - pub callback_delay_ms: u64, - pub callback_url: Arc>>>, -} - -impl Default for MockProofEngineConfig { - fn default() -> Self { - Self { - server_config: ProofEngineServerConfig::default(), - callback_delay_ms: 200, - callback_url: Arc::new(RwLock::new(None)), - } - } -} - -/// Configuration for proof engine server. -#[derive(Clone)] -pub struct ProofEngineServerConfig { - pub listen_port: u16, - pub listen_addr: std::net::Ipv4Addr, -} - -impl Default for ProofEngineServerConfig { - fn default() -> Self { - Self { - listen_port: 0, - listen_addr: std::net::Ipv4Addr::LOCALHOST, - } - } -} - -/// Record of a proof request received by the mock server. -#[derive(Clone, Debug)] -pub struct ProofRequestRecord { - pub proof_gen_id: ProofGenId, - pub new_payload_request_root: Hash256, - pub proof_types: Vec, - pub timestamp: std::time::Instant, -} - -/// Mock proof engine HTTP server. -/// -/// Implements the JSON-RPC endpoints for: -/// - engine_requestProofsV1: Accept proof requests and return ProofGenId -/// - engine_verifyExecutionProofV1: Verify proof validity -pub struct MockProofEngineServer { - server: ServerGuard, - config: MockProofEngineConfig, - proof_requests: Arc>>, - executor: TaskExecutor, - _mocks: Vec, // Keep mocks alive - _phantom: std::marker::PhantomData, -} - -impl MockProofEngineServer { - /// Create a new mock proof engine server. - pub async fn new(config: MockProofEngineConfig, executor: TaskExecutor) -> Self { - // Use Server::new_async() to avoid starting a runtime within a runtime - let server = Server::new_async().await; - let proof_requests = Arc::new(Mutex::new(Vec::new())); - - let mut mock_server = Self { - server, - config, - proof_requests, - executor, - _mocks: Vec::new(), - _phantom: std::marker::PhantomData, - }; - - mock_server.setup_endpoints(); - mock_server - } - - pub fn set_validator_callback(&mut self, client: Arc) { - *self.config.callback_url.write() = Some(client); - } - - /// Setup all HTTP endpoints. - fn setup_endpoints(&mut self) { - self.setup_request_proofs_endpoint(); - self.setup_verify_proof_endpoint(); - } - - /// Setup the engine_requestProofsV1 endpoint. - fn setup_request_proofs_endpoint(&mut self) { - let proof_requests = self.proof_requests.clone(); - let callback_delay = self.config.callback_delay_ms; - let validator_client_ref = self.config.callback_url.clone(); - let task_executor = self.executor.clone(); - - let mock = self - .server - .mock("POST", "/") - .match_body(Matcher::Regex( - r#".*"method"\s*:\s*"engine_requestProofsV1".*"#.to_string(), - )) - .with_status(200) - .with_body_from_request(move |request| { - // Helper function to return JSON-RPC error response - let error_response = |error_msg: &str| -> Vec { - serde_json::to_vec(&json!({ - "jsonrpc": "2.0", - "error": { - "code": -32602, - "message": format!("Invalid params: {}", error_msg) - }, - "id": 1 - })) - .unwrap_or_else(|_| b"{\"error\":\"internal error\"}".to_vec()) - }; - - // Parse JSON-RPC request with error handling - let body_bytes = match request.body() { - Ok(bytes) => bytes, - Err(e) => { - return error_response(&format!("failed to read request body: {}", e)); - } - }; - - let body: serde_json::Value = match serde_json::from_slice(body_bytes) { - Ok(v) => v, - Err(e) => return error_response(&format!("invalid JSON: {}", e)), - }; - - // Parse params array - let Some(params) = body["params"].as_array() else { - return error_response("params is not an array"); - }; - - if params.len() < 5 { - return error_response(&format!("expected 5 params, got {}", params.len())); - } - - // Parse execution payload - let execution_payload_json: JsonExecutionPayloadFulu = - match serde_json::from_value(params[0].clone()) { - Ok(v) => v, - Err(e) => { - return error_response(&format!("invalid execution payload: {}", e)); - } - }; - - let execution_payload: ExecutionPayloadFulu = match execution_payload_json - .try_into() - { - Ok(v) => v, - Err(e) => return error_response(&format!("failed to convert payload: {}", e)), - }; - - // Parse versioned hashes - let versioned_hashes: VariableList = - match serde_json::from_value(params[1].clone()) { - Ok(v) => v, - Err(e) => { - return error_response(&format!("invalid versioned hashes: {}", e)); - } - }; - - // Parse parent beacon block root - let parent_beacon_block_root: Hash256 = - match serde_json::from_value(params[2].clone()) { - Ok(v) => v, - Err(e) => return error_response(&format!("invalid parent root: {}", e)), - }; - - // Deserialize execution requests from JSON with fork context - let execution_requests_bytes = match serde_json::from_value(params[3].clone()) { - Ok(v) => v, - Err(e) => { - return error_response(&format!("invalid execution requests: {}", e)); - } - }; - let execution_requests = match ExecutionRequests::::from_execution_requests_list( - execution_requests_bytes, - ) { - Ok(r) => r, - Err(e) => return error_response(&e), - }; - - // Parse proof attributes - let proof_attributes: ProofAttributes = - match serde_json::from_value(params[4].clone()) { - Ok(v) => v, - Err(e) => { - return error_response(&format!("invalid proof attributes: {}", e)); - } - }; - - // Compute request root with properly decoded execution_requests - let new_payload_request = NewPayloadRequestFulu { - execution_payload: &execution_payload, - versioned_hashes, - parent_beacon_block_root, - execution_requests: &execution_requests, - }; - let request_root = new_payload_request.tree_hash_root(); - - // Trigger callback if validator client is configured - if let Some(validator) = validator_client_ref.read().as_ref() { - tracing::info!( - target: "simulator", - ?request_root, - proof_types = ?proof_attributes.proof_types, - "Triggering proof callback" - ); - let _ = Self::proof_callback( - validator.clone(), - callback_delay, - task_executor.clone(), - request_root, - proof_attributes.proof_types.clone(), - ); - } - - // Generate deterministic ProofGenId from request root - let mut proof_gen_id = [0u8; 8]; - proof_gen_id.copy_from_slice(&request_root.0[0..8]); - - // Store request - proof_requests.lock().push(ProofRequestRecord { - proof_gen_id, - new_payload_request_root: request_root, - proof_types: proof_attributes.proof_types.clone(), - timestamp: std::time::Instant::now(), - }); - - tracing::info!( - target: "simulator", - proof_gen_id = hex::encode(proof_gen_id), - ?request_root, - num_proof_types = proof_attributes.proof_types.len(), - "Proof request recorded" - ); - - // Return success response - serde_json::to_vec(&json!({ - "jsonrpc": "2.0", - "result": format!("0x{}", hex::encode(proof_gen_id)), - "id": 1 - })) - .unwrap_or_else(|_| b"{\"error\":\"internal error\"}".to_vec()) - }) - .create(); - - self._mocks.push(mock); - } - - /// Setup the engine_verifyExecutionProofV1 endpoint. - fn setup_verify_proof_endpoint(&mut self) { - let mock = self.server - .mock("POST", "/") - .match_body(Matcher::Regex( - r#".*"method"\s*:\s*"engine_verifyExecutionProofV1".*"#.to_string(), - )) - .with_status(200) - .with_body_from_request(move |request| { - // Validate the request has a body - let _body_bytes = match request.body() { - Ok(bytes) => bytes, - Err(e) => { - return serde_json::to_vec(&json!({ - "jsonrpc": "2.0", - "error": {"code": -32602, "message": format!("failed to read request body: {}", e)}, - "id": 1 - })) - .unwrap_or_else(|_| b"{\"error\":\"internal error\"}".to_vec()); - } - }; - - // For the verify endpoint, we just return VALID for all properly formatted requests - serde_json::to_vec(&json!({ - "jsonrpc": "2.0", - "result": {"status": "VALID"}, - "id": 1 - })) - .unwrap_or_else(|_| b"{\"error\":\"internal error\"}".to_vec()) - }) - .create(); - - self._mocks.push(mock); - } - - /// Get the URL of the mock server. - pub fn url(&self) -> SensitiveUrl { - SensitiveUrl::parse(&self.server.url()).unwrap() - } - - /// Get all proof requests received by the server. - pub fn get_proof_requests(&self) -> Vec { - self.proof_requests.lock().clone() - } - - /// Manually trigger a callback to the validator client with a generated proof. - /// - /// This simulates the proof engine calling back to the validator client - /// after generating a proof asynchronously. - pub fn proof_callback( - client: Arc, - callback_delay: u64, - task_executor: TaskExecutor, - new_payload_request_root: Hash256, - proof_types: Vec, - ) -> Result<(), String> { - task_executor.spawn( - async move { - tracing::info!( - target: "simulator", - delay_ms = callback_delay, - "Proof callback task started, sleeping" - ); - - tokio::time::sleep(Duration::from_millis(callback_delay)).await; - - tracing::info!(target: "simulator", "Fetching validators for callback"); - - let validators = match client.get_lighthouse_validators().await { - Ok(v) => v, - Err(e) => { - tracing::error!(target: "simulator", error = ?e, "Failed to get validators"); - return; - } - }; - - let pubkey = match validators.data.first() { - Some(v) => v.voting_pubkey, - None => { - tracing::error!(target: "simulator", "No validators found"); - return; - } - }; - - tracing::info!( - target: "simulator", - ?pubkey, - num_proof_types = proof_types.len(), - "Generating and sending proofs" - ); - - let execution_proofs = - Self::generate_dummy_proofs(new_payload_request_root, proof_types); - - for execution_proof in execution_proofs { - tracing::info!( - target: "simulator", - proof_type = ?execution_proof.proof_type, - "Sending proof to validator client" - ); - - let request_body = SignExecutionProofRequest { - execution_proof, - epoch: None, - }; - - match client.post_execution_proof(&pubkey, request_body).await { - Ok(_) => { - tracing::info!(target: "simulator", "Proof sent successfully"); - } - Err(e) => { - tracing::error!(target: "simulator", error = ?e, "Failed to send proof"); - } - } - } - }, - "proof_callback", - ); - - Ok(()) - } - - /// Generate a dummy execution proof for testing. - fn generate_dummy_proofs(root: Hash256, proof_types: Vec) -> Vec { - let mut proofs = vec![]; - - for proof_type in proof_types { - let mut proof_bytes = vec![0xDE, 0xAD, 0xBE, 0xEF]; - proof_bytes.extend_from_slice(&root.0[0..16]); - - let proof = ExecutionProof { - proof_data: VariableList::new(proof_bytes).unwrap(), - proof_type, - public_input: PublicInput { - new_payload_request_root: root, - }, - }; - - proofs.push(proof); - } - - proofs - } -} diff --git a/testing/proof_engine/Cargo.toml b/testing/proof_engine/Cargo.toml index c4d0718d6eb..ebc69fbc120 100644 --- a/testing/proof_engine/Cargo.toml +++ b/testing/proof_engine/Cargo.toml @@ -4,9 +4,9 @@ edition.workspace = true version.workspace = true [dependencies] -simulator = { path = "../simulator", features = ["test-utils"] } +anyhow = { workspace = true } network = { workspace = true, features = ["disable-backfill"] } +simulator = { path = "../simulator", features = ["test-utils"] } tokio = { workspace = true } tracing = { workspace = true } -anyhow = { workspace = true } diff --git a/testing/proof_engine/src/lib.rs b/testing/proof_engine/src/lib.rs index bdb433980e1..083c577f890 100644 --- a/testing/proof_engine/src/lib.rs +++ b/testing/proof_engine/src/lib.rs @@ -36,11 +36,12 @@ mod test { extra_nodes: 0, proof_generator_nodes: 1, proof_verifier_nodes: 1, - genesis_delay: 20, + genesis_delay: 120, }) } #[tokio::test] + #[cfg_attr(debug_assertions, ignore = "too slow in debug mode")] async fn test_proof_engine_basic() -> anyhow::Result<()> { let mut fixture = test_fixture_builder_base() .with_log_level(LevelFilter::DEBUG) @@ -50,29 +51,31 @@ mod test { fixture.payloads_valid(); fixture.wait_for_genesis().await?; - // Verify continuous operation - tokio::time::sleep(Duration::from_secs(60)).await; - - let requests = fixture + // Subscribe before the run so events accumulate in the broadcast buffer. + let mut event_rx = fixture .network - .proof_engines - .read() - .first() - .unwrap() - .server - .get_proof_requests(); + .proof_generator_subscribe_client_events() + .expect("proof generator node should expose a mock client event stream"); + tokio::time::sleep(Duration::from_secs(60)).await; + + // Drain and count ProofRequested events. + let mut proof_requests = 0usize; + while let Ok(event) = event_rx.try_recv() { + if matches!(event, MockClientEvent::ProofRequested { .. }) { + proof_requests += 1; + } + } assert!( - requests.len() >= 2, - "Should have received multiple proof requests" + proof_requests > 0, + "expected at least one proof request after 60s" ); - // TODO: Add more assertions after we extend test framework. For now just check logs to ensure correctness. - Ok(()) } #[tokio::test] + #[cfg_attr(debug_assertions, ignore = "too slow in debug mode")] async fn test_proof_engine_sync() -> anyhow::Result<()> { let mut fixture = test_fixture_builder_base() .map_spec(|spec| { diff --git a/testing/proof_engine_zkboost/Cargo.toml b/testing/proof_engine_zkboost/Cargo.toml new file mode 100644 index 00000000000..ab3ef2c7b54 --- /dev/null +++ b/testing/proof_engine_zkboost/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "proof_engine_zkboost_test" +version = "0.1.0" +edition.workspace = true + +[features] +portable = ["types/portable"] + +[dependencies] +anyhow = { workspace = true } +axum = { workspace = true } +bytes = { workspace = true } +execution_layer = { workspace = true } +futures = { workspace = true } +metrics-exporter-prometheus = { workspace = true } +reqwest = { workspace = true } +sensitive_url = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +strum = { workspace = true } +tokio = { workspace = true } +tokio-stream = { workspace = true } +tokio-util = { workspace = true } +tracing = { workspace = true } +types = { workspace = true } +url = { workspace = true } +zkboost-server = { workspace = true } +zkboost-types = { workspace = true } diff --git a/testing/proof_engine_zkboost/src/lib.rs b/testing/proof_engine_zkboost/src/lib.rs new file mode 100644 index 00000000000..5c3a318bcf4 --- /dev/null +++ b/testing/proof_engine_zkboost/src/lib.rs @@ -0,0 +1,295 @@ +//! Integration tests verifying wire-level compatibility between Lighthouse's +//! [`HttpProofNodeClient`] and the **real** zkBoost server. +//! +//! ## Architecture +//! +//! This test crate starts the real `zkBoostServer` (from the `zkboost-server` crate) +//! with mock zkVM backends, and validates that Lighthouse's `HttpProofNodeClient` +//! speaks the correct wire protocol against it. +//! +//! A lightweight mock Execution Layer serves fixture data (chain config + +//! execution witness) so the server can generate witnesses without a real node. +//! +//! ## What is validated +//! +//! - Lighthouse sends zkBoost string proof types in query params, URL paths, SSE +//! - The real server accepts Lighthouse's requests and returns valid responses +//! - SSE events with string `proof_type` values are correctly deserialized to u8 +//! - Full lifecycle: request → SSE event → proof download → verification + +pub mod zkboost_harness; + +#[cfg(test)] +mod tests { + use crate::zkboost_harness::{FIXTURE_NEW_PAYLOAD_REQUEST, ZkboostTestHarness}; + use execution_layer::eip8025::{HttpProofNodeClient, ProofNodeClient, ProofType}; + use futures::StreamExt; + use sensitive_url::SensitiveUrl; + use std::time::Duration; + use tokio::time::timeout; + use types::execution::eip8025::ProofAttributes; + use zkboost_types::ProofType as ZkBoostProofType; + + /// Helper: create an `HttpProofNodeClient` pointing at the test server. + fn client_for(url: &str) -> HttpProofNodeClient { + let sensitive_url = SensitiveUrl::parse(url).expect("server URL should be valid"); + HttpProofNodeClient::new(sensitive_url, Some(Duration::from_secs(30))) + } + + /// The u8 value for `EthrexZisk` (our default test proof type). + fn ethrex_zisk_u8() -> u8 { + ProofType::EthrexZisk.to_u8() + } + + // ─── Test 1: request_proofs succeeds against real server ───────────────── + + /// Verifies that `HttpProofNodeClient::request_proofs` sends the correct + /// wire format (string proof types in query param, SSZ body) and the real + /// zkBoost server accepts it and returns a root. + #[tokio::test] + async fn test_request_proofs_accepted_by_real_server() { + let harness = ZkboostTestHarness::start(3000).await; + let client = client_for(&harness.url()); + + let attrs = ProofAttributes { + proof_types: vec![ethrex_zisk_u8()], + }; + + let root = client + .request_proofs(FIXTURE_NEW_PAYLOAD_REQUEST.to_vec(), attrs) + .await + .expect("request_proofs should succeed against real server"); + + // The root should be non-zero (the server computes tree_hash_root of the SSZ). + assert!(!root.is_zero(), "returned root should be non-zero"); + } + + // ─── Test 2: SSE events from real server are parsed correctly ──────────── + + /// Verifies that SSE events from the real zkBoost server (which use string + /// proof types like `"ethrex-zisk"`) are correctly deserialized by + /// Lighthouse's client back to u8 values. + #[tokio::test] + async fn test_sse_events_from_real_server() { + let harness = ZkboostTestHarness::start(1000).await; + let client = client_for(&harness.url()); + + let attrs = ProofAttributes { + proof_types: vec![ethrex_zisk_u8()], + }; + + // Subscribe to events before requesting proofs. + let mut event_stream = client.subscribe_proof_events(None); + + let root = client + .request_proofs(FIXTURE_NEW_PAYLOAD_REQUEST.to_vec(), attrs) + .await + .expect("request_proofs should succeed"); + + // Wait for a proof event from the real server. + let event = timeout(Duration::from_secs(30), event_stream.next()) + .await + .expect("timed out waiting for SSE event") + .expect("stream ended") + .expect("stream error"); + + assert_eq!(event.new_payload_request_root(), root); + assert_eq!( + event.proof_type(), + ethrex_zisk_u8(), + "string 'ethrex-zisk' from real server should deserialize to u8 {}", + ethrex_zisk_u8() + ); + } + + // ─── Test 3: get_proof downloads proof from real server ────────────────── + + /// Verifies that `get_proof` uses the string proof type in the URL path + /// and successfully downloads a proof from the real server after completion. + #[tokio::test] + async fn test_get_proof_from_real_server() { + let harness = ZkboostTestHarness::start(1000).await; + let client = client_for(&harness.url()); + + let attrs = ProofAttributes { + proof_types: vec![ethrex_zisk_u8()], + }; + + // Subscribe and wait for proof completion. + let mut events = client.subscribe_proof_events(None); + + let root = client + .request_proofs(FIXTURE_NEW_PAYLOAD_REQUEST.to_vec(), attrs) + .await + .expect("request should succeed"); + + // Wait for proof_complete event. + let _event = timeout(Duration::from_secs(30), events.next()) + .await + .expect("timed out waiting for event") + .expect("stream ended") + .expect("stream error"); + + // Download the proof using string proof type in URL path. + let proof_bytes = client + .get_proof(root, ethrex_zisk_u8()) + .await + .expect("get_proof should succeed with string proof type in URL"); + + assert!(!proof_bytes.is_empty(), "proof should not be empty"); + } + + // ─── Test 4: verify_proof against real server ──────────────────────────── + + /// Verifies that `verify_proof` sends the string proof type in query params + /// and the real server accepts the verification request. + #[tokio::test] + async fn test_verify_proof_against_real_server() { + let harness = ZkboostTestHarness::start(1000).await; + let client = client_for(&harness.url()); + + let attrs = ProofAttributes { + proof_types: vec![ethrex_zisk_u8()], + }; + + let mut events = client.subscribe_proof_events(None); + + let root = client + .request_proofs(FIXTURE_NEW_PAYLOAD_REQUEST.to_vec(), attrs) + .await + .expect("request should succeed"); + + // Wait for completion. + let _event = timeout(Duration::from_secs(30), events.next()) + .await + .expect("timed out") + .expect("stream ended") + .expect("stream error"); + + // Download proof. + let proof = client + .get_proof(root, ethrex_zisk_u8()) + .await + .expect("get_proof should succeed"); + + // Verify proof. + let status = client + .verify_proof(root, ethrex_zisk_u8(), &proof) + .await + .expect("verify_proof should succeed against real server"); + + assert_eq!(status, types::execution::eip8025::ProofStatus::Valid); + } + + // ─── Test 5: invalid u8 proof type is rejected by client ───────────────── + + /// Verifies that an unmapped u8 value (e.g. 99) fails at the Lighthouse + /// client level before even reaching the server. + #[tokio::test] + async fn test_invalid_proof_type_rejected_by_client() { + let harness = ZkboostTestHarness::start(0).await; + let client = client_for(&harness.url()); + + let result = client + .get_proof(types::Hash256::repeat_byte(0xAA), 99) + .await; + assert!( + result.is_err(), + "u8 value 99 has no zkBoost mapping — should error at client level" + ); + } + + // ─── Test 6: ZkBoostProofType matches zkboost-types::ProofType ────────── + + /// Validates that Lighthouse's `ZkBoostProofType` enum covers all known + /// zkBoost proof types with matching string representations. + #[tokio::test] + async fn test_zkboost_proof_type_matches_upstream() { + // Collect all upstream ProofType variants. + let upstream: Vec<(String, usize)> = ProofType::all() + .iter() + .enumerate() + .map(|(i, pt)| (pt.as_str().to_string(), i)) + .collect(); + + // Verify Lighthouse's ZkBoostProofType has matching variants. + for (s, i) in &upstream { + let pt: ZkBoostProofType = s + .parse() + .unwrap_or_else(|_| panic!("'{s}' should parse as ZkBoostProofType")); + assert_eq!( + pt.as_str(), + s.as_str(), + "string representation should match upstream" + ); + assert_eq!( + pt as u8, *i as u8, + "u8 mapping for '{s}' should match upstream ordinal {i}" + ); + } + + // Verify all Lighthouse variants are in the upstream list. + let upstream_strs: Vec<&str> = upstream.iter().map(|(s, _)| s.as_str()).collect(); + for pt in ProofType::all() { + assert!( + upstream_strs.contains(&pt.as_str()), + "Lighthouse variant {:?} should exist in upstream zkBoost", + pt + ); + } + + // Counts should match. + assert_eq!( + ProofType::all().len(), + upstream.len(), + "variant count should match between Lighthouse and zkBoost" + ); + } + + // ─── Test 7: full lifecycle (request → SSE → download → verify) ───────── + + /// End-to-end lifecycle against the real zkBoost server. + #[tokio::test] + async fn test_full_lifecycle_against_real_server() { + let harness = ZkboostTestHarness::start(1000).await; + let client = client_for(&harness.url()); + + let attrs = ProofAttributes { + proof_types: vec![ethrex_zisk_u8()], + }; + + let mut events = client.subscribe_proof_events(None); + + // Step 1: Request proof. + let root = client + .request_proofs(FIXTURE_NEW_PAYLOAD_REQUEST.to_vec(), attrs) + .await + .expect("request should succeed"); + + assert!(!root.is_zero()); + + // Step 2: Wait for SSE proof_complete event. + let event = timeout(Duration::from_secs(30), events.next()) + .await + .expect("timed out waiting for event") + .expect("stream ended") + .expect("stream error"); + + assert_eq!(event.new_payload_request_root(), root); + assert_eq!(event.proof_type(), ethrex_zisk_u8()); + + // Step 3: Download proof. + let proof = client + .get_proof(root, ethrex_zisk_u8()) + .await + .expect("get_proof should succeed"); + assert!(!proof.is_empty()); + + // Step 4: Verify proof. + let status = client + .verify_proof(root, ethrex_zisk_u8(), &proof) + .await + .expect("verify_proof should succeed"); + assert_eq!(status, types::execution::eip8025::ProofStatus::Valid); + } +} diff --git a/testing/proof_engine_zkboost/src/zkboost_harness.rs b/testing/proof_engine_zkboost/src/zkboost_harness.rs new file mode 100644 index 00000000000..7e37fb2ca57 --- /dev/null +++ b/testing/proof_engine_zkboost/src/zkboost_harness.rs @@ -0,0 +1,173 @@ +//! Test harness that starts the **real** zkBoost server with mock zkVM backends. +//! +//! This validates that Lighthouse's [`HttpProofNodeClient`] speaks the correct +//! wire protocol by testing it against the actual zkBoost server implementation. +//! +//! ## Architecture +//! +//! 1. A lightweight mock Execution Layer (EL) that serves fixture data for +//! `debug_chainConfig` and `debug_executionWitnessByBlockHash` JSON-RPC methods. +//! 2. The real `zkBoostServer` configured with `zkVMConfig::Mock` backends. +//! 3. Lighthouse's `HttpProofNodeClient` as the system under test. + +use axum::{Json, extract::State, routing::post}; +use bytes::Bytes; +use metrics_exporter_prometheus::PrometheusBuilder; +use serde_json::Value; +use std::net::Ipv4Addr; +use std::sync::Arc; +use tokio::net::TcpListener; +use tokio_util::sync::CancellationToken; +use zkboost_server::{ + config::{Config, zkVMConfig}, + server::zkBoostServer, +}; +use zkboost_types::ProofType; + +// ─── Fixture Data ──────────────────────────────────────────────────────────── + +/// SSZ-encoded NewPayloadRequest from zkBoost's test fixture. +pub const FIXTURE_NEW_PAYLOAD_REQUEST: &[u8] = + include_bytes!("../tests/fixture/new_payload_request.ssz"); + +/// Chain config JSON from zkBoost's test fixture. +const FIXTURE_CHAIN_CONFIG: &str = include_str!("../tests/fixture/chain_config.json"); + +/// Execution witness JSON from zkBoost's test fixture. +const FIXTURE_EXECUTION_WITNESS: &str = include_str!("../tests/fixture/execution_witness.json"); + +// ─── Mock Execution Layer ──────────────────────────────────────────────────── + +struct MockElState { + chain_config: Value, + witness: Value, +} + +/// Mock EL handler that responds to JSON-RPC requests with fixture data. +async fn mock_el_handler(State(state): State>, body: Bytes) -> Json { + let request: Value = serde_json::from_slice(&body).unwrap_or_default(); + let method = request["method"].as_str().unwrap_or(""); + + let result = match method { + "debug_chainConfig" => state.chain_config.clone(), + "debug_executionWitnessByBlockHash" => state.witness.clone(), + _ => Value::Null, + }; + + Json(serde_json::json!({ + "jsonrpc": "2.0", + "result": result, + "id": request["id"], + })) +} + +/// Start a mock execution layer server that serves fixture data. +async fn start_mock_el() -> url::Url { + let chain_config: Value = serde_json::from_str(FIXTURE_CHAIN_CONFIG) + .expect("fixture chain_config.json should be valid JSON"); + let witness: Value = serde_json::from_str(FIXTURE_EXECUTION_WITNESS) + .expect("fixture execution_witness.json should be valid JSON"); + + let state = Arc::new(MockElState { + chain_config, + witness, + }); + + let app = axum::Router::new() + .route("/", post(mock_el_handler)) + .with_state(state); + + let listener = TcpListener::bind((Ipv4Addr::LOCALHOST, 0)) + .await + .expect("failed to bind mock EL"); + let port = listener.local_addr().expect("no local addr").port(); + + tokio::spawn(async move { axum::serve(listener, app).await }); + + format!("http://127.0.0.1:{port}").parse().unwrap() +} + +// ─── Real zkBoost Server ───────────────────────────────────────────────────── + +/// Start the real zkBoost server with mock zkVM backends. +async fn start_zkboost_server( + el_endpoint: url::Url, + zkvm_configs: Vec, +) -> (url::Url, CancellationToken) { + let config = Config { + port: 0, + el_endpoint, + chain_config_path: None, + witness_timeout_secs: 120, + proof_timeout_secs: 120, + proof_cache_size: 128, + witness_cache_size: 128, + zkvm: zkvm_configs, + }; + + let metrics = PrometheusBuilder::new().build_recorder().handle(); + let shutdown = CancellationToken::new(); + let server = zkBoostServer::new(config, metrics) + .await + .expect("failed to create zkBoost server"); + let (addr, _handles) = server + .run(shutdown.clone()) + .await + .expect("failed to start zkBoost server"); + + let endpoint = format!("http://127.0.0.1:{}", addr.port()).parse().unwrap(); + (endpoint, shutdown) +} + +// ─── Test Harness ──────────────────────────────────────────────────────────── + +/// Test harness that manages a real zkBoost server with mock backends. +pub struct ZkboostTestHarness { + /// Base URL of the running zkBoost server. + pub endpoint: url::Url, + /// The proof type configured for the mock backend. + pub proof_type: ProofType, + /// Cancellation token for graceful shutdown. + shutdown: CancellationToken, +} + +impl ZkboostTestHarness { + /// Start a test harness with a single mock zkVM backend. + /// + /// The mock backend uses `EthrexZisk` by default (same as zkBoost's own + /// integration tests) with a configurable proving delay. + pub async fn start(mock_proving_time_ms: u64) -> Self { + Self::start_with_proof_type(ProofType::EthrexZisk, mock_proving_time_ms).await + } + + /// Start a test harness with a specific proof type. + pub async fn start_with_proof_type(proof_type: ProofType, mock_proving_time_ms: u64) -> Self { + let el_endpoint = start_mock_el().await; + + let zkvm_config = zkVMConfig::Mock { + proof_type, + mock_proving_time_ms, + mock_proof_size: 1024, + mock_failure: false, + }; + + let (endpoint, shutdown) = start_zkboost_server(el_endpoint, vec![zkvm_config]).await; + + Self { + endpoint, + proof_type, + shutdown, + } + } + + /// Return the base URL as a string. + pub fn url(&self) -> String { + self.endpoint.to_string().trim_end_matches('/').to_string() + } +} + +impl Drop for ZkboostTestHarness { + fn drop(&mut self) { + self.shutdown.cancel(); + } +} diff --git a/testing/proof_engine_zkboost/tests/fixture/chain_config.json b/testing/proof_engine_zkboost/tests/fixture/chain_config.json new file mode 100644 index 00000000000..82be0f85904 --- /dev/null +++ b/testing/proof_engine_zkboost/tests/fixture/chain_config.json @@ -0,0 +1,45 @@ +{ + "chainId": 3151908, + "homesteadBlock": 0, + "daoForkSupport": false, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "mergeNetsplitBlock": 0, + "shanghaiTime": 0, + "cancunTime": 0, + "pragueTime": 0, + "osakaTime": 0, + "bpo1Time": 0, + "terminalTotalDifficulty": 0, + "terminalTotalDifficultyPassed": true, + "depositContractAddress": "0x00000000219ab540356cbb839cbe05303d7705fa", + "blobSchedule": { + "bpo1": { + "baseFeeUpdateFraction": 8346193, + "max": 15, + "target": 10 + }, + "cancun": { + "baseFeeUpdateFraction": 3338477, + "max": 6, + "target": 3 + }, + "osaka": { + "baseFeeUpdateFraction": 5007716, + "max": 9, + "target": 6 + }, + "prague": { + "baseFeeUpdateFraction": 5007716, + "max": 9, + "target": 6 + } + } +} \ No newline at end of file diff --git a/testing/proof_engine_zkboost/tests/fixture/execution_witness.json b/testing/proof_engine_zkboost/tests/fixture/execution_witness.json new file mode 100644 index 00000000000..65887064f82 --- /dev/null +++ b/testing/proof_engine_zkboost/tests/fixture/execution_witness.json @@ -0,0 +1,50 @@ +{ + "state": [ + "0xf90171a046a9f1217c365990825b7d161fc23cae5688cfb6b2307efe4b732c723e03795880a0c0e0b54cb105bad41b4b925883507463ddfae71c619ba2e41d6d57da2a28effea0793c9db0e252f8f5c79a9d872efc5385ab632a9dc31217637b3509fcf6f0b010a077c059a2b360e9c967686a1302a40994cd63a81aa80a841991d8f3d7379b68eb80a0386a1e942dbe86342b17e2e8b28a259d6db65df8e05f944951a089bb9f3d989fa0315b6e4145b520b88ff5fb638b922671ee1ecbcb65b57b9a4be650ab1fce1d39a066e01acc8a9826bc3d5f5286819fc5883dfa30943331f1e7ff2968bfc57ea2d0a00f7041c0b666de2c820d816b27347738f0e8e2d4d7e1e94e2908b88bc3665a338080a012794aea34d39f220863a2977506ebe5555c2b6488a9469fed918b744f67d6d9a0ace6b45485050162428ffe70f5214d2350ed4890b94322bda3ba63a17342983aa0e20d629ffd2bee3848106f86b98c50a9de755283203bb778c19fa269c8ddb2e38080", + "0xe214a0daadd0f2cf85d5b6a644144de38d5eea115a4546c5efc75c3aee9934f46754a0", + "0xf90131a0e9355b99a40b0e92cc489d34c25f68648461fe0dfcefe3c861f1042ae7cbd522a0766a5ea5b9545a72a463a0fc6151efd1ea0e13f7bd151789dcbff75a1e73cd7180a06e27501c46d61120352d54c49863fcf0eaafcfbffdab9e9e09847d62beef79d88080a0731b30b1211ab24c3e719ad1774d6d450379c926217e248edc5c2a6812e0169480a02978c23bea458e7f47cdc57a9938245e2c763f556847e7e320f7f1bd844127628080a047b4dd8c12aa7dec12a56beb24dff26bd425669991ba54e9ec3225fe6293da24a039f39136138de3527d38db1831c98f8897eecd0a75a77129f6386184f28c779ba0fa149b424c332acc1c6967d908eab8e4922f27e00499ed6a1aca3b9975d87ef580a09f297d5e53d34bc2096cce66773c42bf5b177714e3bb9f180521045b34f7127d80", + "0xf90211a0fcf8a530a63eb8575eb9a70c95332fc1047b567be3d1da03a21c9917d92b14a6a006d47616df479b46b302f2a8b7ed03cb537f6cf7c551c15421c65db4e00fa97fa038e34f9e0e4830343ba24f5fcf0eba28d79cb86397adfb16a0169ee7f0180036a004f7ae295715850712c9dc7f1b9f973797b89ef0f46991203ac789210330a517a0dd3420839babaee761e7eaa38ad5f596b1a9b8716e7e9b9261949a964a5a7d61a0eea64374052ac460957bbc34a071cb8b25dcdf44d96785a55b34242914c83f9fa008763a217b516bcfeadc7f6849e812b392643f50a1d25f002ceec6c2ca0adcafa083c6979e463c02818ffeadaeeb8abc9f2f51e767fb9151a7fc89989eb40b57aca0cbcdc1d226a540c50cb1e615e7af99f171d4365b45734940e22d47ec4aa23a14a0be88e4724326382a8b56e2328eeef0ad51f18d5bae0e84296afe14c4028c4af9a018e0f191e57d4186717e0f3c9379d2438cec0babd12d3903a4ad560f017331bfa01796617427e67ed10cdf8a72b02689a700ba71eb93186a1b120c9ad0b0e56eaea0ad0bb86b47186c04223e85a9c33dd1c87dd6e5c17f753f4fd0a56772d8a78399a065fb94808e31ca248fb2d9de329b81735b22f75d109f389678c9965418bf1f16a06a2b50671c3f299bfd4b6cf43d6e5d6aafd4d3677c38a8af52a0cd7680de2b94a037ff00fbe2105bce0e6ed9ea80a1d67b8a476b1ff3d177ac9597a53241e47aa780", + "0xf901b1a027db720cbe694541a361e08b5450894ddce39b11113fe952080ad5f54ada6f4a80a0d2e57f615a47508c6e60935353428b9fc1cc75677a3eb8f5f73d61dd0aaff5f5a0ca976997ddaf06f18992f6207e4f6a05979d07acead96568058789017cc6d06ba04d78166b48044fdc28ed22d2fd39c8df6f8aaa04cb71d3a17286856f6893ff8380a0fc3b71c33e2e6b77c5e494c1db7fdbb447473f003daf378c7a63ba9bf3f0049da0a9c8e462df1860757a204a01fccc87b873837b0a32cbcc645fb663f3eb12a705a07b8e7a21c1178d28074f157b50fca85ee25c12568ff8e9706dcbcdacb77bf854a0973274526811393ea0bf4811ca9077531db00d06b86237a2ecd683f55ba4bcb0a091d9c76bfbc066e84f0b415c737ab8c477498701d920526db41690050cfade99a06aa67101d011d1c22fe739ef83b04b5214a3e2f8e1a2625d8bfdb116b447e86fa0244e4282dfec33c9bb765162ceee4f2e6390033a94b620d50a2fc6943ebd82fca0f3b039a4f32349e85c782d1164c1890e5bf16badc9ee4cf827db6afd2229dde6a0d9240a9d2d5851d05a97ff3305334dfdb0101e1e321fc279d2bb3cad6afa8fc88080", + "0xf90171a06664dd6bcbb08b83f84324db8cbaf2ceb221e49e66971369dd2257e947a3b13d80a0f4ec365c37413b5f9e7d38c3c6409922fa2a593757ef6176b7291ede5ae2b2d780a0614ab7fe84bea831a68e5e39c6e2d339db432b94dcd29ac75de694cfc6641496a036750a0cdda09ef53dc4a7510eb69e87fbafb1739f51d52c60214b7e0d276ddda04eb05cc2337a47e5d315fc9e2972f88b2282caecf7b79cb486ccf4e64ddf54cd80a0044dadb95a10fad8f922e38449d128807ed6c4b3e6af52d0faa865be8cb8847480a0d53e862eebd81f90452eada8434dfdd03a7ef3d06d6db3e68cbc7d05dff81ec0a0eb47388255e7ca68b42fa56180019c61e2dd301bfe20226d6a74d795f6b016a6a0c522d5defc176e5fa5fc0f16d95ad335f25668067c2c9a55db7d901fd8ac04c6a06c457c05a87c557f84f6d98cfb3754a20c1ded0550ef405433d3514f332c77df80a0d5758f21c6c63a45c81d16ecca352c41af637c1729f8866900efcf731dc10db280", + "0xf901518080a0f1a60e8881cfcb2dc50ba58c326ccc9a6da8287c1e5f56d2017563be700058c4a0616362468a3391221e3782da42e2d6fb8ea41da6bdd2d679e20bf0375c06158680a0ed2fba131fadeadeb1082f565fff16ceb008f693056e3140204716c0739cf1e08080a0cfcecd85b5b3b2b03c196589d3d3b9bcd0ddfc01f000cde9fe3cab41dc6a0a16a0b2a5565ce39d8b7fabb242f087f05b7273aef44094f4166046cddd978751c4bea06234ead07239df2c23d50d21d2e045332bb3e2fb0a402aae5780b823e7d5308680a0ebe51b14fea6aaa5c097f2506874e990813c36cd31399ee3d72666de2dde3fcca051eac0e6e8747ed945c8119613a8359cb76220e714610cf783388ce900153208a0e16e6773b65ff27c428b07407a2d2e479712166515a4a43ecc3c4444d77d4f34a0105bafd3bfbb01dd5f28afe06b314ccf6d5f1bddd1e2135dcc010cb3aedd1d4380", + "0xf869a02086c581c7d7b44eecbb92fd9e5867945ec1acdc0ea5bbabda21d17dddf06473b846f8440180a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a00345a365d2f4c5975b9f1599abe0a2ee76b7a3a731bc68781bd04c84e4858f50", + "0xf8518080a0a63eaef203909ce313085e71f47b6855ccd4fffe444fba1ec1efdab787203351808080a079af4179331361fad570767001c2b705c100b691c849c2bda966d070709c4bf880808080808080808080", + "0xf8f18080a0936cc4aad97b5838a8bc0dfa95a5ad0a0fe2c4681ffe209a0228098aad0c619080a0985a93e071c1474beffb3b3feefcf343ea7e4e002d8c6c7675de86cc9ebc27c0a022652c87d9810e05a254c5942729b67343e1069440f4bee452c8c7bb88d193e8a0975e4f968cf4537118047c31c634177fbec68949fd40003601aab2ff822bcc5580a04b66d0874dc47dba19ba179183342befb18cd80e6d4f85516123426f86e0d7afa081ce69bcb065377bda0ac34c8b05086357838440690dbcccba20f3e8cab1b88680808080a0dc281265feb5bcd82bc162628f62f92dc649b77c80a3ac5d14bdf3d367c495238080", + "0xf90211a040da929897ecf8fb0ecdda44d1c6aa37c7b5d19d0a6f1255c3aaac43b77f2d4ca05dbd3e7becc744398948292f4810e753b166b91cc1a763b214b24718e2bc432aa0d8222bc84a44e1d1d03bdae69910ff3b244c815fa99ef1a9aa6bb568cad6b35ca050e569e8e5e77ab130db2842f7598cee50d0b42cc2504a9df287175c307b23c3a01fd4c856668574229bec8b57377eb317351e0695f8c7c8239aa1016a73001b16a0e0cf581054d8c2bab1359dddde660c659c0e5d70ca2c03e667419d1bc9e45f05a0ebe2e281fc5af1d9bc149c1bf210d264f9b283a2c1760abf0ad5f48e08499ac8a08be4370ed1686f92ab5478848e85a1abab751c9c80e7f8d68daa8c3d8232356aa02fb840ef5765a4ceb26d610badea7ea799c28544f3b329b986c400ff272967d2a0e8393cb9738eea3a5031110dd9c2043e360267072374de576cdc9bc4fa015d39a046ff1faf6df6476a5a4d8f6ba32c8f38582b3a7bd4e12893c1712894ea39c017a008afbb10c9064b061ba3a17cfaf8c083b376a402c60704bc0afaa7a55d27f5c9a0582d0c27b5152cb3f3247a8752888739769fb2b6e3f7842298bc26b616773b88a0db4a8cc49ce3a0fefe00143359d4f0fa86026559ea073bb061b7aacd217ac037a0f31e8aa4efb4024c99d873f31485f1c496f484c345b1ec664f4ba723499e03f8a0672f74dceafee2ee98a97fb19f4afdb991ba8c1ee019438f15b809da4b427b5a80", + "0xf90211a04efbc90ce3b15216a559cdb50fb788b0af3916ef1777a585e7093e27cf4bc16da0047b79502e6ba90c8c1b4863e8380b3e6cc23da1c208f8e39c348a936af31ea5a03db8dd4c19ae2b67a736b757995cb7b57ed55ccdd34fb0ebe979a2dee0c66339a0471db2263571236146b863a32d0d1abe6e21a984998b2d7c0376b4243dca42d2a024e8f92fe5bdde58f4954f534b6f91659a8c0f889abdbf7eec9ab77a26478072a0f8afbd19dafaf176fc835595483ea85f554b1b840e8709b3c2a07715411ecb08a034cb0ac81dbce62a5c9855fe0311bd6827fecbb9aa741a7c8e8b7427f73b8716a0a2a9a28a4324e79e625b104a232620f515ff4a3428c78257bbec3621343ec11aa0b030f3e6c8e7b40bc5bea3da238bcf7546c521b7d6b72dbd98e3fbffb0d604ada0de4bf15b56b7a96707c9c6072d1f413322e563f04ec3c3f9fcf7719b073ed285a0c641efacb85f02a412724d2ba1a107c767d66f5258ae33c9c64bd1bcc4a64540a02e14db6c4900768cf91528d8e1b746f9ab032f277077459f5cc79d16b6be0dc3a006c495fb6961358f4bde6c279838bbc557f9927391b42070bd44b30ab824430fa07415c94beb78124e62f7f63ad7a64076cf7b004809565b8a63dedcacc1434ac5a092712479fda69c5e14b2085716b5e5ab229494f395740b941280432b831ed221a0176a9fce68e6fd07098e5bd0e742a828173ba4a7feed5b6455794caad04462a880", + "0xf869a0209d57be05dd69371c4dd2e871bce6e9f4124236825bb612ee18a45e5675be51b846f8440180a0a247228347f628c6463d5f2932202f269bcabe3dbc08a56392c2dc88e7e04249a06e49e66782037c0555897870e29fa5e552daf4719552131a0abce779daec0a5d", + "0xf90211a0e66e395bd17cd8e5cea8b1c1aad2bb861eeb8a2bb096ea6eddeb34422497bdaaa04c03fe869c8cde143d01ab6bfc09226ea42d9ad99a53263f69716a7186c0bf0aa077e46fe2af85fe2ea2de398481c148651e7ee82f27176160eb18b3a802661798a0c52146e012e5094a13d00fc9dbe596a0639c59e2587b7ac55038d3e52d4f4936a044cb808faa3a8e993889588681b030c9a97babe7e15fdb71be950e9a88a7e402a03deea8359c1b0971aa68d701e9cd18016134f5310b0e4a7d9833247db460a1f1a02cedc09ae6f35f5e75e4a65cee5fc753b113311d912b25fc289a872885415a8ca080b9f7d63a5ea0d7b20ace0018da20977a795543c0ab2d4035b60885e5d60828a0b8f2aa8b6816e39e58f9193d23f9573f75e4c0dea753b325da153a6fbdbaabd2a05126fa3c18c632812536718c92ed0747e4a610c245ea1234acbca7533f1506f2a014116df18532e1f44477d3cf371240e82d2cf7c02542d6da6ab56861626a0c24a0ad7eb60b7242bb4abab99f42056bcc64ae2de2b6182550cb6864c404b059fb3fa04e222b8402af16d6151aba0426b59a029db34ba31592f254ba8d6f64e59e07eba0eae43e73dfb5784c88f6424e4da4ac7aae2aa29f09cd528aab89a4003e3a4da7a023fc581b6065c3d34578d7119f3385df16ae9a24aab09a98877d36fd844f2933a0a4cb53144ee264a09401aabbebb43c80264ebfce063a70c28595e1b0c52fdd9c80", + "0xf90211a0f53fd45e8a28bfc7c92543aac0f242249bd15dc550b8d1d43defabfe1ff4622ba072d67f642876a04c9733ce298d4bb2fdc2eb041b6760ed0a3be006785b0705e0a0a86c39e9a32652492ee5240d1715c6a63537351d350754b62952760d8e1f944ba0e79513901b1f313c826300a31dff17f6adf9e2aeb895f730dbb93f0a96a86d9fa031c4646963f14566afb0e50a6c400d69c834c3b1fdb3909677856cbb576db4e5a09cdcc334e9d1c6451e5f5230efdd07ac62f48223d3a71b7082d1c9f3faee6af1a0f5ea37b375d1f04089104149dd9204aa0ff3c90167f7aca7da201905594300e4a076972cc63f4fabea810e87083ec1899b687d8748d26fe16fd4b6a13ae3e303f1a04ff31ed8ee553088b2e578f36bf3ed50d5cbd58611261be37633294dc61acca9a028b05d809456d53fc06c9b102d216cff567a7aca7c9d1cb4cdca67965f0ef4cda01556f03106eeb9fc5a473e8f7f042e57d827b78b76a5f7a8f5b187f8d897515da0f762ae6fe61a92321fb8d528b2f5f4b1b97a94ebf2d5ec0899e8f703fba9790da036affb194c9227b46dece3bc3e1e5ef56403db6c8e34fe1b8bb3ae197158b5d6a0db08702017c418fb841716b9c2454676fa632f607d5b261f55c7434dbc69c4d6a0d4da88e24a26de50f4f0d35a348e12da471480c6e612dacccbc594a61f58d74ca0aaba74a722fd0645b8b7a8886d0e891e04c4e57914480568f5334d7514391f6680", + "0x80", + "0xf8429f37d5f9b51ca71bda3c02250aa5ededabaa712e18e5f1714fde16280d94a4a3a1a0d5848dbf659bcc407318ddcac1ad62fb7b58c53df808ed0a560c8d4a94ac3e6f", + "0xf8689f3aea581b220579a2b99819299dd32c7c28a420018ecb0bde93af007ad89a31b846f8440180a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a078c6cb5202685228bbcbfb992b1c4e116c7ec5ef11e25b8e92716cfc628ddd60", + "0xf869a020d65eaa92c6bc4c13a5ec45527f0c18ea8932588728769ec7aecfe6d9f32e42b846f8440180a0a52d06d7443bb469a8ddfecb744e9750fa7284a237b31f7168562123b84c3547a0f57acd40259872606d76197ef052f3d35588dadf919ee1f0e3cb9b62d3f4b02c", + "0xf90211a065cb9654d83c2c587ff35d995153e55908ccc8d12f99cec6f0fca2174d0d4887a072c2cce9f8770d341a4cb7c7cdc53d75d6308b55e9f991bc8ba67b29434b61dfa0f2b29241a79b4cf67be8c19e0fc49894bbc908bdfaa864f313e640a9656271cca0a4f08ea6851799ebadce763bdb22c8511a37106f2b1f1a2e1da77743588a4751a0e473037e78e7f6b59faf7c818971524734244419165e3b52fd6747e4acbb3235a09f871e9dc9ad7e80a33f12dfb19ec657a944edd24ecd975367a4675d7a2760a0a0f3e41d9e7b89a679eba0c449b24e2f6b074dd4e65abc10fee304b97893689673a0ba956ceecf3546a048edbdb0e93c6bd5f9437ee2bc2eb547d95cad86e16e791ba0be49e1efa56a6325758e40aa25985c3f71f2d20888daa9efd8e2e9cd0d70826ca05d4d0edd678514b0b449d8689f7971252fd7b86378a102395d5ee769d709c2a1a0fcacd3004b2d9f8c601c667041baea5c7ad53bde430303ab3d2f5c765804cd82a0b7195c41d29afbb5b45413885333d6a19b0679d3a92a9f1198ab04689ac0518ca06675b419aca5f5ab938080fd8245ae9c388c144521ad7d4a57e8f36212e218a2a0ddfccdcd7960367614d844e7fca5cb92573ace5ca42ad9381dfc2c69e7f0f890a04651f6d80d233d28e5cda8940d11319698f604ee414041a9374a5ee3d7305b1fa0da847328820b77fcc53e716178f77359797b68b90e53117251c9115ee6fc428880" + ], + "codes": [ + "0x3373fffffffffffffffffffffffffffffffffffffffe1460cb5760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff146101f457600182026001905f5b5f82111560685781019083028483029004916001019190604d565b909390049250505036603814608857366101f457346101f4575f5260205ff35b34106101f457600154600101600155600354806003026004013381556001015f35815560010160203590553360601b5f5260385f601437604c5fa0600101600355005b6003546002548082038060101160df575060105b5f5b8181146101835782810160030260040181604c02815460601b8152601401816001015481526020019060020154807fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252906010019060401c908160381c81600701538160301c81600601538160281c81600501538160201c81600401538160181c81600301538160101c81600201538160081c81600101535360010160e1565b910180921461019557906002556101a0565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff14156101cd57505f5b6001546002828201116101e25750505f6101e8565b01600290035b5f555f600155604c025ff35b5f5ffd", + "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500", + "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500", + "0x3373fffffffffffffffffffffffffffffffffffffffe1460d35760115f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1461019a57600182026001905f5b5f82111560685781019083028483029004916001019190604d565b9093900492505050366060146088573661019a573461019a575f5260205ff35b341061019a57600154600101600155600354806004026004013381556001015f358155600101602035815560010160403590553360601b5f5260605f60143760745fa0600101600355005b6003546002548082038060021160e7575060025b5f5b8181146101295782810160040260040181607402815460601b815260140181600101548152602001816002015481526020019060030154905260010160e9565b910180921461013b5790600255610146565b90505f6002555f6003555b5f54807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff141561017357505f5b6001546001828201116101885750505f61018e565b01600190035b5f555f6001556074025ff35b5f5ffd" + ], + "keys": [ + "0x000f3df6d732807ef1319fb7b8bb8522d0beac02", + "0x0000000000000000000000000000000000000000000000000000000000003808", + "0x0000000000000000000000000000000000000000000000000000000000001809", + "0x00000961ef480eb55e80d19ad83579a64c007002", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000003", + "0x0000f90827f1c53a10cb7a02335b175320002935", + "0x00000000000000000000000000000000000000000000000000000000000004af", + "0x0000bbddc7ce488642fb579f8b00f3a590007251", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x0000000000000000000000000000000000000000000000000000000000000003" + ], + "headers": [ + "0xf9026fa084a5904e068368b6581e5afa05f96e3912068ab8ceee08ca76bdb9719bd1c090a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948943545177806ed17b9f23f0a21ee5948ecaa776a03bb7c2e1c292bc41a27064b9160eb131723e6c345851ee0c386f09115da5fae6a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808204af8402255100808469aeca6d92726574682f76312e31302e312f6c696e7578a0f2940bf2aad7139113b79fcd654cb699530e993a33dc05a31ebfcf017643b55888000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a037afc7de70547b71e752341e78303f688e6f5b87e47367b747947d5d34af77a0a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + ] +} \ No newline at end of file diff --git a/testing/proof_engine_zkboost/tests/fixture/new_payload_request.ssz b/testing/proof_engine_zkboost/tests/fixture/new_payload_request.ssz new file mode 100644 index 00000000000..6ffe35cc644 Binary files /dev/null and b/testing/proof_engine_zkboost/tests/fixture/new_payload_request.ssz differ diff --git a/testing/simulator/Cargo.toml b/testing/simulator/Cargo.toml index f916585ac86..930025ea434 100644 --- a/testing/simulator/Cargo.toml +++ b/testing/simulator/Cargo.toml @@ -3,6 +3,9 @@ name = "simulator" version = "0.2.0" authors = ["Paul Hauner "] edition = { workspace = true } + +[features] +test-utils = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] @@ -13,8 +16,8 @@ environment = { workspace = true, features = ["test-utils"] } eth2 = { workspace = true, features = ["events"] } execution_layer = { workspace = true } futures = { workspace = true } -lighthouse_network = { workspace = true } kzg = { workspace = true } +lighthouse_network = { workspace = true } logging = { workspace = true } node_test_rig = { path = "../node_test_rig" } parking_lot = { workspace = true } @@ -29,6 +32,3 @@ tracing-subscriber = { workspace = true } typenum = { workspace = true } types = { workspace = true } validator_http_api = { workspace = true } - -[features] -test-utils = [] diff --git a/testing/simulator/src/basic_sim.rs b/testing/simulator/src/basic_sim.rs index b5ade95c241..7666c5e6e99 100644 --- a/testing/simulator/src/basic_sim.rs +++ b/testing/simulator/src/basic_sim.rs @@ -27,7 +27,7 @@ use tracing::Level; use types::{Epoch, EthSpec, MinimalEthSpec}; const END_EPOCH: u64 = 16; -const GENESIS_DELAY: u64 = 38; +const GENESIS_DELAY: u64 = 45; const ALTAIR_FORK_EPOCH: u64 = 0; const BELLATRIX_FORK_EPOCH: u64 = 0; const CAPELLA_FORK_EPOCH: u64 = 0; @@ -372,7 +372,7 @@ pub fn run_basic_sim(matches: &ArgMatches) -> Result<(), String> { network_1.add_beacon_node_with_delay( beacon_config.clone(), mock_execution_config.clone(), - END_EPOCH - 1, + END_EPOCH - 3, slot_duration, slots_per_epoch ), diff --git a/testing/simulator/src/fallback_sim.rs b/testing/simulator/src/fallback_sim.rs index 7d2f68658d3..d80d344601a 100644 --- a/testing/simulator/src/fallback_sim.rs +++ b/testing/simulator/src/fallback_sim.rs @@ -22,7 +22,7 @@ use tracing_subscriber::prelude::*; use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt}; use types::{Epoch, EthSpec, MinimalEthSpec}; const END_EPOCH: u64 = 16; -const GENESIS_DELAY: u64 = 38; +const GENESIS_DELAY: u64 = 45; const ALTAIR_FORK_EPOCH: u64 = 0; const BELLATRIX_FORK_EPOCH: u64 = 0; const CAPELLA_FORK_EPOCH: u64 = 1; diff --git a/testing/simulator/src/local_network.rs b/testing/simulator/src/local_network.rs index e4015d127fd..c7d027d5ae2 100644 --- a/testing/simulator/src/local_network.rs +++ b/testing/simulator/src/local_network.rs @@ -4,12 +4,11 @@ use kzg::trusted_setup::get_trusted_setup; use lighthouse_network::types::Enr; use node_test_rig::{ ClientConfig, ClientGenesis, LocalBeaconNode, LocalExecutionNode, LocalValidatorClient, - MockExecutionConfig, MockServerConfig, ValidatorConfig, ValidatorFiles, + MockExecutionConfig, ValidatorConfig, ValidatorFiles, environment::RuntimeContext, eth2::{BeaconNodeHttpClient, types::StateId}, testing_client_config, }; -use node_test_rig::{LocalProofEngine, MockProofEngineConfig}; use parking_lot::RwLock; use sensitive_url::SensitiveUrl; use std::{ @@ -23,12 +22,6 @@ use tempfile::tempdir; use types::{ChainSpec, Epoch, EthSpec}; use validator_http_api::{Config as ValidatorHttpConfig, PK_FILENAME}; -const BOOTNODE_PORT: u16 = 42424; -const QUIC_PORT: u16 = 43424; - -pub const EXECUTION_PORT: u16 = 4000; -pub const PROOF_PORT: u16 = 6000; - pub const TERMINAL_BLOCK: u64 = 0; #[derive(Debug, Copy, Clone)] @@ -120,15 +113,7 @@ fn default_client_config(network_params: LocalNetworkParams, genesis_time: u64) beacon_config.chain.enable_light_client_server = true; beacon_config.chain.optimistic_finalized_sync = false; beacon_config.trusted_setup = get_trusted_setup(); - beacon_config.chain.node_custody_type = NodeCustodyType::Supernode; - let el_config = execution_layer::Config { - execution_endpoint: Some( - SensitiveUrl::parse(&format!("http://localhost:{}", EXECUTION_PORT)).unwrap(), - ), - ..Default::default() - }; - beacon_config.execution_layer = Some(el_config); beacon_config } @@ -136,13 +121,7 @@ fn default_mock_execution_config( spec: &ChainSpec, genesis_time: u64, ) -> MockExecutionConfig { - let mut mock_execution_config = MockExecutionConfig { - server_config: MockServerConfig { - listen_port: EXECUTION_PORT, - ..Default::default() - }, - ..Default::default() - }; + let mut mock_execution_config = MockExecutionConfig::default(); if let Some(capella_fork_epoch) = spec.capella_fork_epoch { mock_execution_config.shanghai_time = Some( @@ -177,7 +156,6 @@ pub struct Inner { pub proposer_nodes: RwLock>>, pub validator_clients: RwLock>>, pub execution_nodes: RwLock>>, - pub proof_engines: RwLock>>, } /// Represents a set of interconnected `LocalBeaconNode` and `LocalValidatorClient`. @@ -235,7 +213,6 @@ impl LocalNetwork { proposer_nodes: RwLock::new(vec![]), execution_nodes: RwLock::new(vec![]), validator_clients: RwLock::new(vec![]), - proof_engines: RwLock::new(vec![]), }), }; @@ -263,11 +240,6 @@ impl LocalNetwork { self.proposer_nodes.read().len() } - /// Returns the number of proof engines in the network. - pub fn proof_engine_count(&self) -> usize { - self.proof_engines.read().len() - } - /// Returns the number of validator clients in the network. /// /// Note: does not count nodes that are external to this `LocalNetwork` that may have connected @@ -281,15 +253,6 @@ impl LocalNetwork { mut beacon_config: ClientConfig, mock_execution_config: MockExecutionConfig, ) -> Result<(LocalBeaconNode, LocalExecutionNode), String> { - beacon_config.network.set_ipv4_listening_address( - std::net::Ipv4Addr::UNSPECIFIED, - BOOTNODE_PORT, - BOOTNODE_PORT, - QUIC_PORT, - ); - - beacon_config.network.enr_udp4_port = Some(BOOTNODE_PORT.try_into().expect("non zero")); - beacon_config.network.enr_tcp4_port = Some(BOOTNODE_PORT.try_into().expect("non zero")); beacon_config.network.discv5_config.table_filter = |_| true; // The boot node is a full data-availability node and should custody all columns from @@ -318,35 +281,13 @@ impl LocalNetwork { async fn construct_beacon_node( &self, mut beacon_config: ClientConfig, - mut mock_execution_config: MockExecutionConfig, + mock_execution_config: MockExecutionConfig, node_type: NodeType, - ) -> Result< - ( - LocalBeaconNode, - Option>, - Option>, - ), - String, - > { - let count = (self.beacon_node_count() + self.proposer_node_count()) as u16; - - // Set config. - let libp2p_tcp_port = BOOTNODE_PORT + count; - let discv5_port = BOOTNODE_PORT + count; - beacon_config.network.set_ipv4_listening_address( - std::net::Ipv4Addr::UNSPECIFIED, - libp2p_tcp_port, - discv5_port, - QUIC_PORT + count, - ); - beacon_config.network.enr_udp4_port = Some(discv5_port.try_into().unwrap()); - beacon_config.network.enr_tcp4_port = Some(libp2p_tcp_port.try_into().unwrap()); + ) -> Result<(LocalBeaconNode, Option>), String> { beacon_config.network.discv5_config.table_filter = |_| true; beacon_config.network.proposer_only = node_type.is_proposer(); let execution_node = if node_type.requires_execution_node() { - // Construct execution node. - mock_execution_config.server_config.listen_port = EXECUTION_PORT + count; let execution_node = LocalExecutionNode::new(self.context.clone(), mock_execution_config); @@ -365,24 +306,24 @@ impl LocalNetwork { None }; - let proof_node = if node_type.requires_proof_node() { - let mut config = MockProofEngineConfig::default(); - config.server_config.listen_port = PROOF_PORT + self.proof_engine_count() as u16; - let proof_engine = LocalProofEngine::new(self.context.clone(), config).await; - if let Some(exeuction_layer) = beacon_config.execution_layer.as_mut() { - exeuction_layer.proof_engine_endpoint = Some(proof_engine.server.url().clone()); + if node_type.requires_proof_node() { + // Subscribe to the execution_proof gossip topic and wire up the mock proof engine. + beacon_config.network.enable_execution_proof = true; + // Index = current length of beacon_nodes (this node's future position in the list). + let bn_idx = self.beacon_nodes.read().len(); + execution_layer::test_utils::register_mock_proof_engine(bn_idx, 0); + let mock_url = + SensitiveUrl::parse(&execution_layer::test_utils::mock_proof_engine_url(bn_idx)) + .expect("mock URL is valid"); + if let Some(el_config) = beacon_config.execution_layer.as_mut() { + el_config.proof_engine_endpoint = Some(mock_url); } else { beacon_config.execution_layer = Some(execution_layer::Config { - proof_engine_endpoint: Some(proof_engine.server.url().clone()), + proof_engine_endpoint: Some(mock_url), ..Default::default() }); } - // Subscribe to the execution_proof gossip topic for nodes with a proof engine. - beacon_config.network.enable_execution_proof = true; - Some(proof_engine) - } else { - None - }; + } if node_type.is_proof_verifier() { beacon_config.chain.optimistic_finalized_sync = true; @@ -394,14 +335,7 @@ impl LocalNetwork { // Construct beacon node using the config, let beacon_node = LocalBeaconNode::production(self.context.clone(), beacon_config).await?; - Ok((beacon_node, execution_node, proof_node)) - } - - pub fn boot_node_enr(&self) -> Option { - self.beacon_nodes - .read() - .first() - .and_then(|bn| bn.client.enr()) + Ok((beacon_node, execution_node)) } pub fn proof_generator_enr(&self) -> Option { @@ -411,6 +345,29 @@ impl LocalNetwork { .and_then(|bn| bn.client.enr()) } + /// Returns the boot node's ENR once it has a valid (non-zero) TCP port, or an error if + /// the port isn't populated within 10 seconds. + async fn boot_node_enr(&self) -> Result, String> { + // If there are no beacon nodes yet, the network hasn't started — return None immediately. + if self.beacon_nodes.read().is_empty() { + return Ok(None); + } + + for _ in 0..100 { + if let Some(enr) = self + .beacon_nodes + .read() + .first() + .and_then(|bn| bn.client.enr()) + .filter(|e| e.tcp4().is_some_and(|p| p != 0)) + { + return Ok(Some(enr)); + } + tokio::time::sleep(Duration::from_millis(100)).await; + } + Err("Boot node ENR did not get a valid TCP port within 10 seconds".to_string()) + } + /// Adds a beacon node to the network, connecting to the 0'th beacon node via ENR. pub async fn add_beacon_node( &self, @@ -418,27 +375,24 @@ impl LocalNetwork { mock_execution_config: MockExecutionConfig, node_type: NodeType, ) -> Result<(), String> { - let (beacon_node, execution_node, proof_node) = - if let Some(boot_node) = self.boot_node_enr() { - // Network already exists. We construct a new node. - beacon_config.network.boot_nodes_enr.push(boot_node); - self.construct_beacon_node(beacon_config, mock_execution_config, node_type) - .await? - } else { - // Network does not exist. We construct a boot node. - let (bn, en) = self - .construct_boot_node(beacon_config, mock_execution_config) - .await?; - (bn, Some(en), None) - }; + let (beacon_node, execution_node) = if let Some(boot_node) = self.boot_node_enr().await? { + // Network already exists. The boot node ENR has a valid TCP port; use it to + // bootstrap the new node. + beacon_config.network.boot_nodes_enr.push(boot_node); + self.construct_beacon_node(beacon_config, mock_execution_config, node_type) + .await? + } else { + // Network does not exist. We construct a boot node. + let (bn, en) = self + .construct_boot_node(beacon_config, mock_execution_config) + .await?; + (bn, Some(en)) + }; // Add nodes to the network. if let Some(execution_node) = execution_node { self.execution_nodes.write().push(execution_node); } - if let Some(proof_node) = proof_node { - self.proof_engines.write().push(proof_node); - } match node_type { NodeType::Proposer => self.proposer_nodes.write().push(beacon_node), _ => self.beacon_nodes.write().push(beacon_node), @@ -474,6 +428,7 @@ impl LocalNetwork { validator_files: ValidatorFiles, node_type: NodeType, ) -> Result<(), String> { + let beacon_node_idx = beacon_node; let context = self.context.clone(); let socket_addr = { let read_lock = self.beacon_nodes.read(); @@ -502,17 +457,7 @@ impl LocalNetwork { .unwrap(); validator_config.beacon_nodes = vec![beacon_node]; - // If this is a proof generator node, we will set the proof engine endpoint to the first proof engine in the network. if node_type.is_proof_generator() { - let proof_engine_url = self - .proof_engines - .read() - .first() - .map(|proof_engine| proof_engine.server.url()) - // use expect here to fail fast if the network has been instantiated incorrectly - // even though we wrap in Some(..) again in the line below. - .expect("Proof generator node must exist if validator is a proof generator"); - validator_config.proof_engine_endpoint = Some(proof_engine_url); let token_path = tempdir().unwrap().path().join(PK_FILENAME); validator_config.http_api = ValidatorHttpConfig { enabled: true, @@ -524,7 +469,14 @@ impl LocalNetwork { http_token_path: token_path, bn_long_timeouts: false, }; - }; + // Wire the VC's proof service to the same mock registered for this beacon node index. + validator_config.proof_engine_endpoint = Some( + SensitiveUrl::parse(&execution_layer::test_utils::mock_proof_engine_url( + beacon_node_idx, + )) + .expect("mock URL is valid"), + ); + } // If we have a proposer node established, use it. if let Some(proposer_socket_addr) = proposer_socket_addr { @@ -547,18 +499,6 @@ impl LocalNetwork { ) .await?; - // Set the callback url on the proof engine if this is a proof generator node. - if node_type.is_proof_generator() { - let validator_http_client = validator_client - .http_client()? - .expect("HTTP client should be available for proof generator node"); - self.proof_engines - .write() - .first_mut() - .unwrap() - .set_validator_client(validator_http_client); - } - self.validator_clients.write().push(validator_client); Ok(()) } @@ -624,6 +564,22 @@ impl LocalNetwork { .map(|body| body.unwrap().data.finalized.epoch) } + /// Subscribe to method-invocation events from the proof generator node's mock proof client. + /// + /// Searches all beacon nodes for the first one that exposes a mock client event stream + /// (i.e. a `ProofGenerator` node configured with the mock proof engine URL). + pub fn proof_generator_subscribe_client_events( + &self, + ) -> Option> + { + self.beacon_nodes.read().iter().find_map(|bn| { + bn.client + .beacon_chain() + .and_then(|chain| chain.execution_layer.as_ref().cloned()) + .and_then(|el| el.subscribe_proof_node_client_events()) + }) + } + pub async fn duration_to_genesis(&self) -> Result { let nodes = self.remote_nodes().expect("Failed to get remote nodes"); let bootnode = nodes.first().expect("Should contain bootnode"); diff --git a/testing/simulator/src/test_utils/mod.rs b/testing/simulator/src/test_utils/mod.rs index d674a61386f..b64e9fcffa6 100644 --- a/testing/simulator/src/test_utils/mod.rs +++ b/testing/simulator/src/test_utils/mod.rs @@ -8,6 +8,7 @@ pub use crate::basic_sim::SUGGESTED_FEE_RECIPIENT; pub use crate::local_network::{LocalNetwork, LocalNetworkParams, NodeType}; pub use environment::LoggerConfig; pub use environment::test_utils::TestEnvironment; +pub use execution_layer::test_utils::MockClientEvent; pub use logging::build_workspace_filter; pub use node_test_rig::ApiTopic; pub use node_test_rig::{ diff --git a/validator_client/initialized_validators/Cargo.toml b/validator_client/initialized_validators/Cargo.toml index 8b2ae62aea3..0ce8696d7b7 100644 --- a/validator_client/initialized_validators/Cargo.toml +++ b/validator_client/initialized_validators/Cargo.toml @@ -14,7 +14,7 @@ lockfile = { workspace = true } metrics = { workspace = true } parking_lot = { workspace = true } rand = { workspace = true } -reqwest = { workspace = true } +reqwest = { workspace = true, features = ["native-tls-vendored"] } serde = { workspace = true } serde_json = { workspace = true } signing_method = { workspace = true } diff --git a/validator_client/src/lib.rs b/validator_client/src/lib.rs index 428476dcca9..e3f80391665 100644 --- a/validator_client/src/lib.rs +++ b/validator_client/src/lib.rs @@ -537,10 +537,22 @@ impl ProductionValidatorClient { // Create proof service (EIP-8025) if proof engine endpoint is configured let proof_service = config.proof_engine_endpoint.as_ref().map(|endpoint| { info!(endpoint = %endpoint, "Initializing proof engine client"); - let proof_engine_client = Arc::new(execution_layer::eip8025::HttpProofEngine::new( - endpoint.clone(), - None, // No custom timeout - )); + let url_str = endpoint.expose_full(); + let proof_engine_client = Arc::new( + if let Some(idx) = execution_layer::test_utils::parse_mock_index(url_str.as_str()) { + let mock = execution_layer::test_utils::get_mock_proof_engine(idx) + .unwrap_or_else(|| { + debug!( + idx, + "No pre-registered mock; creating MockProofNodeClient on the fly" + ); + execution_layer::test_utils::register_mock_proof_engine(idx, 0) + }); + execution_layer::eip8025::HttpProofEngine::with_proof_node((*mock).clone()) + } else { + execution_layer::eip8025::HttpProofEngine::new(endpoint.clone(), None) + }, + ); Arc::new(ProofService::new( validator_store.clone(), diff --git a/validator_client/validator_services/Cargo.toml b/validator_client/validator_services/Cargo.toml index 93ef421ea85..59eeb058569 100644 --- a/validator_client/validator_services/Cargo.toml +++ b/validator_client/validator_services/Cargo.toml @@ -12,12 +12,12 @@ eth2 = { workspace = true, features = ["events"] } execution_layer = { workspace = true } futures = { workspace = true } graffiti_file = { workspace = true } -lighthouse_validator_store = { workspace = true } logging = { workspace = true } parking_lot = { workspace = true } safe_arith = { workspace = true } serde_json = { workspace = true } slot_clock = { workspace = true } +ssz_types = { workspace = true } task_executor = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } @@ -25,4 +25,3 @@ tree_hash = { workspace = true } types = { workspace = true } validator_metrics = { workspace = true } validator_store = { workspace = true } -warp_utils = { workspace = true } diff --git a/validator_client/validator_services/src/proof_service.rs b/validator_client/validator_services/src/proof_service.rs index 679df7cffe5..7544b887336 100644 --- a/validator_client/validator_services/src/proof_service.rs +++ b/validator_client/validator_services/src/proof_service.rs @@ -1,28 +1,44 @@ //! EIP-8025 Execution Proof Service //! -//! This service handles both proactive and reactive execution proof workflows: +//! This service handles execution proof requests, signing and resigning workflows. //! -//! 1. **Proactive Mode**: Monitors beacon chain for new blocks via SSE and requests -//! proofs from the configured proof engine -//! 2. **Reactive Mode**: Receives proof requests from HTTP API (proof engine callbacks) -//! and signs/submits them to the beacon chain -//! -//! The service bridges the gap between external proof engines, validator keys, and -//! beacon nodes, providing a complete end-to-end execution proof flow. +//! Three concurrent tasks: +//! 1. **Beacon event monitor**: subscribes to beacon node SSE for new blocks and +//! validated-proof events — requests proofs and resigns validated proofs. +//! 2. **Proof engine event monitor**: subscribes to proof engine SSE for proof +//! completion/failure events — fetches completed proofs, signs them, and +//! submits to the beacon node. +//! 3. **Reactive HTTP handler**: receives proofs from proof engine callbacks. use beacon_node_fallback::BeaconNodeFallback; use bls::PublicKey; -use eth2::types::EventTopic; +use eth2::types::{BlockId, EventKind, EventTopic, SseExecutionProofValidated}; use execution_layer::NewPayloadRequest; -use execution_layer::eip8025::{HttpProofEngine, ProofEngine}; +use execution_layer::eip8025::{HttpProofEngine, ProofEvent}; use futures::StreamExt; +use parking_lot::RwLock; use slot_clock::SlotClock; +use std::collections::{HashMap, HashSet}; use std::sync::Arc; +use std::time::{Duration, Instant}; use task_executor::TaskExecutor; use tracing::{debug, error, info, warn}; -use types::execution::eip8025::ProofAttributes; -use types::{BeaconBlock, Epoch, EthSpec, ExecutionProof}; -use validator_store::ValidatorStore; +use types::execution::eip8025::{ProofAttributes, ProofData, PublicInput}; +use types::{Epoch, EthSpec, ExecutionProof, Hash256}; +use validator_store::{DoppelgangerStatus, ValidatorStore}; + +/// Discard tracking entries older than this. +const PROOF_REQUEST_STALE_TIMEOUT: Duration = Duration::from_secs(300); + +/// An outstanding proof request awaiting completion from the proof engine. +struct OutstandingProofRequest { + /// Proof types we are still waiting for. + pending_proof_types: HashSet, + /// Slot of the block (for epoch derivation during signing). + slot: types::Slot, + /// When the request was made. + requested_at: Instant, +} /// Background service for execution proof handling pub struct ProofService { @@ -36,6 +52,8 @@ struct Inner { slot_clock: T, executor: TaskExecutor, proof_types: Vec, + /// Outstanding proof requests keyed by `new_payload_request_root`. + outstanding_requests: RwLock>, } impl ProofService { @@ -60,23 +78,26 @@ impl ProofService) -> Result<(), String> { - // Only start monitoring if proof engine is configured let inner = self.inner.clone(); - let service_fut = async move { - inner.monitor_blocks_task().await; - }; - self.inner - .executor - .spawn(service_fut, "proof_service_monitor"); + self.inner.executor.spawn( + async move { inner.monitor_events_task().await }, + "proof_service_monitor", + ); - info!("Proof service started - monitoring for new blocks"); + let inner = self.inner.clone(); + self.inner.executor.spawn( + async move { inner.monitor_proof_engine_events_task().await }, + "proof_service_proof_engine_monitor", + ); + info!("Proof service started - monitoring beacon events and proof engine events"); Ok(()) } @@ -97,122 +118,379 @@ impl ProofService Inner { - /// Proactive: Monitor beacon node for new blocks and request proofs - async fn monitor_blocks_task(self: Arc) { - info!("Starting proof service block monitoring via SSE"); + // ─── Beacon node event monitoring (existing) ──────────────────────── + + /// Subscribe to both `Block` and `ExecutionProofValidated` events via a single SSE stream. + async fn subscribe_to_events( + &self, + ) -> Result< + impl futures::Stream, eth2::Error>>, + String, + > { + self.beacon_nodes + .first_success(|node| async move { + node.get_events::(&[EventTopic::Block, EventTopic::ExecutionProofValidated]) + .await + }) + .await + .map_err(|e| format!("All beacon nodes failed to provide event stream: {}", e)) + } + + /// Monitor block and validated-proof events over a single SSE connection. + async fn monitor_events_task(self: Arc) { + info!("Starting proof service event monitoring via SSE"); loop { - // Attempt to subscribe to block events from beacon node - match self.subscribe_to_blocks().await { + match self.subscribe_to_events().await { Ok(mut stream) => { - info!("Successfully subscribed to block events"); + info!("Successfully subscribed to block and execution proof events"); - // Process events from the stream while let Some(event_result) = stream.next().await { match event_result { - Ok(eth2::types::EventKind::BlockFull(block_event)) => { - let block = block_event.data; - if block.execution_optimistic { + Ok(EventKind::Block(sse_block)) => { + if sse_block.execution_optimistic { debug!( - slot = block.slot.as_u64(), - "Received execution optimistic block event" + slot = sse_block.slot.as_u64(), + "Skipping execution optimistic block" ); + continue; } - self.handle_block_event(&block.block, block.slot).await; + self.handle_block_event(sse_block.block, sse_block.slot) + .await; } - Ok(_) => { - // Ignore other event types (shouldn't happen with our topic filter) - debug!("Received non-block event in block_full stream"); + Ok(EventKind::ExecutionProofValidated(proof_event)) => { + self.handle_validated_proof(proof_event).await; } + Ok(_) => {} Err(e) => { - warn!( - error = %e, - "Error receiving block event, will reconnect" - ); - break; // Break inner loop to reconnect + warn!(error = %e, "Error receiving event, will reconnect"); + break; } } } - // Stream ended or errored - reconnect - warn!("Block event stream ended, reconnecting..."); + warn!("Event stream ended, reconnecting..."); } Err(e) => { - error!( - error = %e, - "Failed to subscribe to block events, retrying..." - ); + error!(error = %e, "Failed to subscribe to events, retrying..."); } } - } - } - /// Helper method to establish SSE subscription with beacon node fallback - async fn subscribe_to_blocks( - &self, - ) -> Result< - impl futures::Stream, eth2::Error>>, - String, - > { - self.beacon_nodes - .first_success( - |node| async move { node.get_events::(&[EventTopic::BlockFull]).await }, - ) - .await - .map_err(|e| format!("All beacon nodes failed to provide event stream: {}", e)) + tokio::time::sleep(Duration::from_secs(2)).await; + } } - /// Handle a new block event by requesting proofs from proof engine - async fn handle_block_event(&self, block: &BeaconBlock, slot: types::Slot) { - let block_root = block.canonical_root(); - + /// Handle a new block event by fetching the full block via RPC then requesting proofs. + async fn handle_block_event(&self, block_root: Hash256, slot: types::Slot) { info!( slot = slot.as_u64(), block = %block_root, - "New block detected, requesting proofs from proof engine" + "New block detected, fetching full block via RPC" ); - // Construct NewPayloadRequest from beacon block - let new_payload_request = match NewPayloadRequest::try_from(block.to_ref()) { + let signed_block = match self + .beacon_nodes + .first_success(|node| async move { + node.get_beacon_blocks::(BlockId::Root(block_root)) + .await + }) + .await + { + Ok(Some(response)) => response.data().clone(), + Ok(None) => { + warn!(block = %block_root, "Block not found on beacon node"); + return; + } + Err(e) => { + error!(block = %block_root, error = %e, "Failed to fetch block via RPC"); + return; + } + }; + + let new_payload_request = match NewPayloadRequest::try_from(signed_block.message()) { Ok(req) => req, Err(e) => { - error!( - error = ?e, - block = %block_root, - "Failed to construct NewPayloadRequest from block" - ); + error!(block = %block_root, error = ?e, "Failed to construct NewPayloadRequest"); return; } }; - // Use configured proof types let proof_attributes = ProofAttributes { proof_types: self.proof_types.clone(), }; - // Request proofs from proof engine - HttpProofEngine handles JSON serialization match self .proof_engine .request_proofs(new_payload_request, proof_attributes) .await { - Ok(proof_gen_id) => { + Ok(new_payload_request_root) => { + let pending_proof_types: HashSet = self.proof_types.iter().copied().collect(); + let num_types = pending_proof_types.len(); + self.outstanding_requests.write().insert( + new_payload_request_root, + OutstandingProofRequest { + pending_proof_types, + slot, + requested_at: Instant::now(), + }, + ); debug!( - proof_gen_id = ?proof_gen_id, + root = %new_payload_request_root, block = %block_root, - "Proof generation requested, awaiting callback to HTTP API" + num_proof_types = num_types, + "Proof generation requested, tracking for completion" ); } Err(e) => { - error!( - error = ?e, - block = %block_root, - "Failed to request proofs from proof engine" + error!(block = %block_root, error = ?e, "Failed to request proofs from proof engine"); + } + } + } + + /// Handle a validated proof event by resigning with the first local validator key. + async fn handle_validated_proof(&self, event: SseExecutionProofValidated) { + let execution_proof = event.execution_proof; + let epoch = Epoch::new(event.epoch); + + let Some(pubkey) = self + .validator_store + .voting_pubkeys::, _>(DoppelgangerStatus::ignored) + .first() + .cloned() + else { + warn!("No local validators available to resign proof"); + return; + }; + + match self + .validator_store + .sign_execution_proof(pubkey, execution_proof, epoch) + .await + { + Ok(signed_proof) => { + match self + .beacon_nodes + .first_success(move |node| { + let proof = signed_proof.clone(); + async move { node.post_beacon_execution_proofs(&[proof]).await } + }) + .await + { + Ok(_) => { + info!(?pubkey, "Resigned proof submitted"); + } + Err(e) => { + warn!(?pubkey, error = %e, "Failed to submit resigned proof"); + } + } + } + Err(e) => { + warn!(?pubkey, error = ?e, "Failed to sign proof for validator"); + } + } + } + + // ─── Proof engine event monitoring (new) ──────────────────────────── + + /// Monitor proof engine SSE events for proof completion, failure, and timeouts. + async fn monitor_proof_engine_events_task(self: Arc) { + info!("Starting proof engine event monitoring via SSE"); + + loop { + let mut stream = self.proof_engine.subscribe_proof_events(None); + + loop { + tokio::select! { + event = stream.next() => { + match event { + Some(Ok(proof_event)) => { + self.handle_proof_engine_event(proof_event).await; + } + Some(Err(e)) => { + warn!(error = %e, "Proof engine SSE error, will reconnect"); + break; + } + None => { + warn!("Proof engine SSE stream ended, reconnecting..."); + break; + } + } + } + _ = tokio::time::sleep(PROOF_REQUEST_STALE_TIMEOUT) => { + self.cleanup_stale_requests(); + } + } + } + + tokio::time::sleep(Duration::from_secs(2)).await; + } + } + + /// Process a single proof engine SSE event. + async fn handle_proof_engine_event(&self, event: ProofEvent) { + let root = event.new_payload_request_root(); + let proof_type = event.proof_type(); + + // Only process events for tracked requests and requested proof types. + let is_tracked = self + .outstanding_requests + .read() + .get(&root) + .map(|req| req.pending_proof_types.contains(&proof_type)) + .unwrap_or(false); + + if !is_tracked { + return; + } + + match event { + ProofEvent::ProofComplete(complete) => { + self.handle_proof_complete(complete.new_payload_request_root, complete.proof_type) + .await; + } + ProofEvent::ProofFailure(failure) => { + warn!( + root = %failure.new_payload_request_root, + proof_type = failure.proof_type, + error = %failure.error, + "Proof generation failed" + ); + self.remove_pending_proof_type( + failure.new_payload_request_root, + failure.proof_type, ); } + ProofEvent::WitnessTimeout(ref info) | ProofEvent::ProofTimeout(ref info) => { + warn!( + root = %info.new_payload_request_root, + proof_type = info.proof_type, + "Proof generation timed out" + ); + self.remove_pending_proof_type(info.new_payload_request_root, info.proof_type); + } + } + } + + /// Fetch a completed proof from the proof engine, sign it, and submit to the beacon node. + async fn handle_proof_complete(&self, root: Hash256, proof_type: u8) { + // Download proof bytes from proof engine. + let proof_bytes = match self.proof_engine.get_proof(root, proof_type).await { + Ok(bytes) => bytes, + Err(e) => { + error!(root = %root, proof_type, error = ?e, "Failed to fetch completed proof"); + return; + } + }; + + // Construct ExecutionProof. + let proof_data = match ProofData::new(proof_bytes.to_vec()) { + Ok(data) => data, + Err(e) => { + error!(root = %root, proof_type, error = ?e, "Proof data exceeds max size"); + return; + } + }; + + let execution_proof = ExecutionProof { + proof_data, + proof_type, + public_input: PublicInput { + new_payload_request_root: root, + }, + }; + + // Derive signing epoch from the stored slot. + let epoch = self + .outstanding_requests + .read() + .get(&root) + .map(|req| req.slot.epoch(S::E::slots_per_epoch())); + + let Some(epoch) = epoch else { + // Entry was removed (e.g. by stale cleanup) between the is_tracked check + // and here — nothing to do. + return; + }; + + // Resolve a safe validator for signing. + let Some(pubkey) = self + .validator_store + .voting_pubkeys::, _>(DoppelgangerStatus::only_safe) + .first() + .cloned() + else { + warn!("No safe validators available to sign completed proof"); + return; + }; + + // Sign and submit. + match self + .validator_store + .sign_execution_proof(pubkey, execution_proof, epoch) + .await + { + Ok(signed_proof) => { + match self + .beacon_nodes + .first_success(move |node| { + let proof = signed_proof.clone(); + async move { node.post_beacon_execution_proofs(&[proof]).await } + }) + .await + { + Ok(_) => { + info!(root = %root, proof_type, ?pubkey, "Completed proof signed and submitted"); + } + Err(e) => { + warn!(root = %root, proof_type, error = %e, "Failed to submit completed proof"); + } + } + } + Err(e) => { + warn!(root = %root, proof_type, error = ?e, "Failed to sign completed proof"); + } } + + // Remove this proof type from the outstanding set. + self.remove_pending_proof_type(root, proof_type); } + // ─── Outstanding request management ───────────────────────────────── + + /// Remove a single proof type from an outstanding request. + /// + /// If all requested proof types have been resolved the entry is removed entirely. + fn remove_pending_proof_type(&self, root: Hash256, proof_type: u8) { + let mut requests = self.outstanding_requests.write(); + if let Some(entry) = requests.get_mut(&root) { + entry.pending_proof_types.remove(&proof_type); + if entry.pending_proof_types.is_empty() { + requests.remove(&root); + debug!(root = %root, "All proof types resolved, removing from tracker"); + } + } + } + + /// Remove outstanding requests that have exceeded the stale timeout. + fn cleanup_stale_requests(&self) { + let mut requests = self.outstanding_requests.write(); + let before = requests.len(); + requests.retain(|root, req| { + let stale = req.requested_at.elapsed() > PROOF_REQUEST_STALE_TIMEOUT; + if stale { + warn!(root = %root, "Removing stale proof request (timed out)"); + } + !stale + }); + let removed = before - requests.len(); + if removed > 0 { + info!(removed, "Cleaned up stale proof requests"); + } + } + + // ─── Reactive signing (existing) ──────────────────────────────────── + /// Reactive: Sign and submit proof (called by HTTP API) async fn sign_and_submit_proof( &self,