diff --git a/.github/workflows/pr-main_l1.yaml b/.github/workflows/pr-main_l1.yaml index f1962402b8d..e4ca15d9f88 100644 --- a/.github/workflows/pr-main_l1.yaml +++ b/.github/workflows/pr-main_l1.yaml @@ -347,6 +347,9 @@ jobs: - name: Free Disk Space uses: ./.github/actions/free-disk + - name: Setup Rust Environment + uses: ./.github/actions/setup-rust + # If this fails you should run `make update-cargo-lock` - name: Check Cargo.lock files are committed run: | diff --git a/Cargo.lock b/Cargo.lock index 334280fb331..87b6367f438 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,7 +67,7 @@ version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "cpp_demangle 0.4.5", + "cpp_demangle", "fallible-iterator 0.3.0", "gimli 0.31.1", "memmap2", @@ -235,7 +235,7 @@ dependencies = [ "either", "k256", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "secp256k1", "serde", "serde_json", @@ -349,13 +349,14 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" +checksum = "407510740da514b694fecb44d8b3cebdc60d448f70cc5d24743e8ba273448a6e" dependencies = [ "alloy-primitives", "alloy-rlp", "borsh", + "once_cell", "serde", ] @@ -610,7 +611,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "derive_more 2.1.1", - "rand 0.8.5", + "rand 0.8.6", "serde", "strum 0.27.2", ] @@ -675,7 +676,7 @@ dependencies = [ "async-trait", "eth-keystore", "k256", - "rand 0.8.5", + "rand 0.8.6", "thiserror 2.0.18", ] @@ -1266,7 +1267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -1276,7 +1277,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -1286,7 +1287,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -1386,9 +1387,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.16.2" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" dependencies = [ "aws-lc-sys", "zeroize", @@ -1396,9 +1397,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.39.1" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a25cf98105baa966497416dbd42565ce3a8cf8dbfd59803ec9ad46f3126399" +checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" dependencies = [ "cc", "cmake", @@ -1594,7 +1595,7 @@ dependencies = [ "getrandom 0.2.17", "instant", "pin-project-lite", - "rand 0.8.5", + "rand 0.8.6", "tokio", ] @@ -1638,6 +1639,12 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + [[package]] name = "bincode" version = "1.3.3" @@ -2310,7 +2317,7 @@ dependencies = [ "num-traits", "prost 0.13.5", "prost-build 0.13.5", - "rand 0.8.5", + "rand 0.8.6", "ruint", "serde", "serde_json", @@ -2349,9 +2356,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -2371,9 +2378,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -2505,11 +2512,12 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_format" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" dependencies = [ "const_format_proc_macros", + "konst", ] [[package]] @@ -2617,15 +2625,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "cpp_demangle" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0667304c32ea56cb4cd6d2d7c0cfe9a2f8041229db8c033af7f8d69492429def" -dependencies = [ - "cfg-if", -] - [[package]] name = "cpufeatures" version = "0.2.17" @@ -3149,7 +3148,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" dependencies = [ - "uuid 1.23.0", + "uuid 1.23.1", ] [[package]] @@ -3426,7 +3425,7 @@ checksum = "9ac1e888d6830712d565b2f3a974be3200be9296bc1b03db8251a4cbf18a4a34" dependencies = [ "digest 0.10.7", "futures", - "rand 0.8.5", + "rand 0.8.6", "reqwest 0.12.28", "thiserror 1.0.69", "tokio", @@ -3726,7 +3725,7 @@ dependencies = [ "hex", "hmac", "pbkdf2", - "rand 0.8.5", + "rand 0.8.6", "scrypt", "serde", "serde_json", @@ -3818,7 +3817,7 @@ dependencies = [ "lazy_static", "local-ip-address", "pprof", - "rand 0.8.5", + "rand 0.8.6", "rayon", "reqwest 0.12.28", "secp256k1", @@ -4035,7 +4034,9 @@ dependencies = [ "hex", "jsonwebtoken", "lazy_static", - "rand 0.8.5", + "prost 0.13.5", + "protox", + "rand 0.8.6", "ratatui", "reqwest 0.12.28", "secp256k1", @@ -4047,7 +4048,10 @@ dependencies = [ "spawned-rt", "thiserror 2.0.18", "tokio", + "tokio-stream", "tokio-util", + "tonic 0.12.3", + "tonic-build", "tracing", "tui-logger", "vergen-git2 1.0.7", @@ -4231,7 +4235,7 @@ dependencies = [ "lazy_static", "lru 0.16.4", "prometheus", - "rand 0.8.5", + "rand 0.8.6", "rayon", "rocksdb", "rustc-hash 2.1.2", @@ -4337,7 +4341,7 @@ dependencies = [ "jsonwebtoken", "libssz-merkle", "libssz-types", - "rand 0.8.5", + "rand 0.8.6", "reqwest 0.12.28", "secp256k1", "serde", @@ -4351,7 +4355,7 @@ dependencies = [ "tower-http 0.6.8", "tracing", "tracing-subscriber 0.3.23", - "uuid 1.23.0", + "uuid 1.23.1", ] [[package]] @@ -4456,7 +4460,7 @@ dependencies = [ "lazy_static", "once_cell", "proptest", - "rand 0.8.5", + "rand 0.8.6", "reqwest 0.12.28", "rkyv", "rustc-hash 2.1.2", @@ -4495,6 +4499,7 @@ dependencies = [ "ethrex-crypto", "ethrex-levm", "ethrex-rlp", + "hex", "rayon", "rustc-hash 2.1.2", "serde", @@ -4676,7 +4681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.5", + "rand 0.8.6", "rustc-hex", "static_assertions", ] @@ -5098,7 +5103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" dependencies = [ "ff 0.13.1", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "rand_xorshift 0.3.0", "subtle", @@ -5176,7 +5181,7 @@ dependencies = [ "itertools 0.11.0", "maybe-rayon", "pairing 0.23.0", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "rayon", "rustc-hash 1.1.0", @@ -5216,7 +5221,7 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "rand_chacha 0.3.1", "rand_core 0.6.4", "rayon", @@ -5257,7 +5262,7 @@ dependencies = [ "num-traits", "pairing 0.23.0", "paste", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "rayon", "serde", @@ -5285,7 +5290,7 @@ dependencies = [ "pairing 0.23.0", "pasta_curves 0.5.1", "paste", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "rayon", "serde", @@ -5312,7 +5317,7 @@ dependencies = [ "pairing 0.23.0", "pasta_curves 0.5.1", "paste", - "rand 0.8.5", + "rand 0.8.6", "rand_core 0.6.4", "rayon", "serde", @@ -5682,9 +5687,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.8" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2b52f86d1d4bc0d6b4e6826d960b1b333217e07d36b882dca570a5e1c48895b" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http 1.4.0", "hyper 1.9.0", @@ -5693,7 +5698,7 @@ dependencies = [ "tokio", "tokio-rustls 0.26.4", "tower-service", - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] @@ -6322,6 +6327,21 @@ dependencies = [ "sha3-asm", ] +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "kzg-rs" version = "0.2.8" @@ -6342,7 +6362,7 @@ version = "0.12.0" source = "git+https://github.com/lambdaclass/lambdaworks.git?rev=5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b#5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b" dependencies = [ "lambdaworks-math 0.12.0", - "rand 0.8.5", + "rand 0.8.6", "rand_chacha 0.3.1", "serde", "sha2", @@ -6356,7 +6376,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58b1a1c1102a5a7fbbda117b79fb3a01e033459c738a3c1642269603484fd1c1" dependencies = [ "lambdaworks-math 0.13.0", - "rand 0.8.5", + "rand 0.8.6", "rand_chacha 0.3.1", "serde", "sha2", @@ -6369,7 +6389,7 @@ version = "0.12.0" source = "git+https://github.com/lambdaclass/lambdaworks.git?rev=5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b#5f8f2cfcc8a1a22f77e8dff2d581f1166eefb80b" dependencies = [ "getrandom 0.2.17", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_json", ] @@ -6383,7 +6403,7 @@ dependencies = [ "getrandom 0.2.17", "num-bigint 0.4.6", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_json", ] @@ -6560,7 +6580,7 @@ dependencies = [ "tower 0.4.13", "tower-http 0.4.4", "tracing", - "uuid 1.23.0", + "uuid 1.23.1", "zerocopy 0.7.35", ] @@ -6656,7 +6676,7 @@ dependencies = [ "tokio-util", "tonic 0.11.0", "tracing", - "uuid 1.23.0", + "uuid 1.23.1", "zerocopy 0.7.35", ] @@ -6735,9 +6755,9 @@ checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "local-ip-address" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a59a0cb1c7f84471ad5cd38d768c2a29390d17f1ff2827cdf49bc53e8ac70b" +checksum = "d7b0187df4e614e42405b49511b82ff7a1774fbd9a816060ee465067847cac22" dependencies = [ "libc", "neli", @@ -6765,6 +6785,39 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "logos" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7251356ef8cb7aec833ddf598c6cb24d17b689d20b993f9d11a3d764e34e6458" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f80069600c0d66734f5ff52cc42f2dabd6b29d205f333d61fd7832e9e9963f" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2", + "quote", + "regex-syntax", + "syn 2.0.117", +] + +[[package]] +name = "logos-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24fb722b06a9dc12adb0963ed585f19fc61dc5413e6a9be9422ef92c091e731d" +dependencies = [ + "logos-codegen", +] + [[package]] name = "lru" version = "0.12.5" @@ -7088,6 +7141,28 @@ dependencies = [ "sketches-ddsketch", ] +[[package]] +name = "miette" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" +dependencies = [ + "cfg-if", + "miette-derive", + "unicode-width 0.1.14", +] + +[[package]] +name = "miette-derive" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "mime" version = "0.3.17" @@ -7343,7 +7418,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -7358,7 +7433,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "zeroize", ] @@ -7458,7 +7533,7 @@ dependencies = [ "num-integer", "num-modular 0.5.1", "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -7558,7 +7633,7 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -7673,9 +7748,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.77" +version = "0.10.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe4646e360ec77dff7dde40ed3d6c5fee52d156ef4a62f53973d38294dad87f" +checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" dependencies = [ "bitflags 2.11.1", "cfg-if", @@ -7711,9 +7786,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.113" +version = "0.9.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2f2c0eba47118757e4c6d2bff2838f3e0523380021356e7875e858372ce644" +checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" dependencies = [ "cc", "libc", @@ -8331,7 +8406,7 @@ dependencies = [ "openvm-instructions", "openvm-stark-backend 1.3.0", "openvm-stark-sdk 1.3.0", - "rand 0.8.5", + "rand 0.8.6", "rand 0.9.4", "tracing", ] @@ -8418,7 +8493,7 @@ dependencies = [ "p3-fri 0.4.1", "p3-merkle-tree 0.4.1", "p3-symmetric 0.4.1", - "rand 0.8.5", + "rand 0.8.6", "rand 0.9.4", "serde", "serde_json", @@ -8457,7 +8532,7 @@ dependencies = [ "openvm-pairing-guest 1.4.1", "openvm-platform 1.4.1", "openvm-rv32im-guest 1.4.1", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -8507,7 +8582,7 @@ dependencies = [ "openvm-algebra-moduli-macros 1.4.1", "openvm-custom-insn 0.1.0 (git+https://github.com/openvm-org/openvm.git?tag=v1.4.1)", "openvm-ecc-guest 1.4.1", - "rand 0.8.5", + "rand 0.8.6", "serde", "strum_macros 0.26.4", ] @@ -8860,7 +8935,7 @@ dependencies = [ "p3-poseidon 0.1.0", "p3-poseidon2 0.1.0", "p3-symmetric 0.1.0", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_json", "static_assertions", @@ -8995,7 +9070,7 @@ dependencies = [ "p3-monty-31 0.1.0", "p3-poseidon2 0.1.0", "p3-symmetric 0.1.0", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9010,7 +9085,7 @@ dependencies = [ "p3-mds 0.2.3-succinct", "p3-poseidon2 0.2.3-succinct", "p3-symmetric 0.2.3-succinct", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9077,7 +9152,7 @@ dependencies = [ "p3-field 0.1.0", "p3-poseidon2 0.1.0", "p3-symmetric 0.1.0", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9092,7 +9167,7 @@ dependencies = [ "p3-field 0.2.3-succinct", "p3-poseidon2 0.2.3-succinct", "p3-symmetric 0.2.3-succinct", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9107,7 +9182,7 @@ dependencies = [ "p3-field 0.3.2-succinct", "p3-poseidon2 0.3.2-succinct", "p3-symmetric 0.3.2-succinct", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9274,7 +9349,7 @@ dependencies = [ "nums", "p3-maybe-rayon 0.1.0", "p3-util 0.1.0", - "rand 0.8.5", + "rand 0.8.6", "serde", "tracing", ] @@ -9289,7 +9364,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "p3-util 0.2.3-succinct", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9303,7 +9378,7 @@ dependencies = [ "num-bigint 0.4.6", "num-traits", "p3-util 0.3.2-succinct", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9337,7 +9412,7 @@ dependencies = [ "p3-matrix 0.1.0", "p3-maybe-rayon 0.1.0", "p3-util 0.1.0", - "rand 0.8.5", + "rand 0.8.6", "serde", "tracing", ] @@ -9395,7 +9470,7 @@ dependencies = [ "p3-poseidon2 0.1.0", "p3-symmetric 0.1.0", "p3-util 0.1.0", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9515,7 +9590,7 @@ dependencies = [ "p3-monty-31 0.1.0", "p3-poseidon2 0.1.0", "p3-symmetric 0.1.0", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9530,7 +9605,7 @@ dependencies = [ "p3-mds 0.3.2-succinct", "p3-poseidon2 0.3.2-succinct", "p3-symmetric 0.3.2-succinct", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9557,7 +9632,7 @@ dependencies = [ "p3-field 0.1.0", "p3-maybe-rayon 0.1.0", "p3-util 0.1.0", - "rand 0.8.5", + "rand 0.8.6", "serde", "tracing", "transpose", @@ -9573,7 +9648,7 @@ dependencies = [ "p3-field 0.2.3-succinct", "p3-maybe-rayon 0.2.3-succinct", "p3-util 0.2.3-succinct", - "rand 0.8.5", + "rand 0.8.6", "serde", "tracing", ] @@ -9588,7 +9663,7 @@ dependencies = [ "p3-field 0.3.2-succinct", "p3-maybe-rayon 0.3.2-succinct", "p3-util 0.3.2-succinct", - "rand 0.8.5", + "rand 0.8.6", "serde", "tracing", ] @@ -9652,7 +9727,7 @@ dependencies = [ "p3-matrix 0.1.0", "p3-symmetric 0.1.0", "p3-util 0.1.0", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -9667,7 +9742,7 @@ dependencies = [ "p3-matrix 0.2.3-succinct", "p3-symmetric 0.2.3-succinct", "p3-util 0.2.3-succinct", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -9682,7 +9757,7 @@ dependencies = [ "p3-matrix 0.3.2-succinct", "p3-symmetric 0.3.2-succinct", "p3-util 0.3.2-succinct", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -9710,7 +9785,7 @@ dependencies = [ "p3-maybe-rayon 0.1.0", "p3-symmetric 0.1.0", "p3-util 0.1.0", - "rand 0.8.5", + "rand 0.8.6", "serde", "tracing", ] @@ -9766,7 +9841,7 @@ dependencies = [ "p3-poseidon2 0.1.0", "p3-symmetric 0.1.0", "p3-util 0.1.0", - "rand 0.8.5", + "rand 0.8.6", "serde", "tracing", "transpose", @@ -9804,7 +9879,7 @@ dependencies = [ "p3-field 0.1.0", "p3-mds 0.1.0", "p3-symmetric 0.1.0", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -9828,7 +9903,7 @@ dependencies = [ "p3-field 0.1.0", "p3-mds 0.1.0", "p3-symmetric 0.1.0", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -9841,7 +9916,7 @@ dependencies = [ "p3-field 0.2.3-succinct", "p3-mds 0.2.3-succinct", "p3-symmetric 0.2.3-succinct", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -9855,7 +9930,7 @@ dependencies = [ "p3-field 0.3.2-succinct", "p3-mds 0.3.2-succinct", "p3-symmetric 0.3.2-succinct", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -10081,7 +10156,7 @@ dependencies = [ "ff 0.12.1", "group 0.12.1", "lazy_static", - "rand 0.8.5", + "rand 0.8.6", "static_assertions", "subtle", ] @@ -10096,7 +10171,7 @@ dependencies = [ "ff 0.13.1", "group 0.13.0", "lazy_static", - "rand 0.8.5", + "rand 0.8.6", "static_assertions", "subtle", ] @@ -10215,7 +10290,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -10341,9 +10416,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] @@ -10358,7 +10433,7 @@ dependencies = [ "ff 0.13.1", "lazy_static", "log", - "rand 0.8.5", + "rand 0.8.6", "rand_xorshift 0.3.0", "thiserror 1.0.69", ] @@ -10755,6 +10830,19 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "prost-reflect" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5edd582b62f5cde844716e66d92565d7faf7ab1445c8cebce6e00fba83ddb2" +dependencies = [ + "logos", + "miette", + "once_cell", + "prost 0.13.5", + "prost-types 0.13.5", +] + [[package]] name = "prost-types" version = "0.12.6" @@ -10793,6 +10881,33 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "protox" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f352af331bf637b8ecc720f7c87bf903d2571fa2e14a66e9b2558846864b54a" +dependencies = [ + "bytes", + "miette", + "prost 0.13.5", + "prost-reflect", + "prost-types 0.13.5", + "protox-parse", + "thiserror 1.0.69", +] + +[[package]] +name = "protox-parse" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a462d115462c080ae000c29a47f0b3985737e5d3a995fcdbcaa5c782068dde" +dependencies = [ + "logos", + "miette", + "prost-types 0.13.5", + "thiserror 1.0.69", +] + [[package]] name = "ptr_meta" version = "0.3.1" @@ -10961,9 +11076,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -11240,7 +11355,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.9.0", - "hyper-rustls 0.27.8", + "hyper-rustls 0.27.9", "hyper-tls", "hyper-util", "js-sys", @@ -11269,7 +11384,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] @@ -11285,7 +11400,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "hyper 1.9.0", - "hyper-rustls 0.27.8", + "hyper-rustls 0.27.9", "hyper-util", "js-sys", "log", @@ -11751,7 +11866,7 @@ dependencies = [ "rend", "rkyv_derive", "tinyvec", - "uuid 1.23.0", + "uuid 1.23.1", ] [[package]] @@ -11856,7 +11971,7 @@ dependencies = [ "parity-scale-codec", "primitive-types 0.12.2", "proptest", - "rand 0.8.5", + "rand 0.8.6", "rand 0.9.4", "rlp 0.5.2", "ruint-macro", @@ -11986,7 +12101,7 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.103.12", + "rustls-webpki 0.103.13", "subtle", "zeroize", ] @@ -12049,7 +12164,7 @@ dependencies = [ "rustls 0.23.38", "rustls-native-certs 0.8.3", "rustls-platform-verifier-android", - "rustls-webpki 0.103.12", + "rustls-webpki 0.103.13", "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", @@ -12075,9 +12190,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.12" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8279bb85272c9f10811ae6a6c547ff594d6a7f3c6c6b02ee9726d1d0dcfcdd06" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -12330,7 +12445,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", + "rand 0.8.6", "secp256k1-sys", "serde", ] @@ -12601,9 +12716,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" dependencies = [ "digest 0.10.7", "keccak", @@ -12835,7 +12950,7 @@ dependencies = [ "num-integer", "num-traits", "pairing 0.23.0", - "rand 0.8.5", + "rand 0.8.6", "ruint", "serde", "sha3", @@ -12857,7 +12972,7 @@ dependencies = [ "num-bigint 0.4.6", "num-integer", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "rand_chacha 0.3.1", "serde", "serde_json", @@ -12932,7 +13047,7 @@ dependencies = [ "p3-field 0.2.3-succinct", "p3-maybe-rayon 0.2.3-succinct", "p3-util 0.2.3-succinct", - "rand 0.8.5", + "rand 0.8.6", "range-set-blaze", "rrs-succinct", "rustc-demangle", @@ -12983,7 +13098,7 @@ dependencies = [ "p3-uni-stark 0.2.3-succinct", "p3-util 0.2.3-succinct", "pathdiff", - "rand 0.8.5", + "rand 0.8.6", "rayon", "rayon-scan", "serde", @@ -13177,7 +13292,7 @@ dependencies = [ "p3-symmetric 0.2.3-succinct", "p3-uni-stark 0.2.3-succinct", "p3-util 0.2.3-succinct", - "rand 0.8.5", + "rand 0.8.6", "rayon", "serde", "sp1-core-executor", @@ -13243,7 +13358,7 @@ dependencies = [ "p3-symmetric 0.2.3-succinct", "p3-util 0.2.3-succinct", "pathdiff", - "rand 0.8.5", + "rand 0.8.6", "serde", "sp1-core-machine", "sp1-derive", @@ -13567,7 +13682,7 @@ dependencies = [ "byteorder", "crunchy", "lazy_static", - "rand 0.8.5", + "rand 0.8.6", "rustc-hex", ] @@ -13579,27 +13694,33 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "symbolic-common" -version = "12.18.0" +version = "12.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aba7211a1803a826f108af9f4d86d25abe880712f2a6449479279e861b293f8" +checksum = "4bbc8b4f883265fb2207a0d6ce67095f4bd6cf13e5f9183eb8b3e73fa1b2466a" dependencies = [ "debugid", "memmap2", "stable_deref_trait", - "uuid 1.23.0", + "uuid 1.23.1", ] [[package]] name = "symbolic-demangle" -version = "12.18.0" +version = "12.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595bddd9d363c2ef6fc9fb33b98416ff209c5aae8bdca89a7dbbf2ef5e0ecc45" +checksum = "8eda470a26ea40eb5cafc35487718e23a2f19153d3a589affb9e8f7fa1aa73b3" dependencies = [ - "cpp_demangle 0.5.1", + "cpp_demangle", "rustc-demangle", "symbolic-common", ] +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + [[package]] name = "syn" version = "1.0.109" @@ -13930,9 +14051,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.51.1" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", @@ -14099,7 +14220,7 @@ dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.1", + "winnow 1.0.2", ] [[package]] @@ -14108,7 +14229,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.1", + "winnow 1.0.2", ] [[package]] @@ -14177,6 +14298,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "tonic-build" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build 0.13.5", + "prost-types 0.13.5", + "quote", + "syn 2.0.117", +] + [[package]] name = "tonic-web" version = "0.11.0" @@ -14208,7 +14343,7 @@ dependencies = [ "indexmap 1.9.3", "pin-project", "pin-project-lite", - "rand 0.8.5", + "rand 0.8.6", "slab", "tokio", "tokio-util", @@ -14297,11 +14432,12 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" dependencies = [ "crossbeam-channel", + "symlink", "thiserror 2.0.18", "time", "tracing-subscriber 0.3.23", @@ -14489,9 +14625,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "typetag" @@ -14519,9 +14655,9 @@ dependencies = [ [[package]] name = "typewit" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc19094686c694eb41b3b99dcc2f2975d4b078512fa22ae6c63f7ca318bdcff7" +checksum = "214ca0b2191785cbc06209b9ca1861e048e39b5ba33574b3cedd58363d5bb5f6" [[package]] name = "ucd-trie" @@ -14684,9 +14820,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -14846,11 +14982,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -14859,7 +14995,7 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] @@ -15000,9 +15136,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" dependencies = [ "rustls-pki-types", ] @@ -15013,14 +15149,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] @@ -15573,9 +15709,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" dependencies = [ "memchr", ] @@ -15589,6 +15725,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -15886,7 +16028,7 @@ dependencies = [ "num-integer", "num-traits", "precompiles-helpers", - "rand 0.8.5", + "rand 0.8.6", "serde", "sha2", "tiny-keccak", @@ -15914,7 +16056,7 @@ dependencies = [ "jubjub", "lazy_static", "pasta_curves 0.5.1", - "rand 0.8.5", + "rand 0.8.6", "serde", "sha2", "sha3", @@ -15940,7 +16082,7 @@ dependencies = [ "jubjub", "lazy_static", "pasta_curves 0.5.1", - "rand 0.8.5", + "rand 0.8.6", "serde", "sha2", "sha3", diff --git a/cmd/ethrex/l2/options.rs b/cmd/ethrex/l2/options.rs index 78e4426e1d3..79f6c6d444c 100644 --- a/cmd/ethrex/l2/options.rs +++ b/cmd/ethrex/l2/options.rs @@ -8,7 +8,7 @@ use ethrex_l2::sequencer::utils::resolve_aligned_network; use ethrex_l2::{ BasedConfig, BlockFetcherConfig, BlockProducerConfig, CommitterConfig, EthConfig, L1WatcherConfig, ProofCoordinatorConfig, SequencerConfig, StateUpdaterConfig, - sequencer::configs::{AdminConfig, AlignedConfig, MonitorConfig}, + sequencer::configs::{AdminConfig, AlignedConfig, CredibleLayerConfig, MonitorConfig}, }; use ethrex_l2_prover::{backend::BackendType, config::ProverConfig}; use ethrex_l2_rpc::signer::{LocalSigner, RemoteSigner, Signer}; @@ -92,6 +92,8 @@ pub struct SequencerOptions { pub admin_opts: AdminOptions, #[clap(flatten)] pub state_updater_opts: StateUpdaterOptions, + #[clap(flatten)] + pub credible_layer_opts: CredibleLayerOptions, #[arg( long = "validium", default_value = "false", @@ -263,6 +265,9 @@ impl TryFrom for SequencerConfig { start_at: opts.state_updater_opts.start_at, l2_head_check_rpc_url: opts.state_updater_opts.l2_head_check_rpc_url, }, + credible_layer: CredibleLayerConfig { + sidecar_url: opts.credible_layer_opts.credible_layer_url, + }, }) } } @@ -1066,6 +1071,18 @@ impl Default for AdminOptions { } } +#[derive(Parser, Default, Debug)] +pub struct CredibleLayerOptions { + #[arg( + long = "credible-layer-url", + value_name = "URL", + env = "ETHREX_CREDIBLE_LAYER_URL", + help = "gRPC endpoint for the Credible Layer Assertion Enforcer sidecar (e.g. http://localhost:50051). Passing this flag enables the integration.", + help_heading = "Credible Layer options" + )] + pub credible_layer_url: Option, +} + #[derive(Parser)] pub struct ProverClientOptions { #[arg( diff --git a/crates/guest-program/bin/openvm/Cargo.lock b/crates/guest-program/bin/openvm/Cargo.lock index 2550acb10f9..775d336e4bb 100644 --- a/crates/guest-program/bin/openvm/Cargo.lock +++ b/crates/guest-program/bin/openvm/Cargo.lock @@ -863,6 +863,7 @@ dependencies = [ "ethrex-crypto", "ethrex-levm", "ethrex-rlp", + "hex", "rayon", "rustc-hash", "serde", diff --git a/crates/guest-program/bin/risc0/Cargo.lock b/crates/guest-program/bin/risc0/Cargo.lock index 1665d67ca3f..3eb9ceb19c3 100644 --- a/crates/guest-program/bin/risc0/Cargo.lock +++ b/crates/guest-program/bin/risc0/Cargo.lock @@ -1108,6 +1108,7 @@ dependencies = [ "ethrex-crypto", "ethrex-levm", "ethrex-rlp", + "hex", "rayon", "rustc-hash", "serde", diff --git a/crates/guest-program/bin/sp1/Cargo.lock b/crates/guest-program/bin/sp1/Cargo.lock index e6530927eac..4ca066fa512 100644 --- a/crates/guest-program/bin/sp1/Cargo.lock +++ b/crates/guest-program/bin/sp1/Cargo.lock @@ -918,6 +918,7 @@ dependencies = [ "ethrex-crypto", "ethrex-levm", "ethrex-rlp", + "hex", "rayon", "rustc-hash", "serde", diff --git a/crates/guest-program/bin/zisk/Cargo.lock b/crates/guest-program/bin/zisk/Cargo.lock index 7aa3108b9cb..2855204d802 100644 --- a/crates/guest-program/bin/zisk/Cargo.lock +++ b/crates/guest-program/bin/zisk/Cargo.lock @@ -1203,6 +1203,7 @@ dependencies = [ "ethrex-crypto", "ethrex-levm", "ethrex-rlp", + "hex", "rayon", "rustc-hash 2.1.1", "serde", diff --git a/crates/l2/Cargo.toml b/crates/l2/Cargo.toml index dc1f8d8a4b3..2aba8708f55 100644 --- a/crates/l2/Cargo.toml +++ b/crates/l2/Cargo.toml @@ -54,13 +54,19 @@ crossterm = { version = "0.29.0", features = ["event-stream"] } ratatui = "0.29.0" tui-logger.workspace = true axum.workspace = true +tonic = "0.12" +prost = "0.13" +tokio-stream = "0.1" ethrex-guest-program = { path = "../guest-program", optional = true } [build-dependencies] vergen-git2 = { version = "1.0.7" } +tonic-build = "0.12" +protox = "0.7" [dev-dependencies] anyhow = "1.0.86" +hex = { workspace = true } [lib] path = "./l2.rs" diff --git a/crates/l2/build.rs b/crates/l2/build.rs index 3ea743e82cf..6a859141df4 100644 --- a/crates/l2/build.rs +++ b/crates/l2/build.rs @@ -9,10 +9,16 @@ fn main() -> Result<(), Box> { // When building tdx image with nix the commit version is stored as an env var if let Ok(sha) = std::env::var("VERGEN_GIT_SHA") { println!("cargo:rustc-env=VERGEN_GIT_SHA={}", sha.trim()); - return Ok(()); + } else { + let git2 = Git2Builder::default().sha(true).build()?; + Emitter::default().add_instructions(&git2)?.emit()?; } - let git2 = Git2Builder::default().sha(true).build()?; - Emitter::default().add_instructions(&git2)?.emit()?; + // Compile Credible Layer protobuf definitions (client only). + let fds = protox::compile(["proto/sidecar.proto"], ["proto/"])?; + tonic_build::configure() + .build_server(false) + .compile_fds(fds)?; + Ok(()) } diff --git a/crates/l2/l2.rs b/crates/l2/l2.rs index 2cd23de17c3..d9dfa93e646 100644 --- a/crates/l2/l2.rs +++ b/crates/l2/l2.rs @@ -6,7 +6,7 @@ pub mod utils; pub use based::block_fetcher::BlockFetcher; pub use sequencer::configs::{ - BasedConfig, BlockFetcherConfig, BlockProducerConfig, CommitterConfig, EthConfig, - L1WatcherConfig, ProofCoordinatorConfig, SequencerConfig, StateUpdaterConfig, + BasedConfig, BlockFetcherConfig, BlockProducerConfig, CommitterConfig, CredibleLayerConfig, + EthConfig, L1WatcherConfig, ProofCoordinatorConfig, SequencerConfig, StateUpdaterConfig, }; pub use sequencer::start_l2; diff --git a/crates/l2/proto/sidecar.proto b/crates/l2/proto/sidecar.proto new file mode 100644 index 00000000000..c05857184f8 --- /dev/null +++ b/crates/l2/proto/sidecar.proto @@ -0,0 +1,198 @@ +syntax = "proto3"; + +package sidecar.transport.v1; + +// Block environment for EVM execution context. +// All numeric types that exceed 64 bits are encoded as big-endian bytes. +message BlockEnv { + bytes number = 1; // 32 bytes - block number as U256 big-endian + bytes beneficiary = 2; // 20 bytes - coinbase address + bytes timestamp = 3; // 32 bytes - timestamp as U256 big-endian + uint64 gas_limit = 4; // block gas limit + uint64 basefee = 5; // base fee per gas (u64) + bytes difficulty = 6; // 32 bytes - difficulty as U256 big-endian + optional bytes prevrandao = 7; // 32 bytes - prevrandao hash + optional BlobExcessGasAndPrice blob_excess_gas_and_price = 8; +} + +// EIP-4844 blob gas pricing information. +message BlobExcessGasAndPrice { + uint64 excess_blob_gas = 1; // excess blob gas + bytes blob_gasprice = 2; // 16 bytes - blob gas price as u128 big-endian +} + +// Generic acknowledgment response. +message BasicAck { + bool accepted = 1; + string message = 2; +} + +// Access list item for EIP-2930 transactions. +message AccessListItem { + bytes address = 1; // 20 bytes - account address + repeated bytes storage_keys = 2; // 32 bytes each - storage slot keys +} + +// Authorization for EIP-7702 (account abstraction). +message Authorization { + bytes chain_id = 1; // 32 bytes - chain ID as U256 big-endian + bytes address = 2; // 20 bytes - authorized address + uint64 nonce = 3; // authorization nonce + uint32 y_parity = 4; // signature y-parity (0 or 1) + bytes r = 5; // 32 bytes - signature r as U256 big-endian + bytes s = 6; // 32 bytes - signature s as U256 big-endian +} + +// Transaction environment equivalent to revm's TxEnv. +// Uses raw bytes for all hash/address/numeric fields to avoid hex encoding overhead. +message TransactionEnv { + uint32 tx_type = 1; // transaction type (0=legacy, 1=eip2930, 2=eip1559, etc.) + bytes caller = 2; // 20 bytes - sender address + uint64 gas_limit = 3; // gas limit + bytes gas_price = 4; // 16 bytes - gas price as u128 big-endian + bytes transact_to = 5; // 20 bytes or empty for create + bytes value = 6; // 32 bytes - transaction value as U256 big-endian + bytes data = 7; // calldata bytes + uint64 nonce = 8; // transaction nonce + optional uint64 chain_id = 9; // chain ID (optional for legacy) + repeated AccessListItem access_list = 10; // EIP-2930 access list + optional bytes gas_priority_fee = 11; // 16 bytes - priority fee as u128 big-endian (optional) + repeated bytes blob_hashes = 12; // 32 bytes each - EIP-4844 blob versioned hashes + bytes max_fee_per_blob_gas = 13; // 16 bytes - max fee per blob gas as u128 big-endian + repeated Authorization authorization_list = 14; // EIP-7702 authorization list +} + +// Unique identifier for a transaction execution within a block iteration. +message TxExecutionId { + bytes block_number = 1; // 32 bytes - block number as U256 big-endian + uint64 iteration_id = 2; // iteration ID within the block + bytes tx_hash = 3; // 32 bytes - transaction hash + uint64 index = 4; // transaction index within iteration +} + +// Transaction with execution context. +message Transaction { + TxExecutionId tx_execution_id = 1; // TX execution ID + TransactionEnv tx_env = 2; // transaction environment + optional bytes prev_tx_hash = 3; // 32 bytes - previous TX hash for ordering +} + +// Commit head event - signals the start of a new block building round. +message CommitHead { + optional bytes last_tx_hash = 1; // 32 bytes - last TX hash (None means absent) + uint64 n_transactions = 2; // number of transactions in previous iteration + bytes block_number = 3; // 32 bytes - block number as U256 big-endian + uint64 selected_iteration_id = 4; // selected iteration ID + optional bytes block_hash = 5; // 32 bytes - block hash for EIP-2935 + optional bytes parent_beacon_block_root = 6; // 32 bytes - parent beacon block root for EIP-4788 + bytes timestamp = 7; // 32 bytes - timestamp as U256 big-endian +} + +// New iteration event - initializes building for an iteration ID. +// Contains all data needed to apply system calls as EIP-4788, EIP-2935 before transaction execution. +message NewIteration { + BlockEnv block_env = 1; // block environment + uint64 iteration_id = 2; // iteration ID + optional bytes parent_block_hash = 3; // 32 bytes - parent block hash for EIP-2935 + optional bytes parent_beacon_block_root = 4; // 32 bytes - parent beacon block root for EIP-4788 +} + +// Reorg event - signals a chain reorganization. +message ReorgEvent { + TxExecutionId tx_execution_id = 1; // TX execution ID to reorg from + repeated bytes tx_hashes = 2; // Transaction hashes to reorg (oldest -> newest) +} + +// Unified event wrapper for streaming. +// Each event includes a event_id for matching with the corresponding StreamAck. +message Event { + uint64 event_id = 1; // client-provided ID for request-response matching + oneof event { + CommitHead commit_head = 2; + NewIteration new_iteration = 3; + Transaction transaction = 4; + ReorgEvent reorg = 5; + } +} + +// Stream acknowledgment - sent for each event processed. +// The event_id matches the event_id from the corresponding Event. +message StreamAck { + bool success = 1; // whether processing succeeded + string message = 2; // info/error message + uint64 events_processed = 3; // total events processed so far + uint64 event_id = 4; // matches the event_id from the Event +} + +// Transaction execution result status. +enum ResultStatus { + RESULT_STATUS_UNSPECIFIED = 0; + RESULT_STATUS_SUCCESS = 1; // transaction executed successfully + RESULT_STATUS_REVERTED = 2; // transaction reverted + RESULT_STATUS_HALTED = 3; // transaction halted (out of gas, etc.) + RESULT_STATUS_FAILED = 4; // validation failed + RESULT_STATUS_ASSERTION_FAILED = 5; // assertion validation failed +} + +// Transaction execution result. +message TransactionResult { + TxExecutionId tx_execution_id = 1; // TX execution ID + ResultStatus status = 2; // execution status + uint64 gas_used = 3; // gas consumed (0 when unknown) + string error = 4; // error message (empty when none) +} + +// Request to subscribe to transaction results stream. +message SubscribeResultsRequest { + optional bytes from_block = 1; // 32 bytes - filter by starting block number as U256 big-endian +} + +// Request for multiple transaction results (kept for compatibility with unary queries). +message GetTransactionsRequest { + repeated TxExecutionId tx_execution_ids = 1; // TX execution IDs to query +} + +// Response containing multiple transaction results. +message GetTransactionsResponse { + repeated TransactionResult results = 1; // found results + repeated bytes not_found = 2; // 32-byte hashes not found +} + +// Request for a single transaction result. +message GetTransactionRequest { + TxExecutionId tx_execution_id = 1; // TX execution ID +} + +// Response for a single transaction result. +message GetTransactionResponse { + oneof outcome { + TransactionResult result = 1; // transaction result if found + bytes not_found = 2; // 32-byte hash if not found + } +} + +// Sidecar transport service. +// +// Streaming RPCs: +// - StreamEvents: Bidirectional stream for sending events +// - SubscribeResults: Server stream for receiving transaction results as they complete +// +// Unary RPCs (kept for simple queries): +// - GetTransactions: Query multiple transaction results +// - GetTransaction: Query a single transaction result +service SidecarTransport { + // Bidirectional stream: client sends events, server sends periodic acks. + // Events must start with CommitHead before any other event type. + // Each ack includes the event_id from the corresponding event for explicit matching. + rpc StreamEvents(stream Event) returns (stream StreamAck); + + // Server stream: subscribe to transaction results as they complete. + // Results are pushed immediately when transactions finish executing. + rpc SubscribeResults(SubscribeResultsRequest) returns (stream TransactionResult); + + // Query multiple transaction results by their execution IDs. + rpc GetTransactions(GetTransactionsRequest) returns (GetTransactionsResponse); + + // Query a single transaction result by its execution ID. + rpc GetTransaction(GetTransactionRequest) returns (GetTransactionResponse); +} diff --git a/crates/l2/sequencer/block_producer.rs b/crates/l2/sequencer/block_producer.rs index 686596b6375..32810060164 100644 --- a/crates/l2/sequencer/block_producer.rs +++ b/crates/l2/sequencer/block_producer.rs @@ -38,6 +38,8 @@ use ethrex_l2_common::sequencer_state::{SequencerState, SequencerStatus}; use std::str::FromStr; +use super::credible_layer::CredibleLayerClient; +use super::credible_layer::client::CredibleLayerProtocol; use super::errors::BlockProducerError; use ethrex_metrics::metrics; @@ -64,6 +66,7 @@ pub struct BlockProducer { block_gas_limit: u64, eth_client: EthClient, router_address: Address, + credible_layer: Option>, /// Actor handle for sending new block headers to WS subscribers. subscription_manager: Option>, } @@ -87,6 +90,7 @@ impl BlockProducer { sequencer_state: SequencerState, router_address: Address, l2_gas_limit: u64, + credible_layer: Option>, subscription_manager: Option>, ) -> Result { let BlockProducerConfig { @@ -126,6 +130,7 @@ impl BlockProducer { block_gas_limit: l2_gas_limit, eth_client, router_address, + credible_layer, subscription_manager, }) } @@ -162,6 +167,11 @@ impl BlockProducer { }; let payload = create_payload(&args, &self.store, Bytes::new())?; + // Credible Layer: send NewIteration before building the block. + if let Some(ref cl) = self.credible_layer { + let _ = cl.new_iteration(payload.header.clone()); + } + let registered_chains = self.get_registered_l2_chain_ids().await?; // Blockchain builds the payload from mempool txs and executes them @@ -172,6 +182,7 @@ impl BlockProducer { &mut self.privileged_nonces, self.block_gas_limit, registered_chains, + self.credible_layer.clone(), ) .await?; info!( @@ -204,6 +215,7 @@ impl BlockProducer { .ok_or(ChainError::ParentStateNotFound)?; let transactions_count = block.body.transactions.len(); + let last_tx_hash = block.body.transactions.last().map(|tx| tx.hash()); let block_number = block.header.number; let block_hash = block.hash(); // Save the header for newHeads notifications before block is moved into store_block. @@ -224,6 +236,20 @@ impl BlockProducer { // Make the new head be part of the canonical chain apply_fork_choice(&self.store, block_hash, block_hash, block_hash).await?; + // Credible Layer: send CommitHead after block is stored + if let Some(ref cl) = self.credible_layer { + let tx_count: u64 = transactions_count + .try_into() + .map_err(|_| BlockProducerError::Custom("tx count overflow".into()))?; + let _ = cl.commit_head( + block_number, + block_hash, + block_header.timestamp, + tx_count, + last_tx_hash, + ); + } + // Notify all eth_subscribe("newHeads") subscribers. if let Some(ref manager) = self.subscription_manager { let _ = manager.new_head(block_header); @@ -312,6 +338,7 @@ impl BlockProducer { sequencer_state: SequencerState, router_address: Address, l2_gas_limit: u64, + credible_layer: Option>, subscription_manager: Option>, ) -> Result, BlockProducerError> { let block_producer = Self::new( @@ -323,6 +350,7 @@ impl BlockProducer { sequencer_state, router_address, l2_gas_limit, + credible_layer, subscription_manager, )?; let actor_ref = block_producer.start_with_backend(Backend::Blocking); diff --git a/crates/l2/sequencer/block_producer/payload_builder.rs b/crates/l2/sequencer/block_producer/payload_builder.rs index ff2de93ba42..1eb89aea9c5 100644 --- a/crates/l2/sequencer/block_producer/payload_builder.rs +++ b/crates/l2/sequencer/block_producer/payload_builder.rs @@ -1,3 +1,5 @@ +use crate::sequencer::credible_layer::CredibleLayerClient; +use crate::sequencer::credible_layer::client::CredibleLayerProtocol; use crate::sequencer::errors::BlockProducerError; use ethrex_blockchain::{ Blockchain, @@ -20,6 +22,7 @@ use ethrex_metrics::{ }; use ethrex_rlp::encode::RLPEncode; use ethrex_storage::Store; +use spawned_concurrency::tasks::ActorRef; use std::sync::Arc; use std::{collections::HashMap, ops::Div}; use tokio::time::Instant; @@ -35,6 +38,7 @@ pub async fn build_payload( privileged_nonces: &mut HashMap>, block_gas_limit: u64, registered_chains: Vec, + credible_layer: Option>, ) -> Result { let since = Instant::now(); let gas_limit = payload.header.gas_limit; @@ -49,6 +53,7 @@ pub async fn build_payload( privileged_nonces, block_gas_limit, registered_chains, + credible_layer, ) .await?; blockchain.finalize_payload(&mut context)?; @@ -100,6 +105,7 @@ pub async fn fill_transactions( privileged_nonces: &mut HashMap>, configured_block_gas_limit: u64, registered_chains: Vec, + credible_layer: Option>, ) -> Result<(), BlockProducerError> { let mut privileged_tx_count = 0; let VMType::L2(fee_config) = context.vm.vm_type else { @@ -209,6 +215,29 @@ pub async fn fill_transactions( continue; } + // Credible Layer: send transaction event to the sidecar before execution. + // TODO: Privileged transactions (L1->L2 deposits) currently bypass the Credible Layer + // check entirely. This should be revisited — the sidecar should be aware of all + // transactions for accurate state tracking, even if privileged txs are never dropped. + if let Some(ref cl) = credible_layer + && !head_tx.is_privileged() + { + let tx_index: u64 = context + .payload + .body + .transactions + .len() + .try_into() + .map_err(|_| BlockProducerError::Custom("tx index overflow".into()))?; + let _ = cl.send_transaction( + tx_hash, + context.block_number(), + tx_index, + head_tx.tx.sender(), + tx.clone(), + ); + } + // Set BAL index for this transaction (1-indexed per EIP-7928) #[allow(clippy::cast_possible_truncation, clippy::as_conversions)] let tx_index = (context.payload.body.transactions.len() + 1) as u16; @@ -237,6 +266,34 @@ pub async fn fill_transactions( } }; + // Credible Layer: poll for the sidecar's verdict after execution. + // If the sidecar rejected the transaction, undo execution and drop it. + if let Some(ref cl) = credible_layer + && !head_tx.is_privileged() + { + let check_tx_index: u64 = context + .payload + .body + .transactions + .len() + .try_into() + .map_err(|_| BlockProducerError::Custom("tx index overflow".into()))?; + let include = cl + .check_transaction(tx_hash, context.block_number(), check_tx_index) + .await + .unwrap_or(true); + if !include { + debug!("Credible layer rejected transaction: {tx_hash:#x}"); + txs.pop(); + context.vm.undo_last_tx()?; + context.remaining_gas = previous_remaining_gas; + context.block_value = previous_block_value; + context.cumulative_gas_spent = previous_cumulative_gas_spent; + blockchain.remove_transaction_from_pool(&tx_hash)?; + continue; + } + } + let l2_messages = get_block_l2_out_messages(std::slice::from_ref(&receipt), chain_id); let mut found_invalid_message = false; for msg in l2_messages { diff --git a/crates/l2/sequencer/configs.rs b/crates/l2/sequencer/configs.rs index 66deab3bcbf..58ad1fa78df 100644 --- a/crates/l2/sequencer/configs.rs +++ b/crates/l2/sequencer/configs.rs @@ -17,6 +17,15 @@ pub struct SequencerConfig { pub monitor: MonitorConfig, pub admin_server: AdminConfig, pub state_updater: StateUpdaterConfig, + pub credible_layer: CredibleLayerConfig, +} + +/// Configuration for the Credible Layer sidecar integration. +/// URL is optional; if absent, the feature is disabled. +#[derive(Clone, Debug, Default)] +pub struct CredibleLayerConfig { + /// gRPC endpoint for the Credible Layer Assertion Enforcer sidecar. + pub sidecar_url: Option, } // TODO: Move to blockchain/dev diff --git a/crates/l2/sequencer/credible_layer/client.rs b/crates/l2/sequencer/credible_layer/client.rs new file mode 100644 index 00000000000..61571698823 --- /dev/null +++ b/crates/l2/sequencer/credible_layer/client.rs @@ -0,0 +1,401 @@ +use std::time::Duration; + +use ethrex_common::types::{BlockHeader, Transaction}; +use ethrex_common::{Address, H256}; +use spawned_concurrency::{ + actor, + error::ActorError, + protocol, + tasks::{ + Actor, ActorRef, ActorStart as _, Context, Handler, Response, send_after, spawn_listener, + }, +}; +use tokio::sync::mpsc; +use tokio_stream::StreamExt; +use tonic::transport::Channel; +use tracing::{debug, info, warn}; + +use super::errors::CredibleLayerError; +use super::sidecar_proto::{ + self, BlobExcessGasAndPrice, BlockEnv, CommitHead, Event, GetTransactionRequest, NewIteration, + ResultStatus, Transaction as SidecarTransaction, TxExecutionId, + sidecar_transport_client::SidecarTransportClient, +}; + +#[protocol] +pub trait CredibleLayerProtocol: Send + Sync { + /// Notify the sidecar that a new block iteration has started. + fn new_iteration(&self, header: BlockHeader) -> Result<(), ActorError>; + + /// Notify the sidecar that a block has been committed. + fn commit_head( + &self, + block_number: u64, + block_hash: H256, + timestamp: u64, + tx_count: u64, + last_tx_hash: Option, + ) -> Result<(), ActorError>; + + /// Send a transaction event to the sidecar (pre-execution, fire-and-forget). + fn send_transaction( + &self, + tx_hash: H256, + block_number: u64, + tx_index: u64, + sender: Address, + tx: Transaction, + ) -> Result<(), ActorError>; + + /// Poll the sidecar for a transaction verdict (post-execution). + /// Returns `true` if the transaction should be included, `false` if it should be dropped. + /// On any error or timeout, returns `true` (permissive — liveness over safety). + fn check_transaction(&self, tx_hash: H256, block_number: u64, tx_index: u64) -> Response; + + /// Attempt to (re)connect to the sidecar. Scheduled by `send_after` on failure. + fn reconnect(&self) -> Result<(), ActorError>; + + /// Process a stream ack from the sidecar. When `disconnected` is true, + /// the stream has ended and a reconnect is scheduled. + fn stream_ack( + &self, + disconnected: bool, + success: bool, + event_id: u64, + message: String, + ) -> Result<(), ActorError>; +} + +/// gRPC client actor for communicating with the Credible Layer Assertion Enforcer sidecar. +/// +/// Maintains a persistent bidirectional `StreamEvents` gRPC stream. Connection is +/// established in `#[started]` and ack messages are bridged into the actor via +/// `spawn_listener`. Reconnection on failure is scheduled with `send_after`. +pub struct CredibleLayerClient { + /// Write handle for the gRPC StreamEvents bidirectional stream (Rust equivalent + /// of Java gRPC's `StreamObserver.onNext()`). None when disconnected. + sidecar_tx: Option>, + /// Monotonically increasing event ID counter + event_id_counter: u64, + /// Current iteration ID (incremented per block) + iteration_id: u64, + /// gRPC client for sidecar RPCs (StreamEvents, GetTransaction). + sidecar_client: SidecarTransportClient, + /// Whether the StreamEvents stream is currently active. + stream_connected: bool, +} + +#[actor(protocol = CredibleLayerProtocol)] +impl CredibleLayerClient { + /// Spawn the Credible Layer client actor. + pub async fn spawn(sidecar_url: String) -> Result, Box> { + let client = Self::new(sidecar_url)?; + Ok(client.start()) + } + + fn new(sidecar_url: String) -> Result> { + info!(url = %sidecar_url, "Configuring Credible Layer sidecar client"); + + let channel = Channel::from_shared(sidecar_url) + .map_err(|e| CredibleLayerError::Internal(format!("Invalid URL: {e}")))? + .connect_timeout(Duration::from_secs(5)) + .timeout(Duration::from_secs(5)) + .connect_lazy(); + + Ok(Self { + sidecar_client: SidecarTransportClient::new(channel), + sidecar_tx: None, + event_id_counter: 1, + iteration_id: 0, + stream_connected: false, + }) + } + + /// Attempt to establish a bidirectional StreamEvents connection with the sidecar. + /// On success, stores the event sender and spawns an ack listener. + /// On failure, schedules a retry via `send_after`. + async fn open_event_stream(&mut self, ctx: &Context) { + let (tx, rx) = mpsc::channel::(64); + let grpc_stream = tokio_stream::wrappers::ReceiverStream::new(rx); + + match self.sidecar_client.stream_events(grpc_stream).await { + Ok(response) => { + info!("StreamEvents stream stream_connected to sidecar"); + + // The sidecar requires CommitHead as the first event on every new stream. + let init_commit = Event { + event_id: 0, + event: Some(sidecar_proto::event::Event::CommitHead(CommitHead { + last_tx_hash: None, + n_transactions: 0, + block_number: vec![0u8; 32], + selected_iteration_id: 0, + block_hash: Some(vec![0u8; 32]), + parent_beacon_block_root: None, + timestamp: vec![0u8; 32], + })), + }; + if tx.send(init_commit).await.is_err() { + warn!("Failed to send initial CommitHead, scheduling reconnect"); + send_after( + Duration::from_secs(5), + ctx.clone(), + credible_layer_protocol::Reconnect, + ); + return; + } + + self.sidecar_tx = Some(tx); + self.stream_connected = true; + + // Bridge the ack stream into actor messages via spawn_listener. + // When the stream ends or errors, a final "disconnected" message is sent. + let ack_stream = response.into_inner(); + let mapped = ack_stream + .map(|result| match result { + Ok(ack) => credible_layer_protocol::StreamAck { + disconnected: false, + success: ack.success, + event_id: ack.event_id, + message: ack.message, + }, + Err(status) => credible_layer_protocol::StreamAck { + disconnected: true, + success: false, + event_id: 0, + message: status.to_string(), + }, + }) + .chain(tokio_stream::once(credible_layer_protocol::StreamAck { + disconnected: true, + success: false, + event_id: 0, + message: "ack stream ended".into(), + })); + + spawn_listener(ctx.clone(), mapped); + } + Err(status) => { + warn!(%status, "StreamEvents connect failed, retrying in 5s"); + send_after( + Duration::from_secs(5), + ctx.clone(), + credible_layer_protocol::Reconnect, + ); + } + } + } + + fn next_event_id(&mut self) -> u64 { + let id = self.event_id_counter; + self.event_id_counter += 1; + id + } + + /// Send an event on the active gRPC stream. If the channel is closed, + /// marks the connection as disconnected. + async fn send_event(&mut self, event_payload: sidecar_proto::event::Event) { + let event = Event { + event_id: self.next_event_id(), + event: Some(event_payload), + }; + if let Some(ref sender) = self.sidecar_tx + && sender.send(event).await.is_err() + { + warn!("Event channel closed, marking disconnected"); + self.stream_connected = false; + self.sidecar_tx = None; + } + } + + #[started] + async fn started(&mut self, ctx: &Context) { + self.open_event_stream(ctx).await; + } + + #[send_handler] + async fn handle_reconnect( + &mut self, + _msg: credible_layer_protocol::Reconnect, + ctx: &Context, + ) { + if self.stream_connected { + return; + } + self.open_event_stream(ctx).await; + } + + #[send_handler] + async fn handle_stream_ack( + &mut self, + msg: credible_layer_protocol::StreamAck, + ctx: &Context, + ) { + if msg.disconnected { + if !self.stream_connected { + return; + } + info!("Sidecar stream disconnected: {}", msg.message); + self.stream_connected = false; + self.sidecar_tx = None; + send_after( + Duration::from_secs(5), + ctx.clone(), + credible_layer_protocol::Reconnect, + ); + } else if !msg.success { + warn!( + event_id = msg.event_id, + msg = %msg.message, + "Sidecar rejected event" + ); + } + } + + #[send_handler] + async fn handle_new_iteration( + &mut self, + msg: credible_layer_protocol::NewIteration, + _ctx: &Context, + ) { + self.iteration_id += 1; + let header = &msg.header; + + let block_env = BlockEnv { + number: u64_to_u256_bytes(header.number), + beneficiary: header.coinbase.as_bytes().to_vec(), + timestamp: u64_to_u256_bytes(header.timestamp), + gas_limit: header.gas_limit, + basefee: header.base_fee_per_gas.unwrap_or(0), + difficulty: header.difficulty.to_big_endian().to_vec(), + prevrandao: Some(header.prev_randao.to_fixed_bytes().to_vec()), + blob_excess_gas_and_price: Some(BlobExcessGasAndPrice { + excess_blob_gas: 0, + blob_gasprice: vec![0u8; 16], + }), + }; + let new_iteration = NewIteration { + block_env: Some(block_env), + iteration_id: self.iteration_id, + parent_block_hash: Some(header.parent_hash.to_fixed_bytes().to_vec()), + parent_beacon_block_root: header + .parent_beacon_block_root + .map(|h| h.to_fixed_bytes().to_vec()), + }; + self.send_event(sidecar_proto::event::Event::NewIteration(new_iteration)) + .await; + } + + #[send_handler] + async fn handle_commit_head( + &mut self, + msg: credible_layer_protocol::CommitHead, + _ctx: &Context, + ) { + let commit_head = CommitHead { + last_tx_hash: msg.last_tx_hash.map(|h| h.to_fixed_bytes().to_vec()), + n_transactions: msg.tx_count, + block_number: u64_to_u256_bytes(msg.block_number), + selected_iteration_id: self.iteration_id, + block_hash: Some(msg.block_hash.to_fixed_bytes().to_vec()), + parent_beacon_block_root: None, + timestamp: u64_to_u256_bytes(msg.timestamp), + }; + self.send_event(sidecar_proto::event::Event::CommitHead(commit_head)) + .await; + } + + #[send_handler] + async fn handle_send_transaction( + &mut self, + msg: credible_layer_protocol::SendTransaction, + _ctx: &Context, + ) { + if !self.stream_connected { + return; + } + let tx_execution_id = Some(TxExecutionId { + block_number: u64_to_u256_bytes(msg.block_number), + iteration_id: self.iteration_id, + tx_hash: msg.tx_hash.as_bytes().to_vec(), + index: msg.tx_index, + }); + let tx_env = (msg.tx, msg.sender).into(); + let sidecar_tx = SidecarTransaction { + tx_execution_id, + tx_env: Some(tx_env), + prev_tx_hash: None, + }; + self.send_event(sidecar_proto::event::Event::Transaction(sidecar_tx)) + .await; + } + + #[request_handler] + async fn handle_check_transaction( + &mut self, + msg: credible_layer_protocol::CheckTransaction, + _ctx: &Context, + ) -> bool { + if !self.stream_connected { + return true; + } + + let tx_exec_id = TxExecutionId { + block_number: u64_to_u256_bytes(msg.block_number), + iteration_id: self.iteration_id, + tx_hash: msg.tx_hash.as_bytes().to_vec(), + index: msg.tx_index, + }; + + let poll_attempts = 10; + let poll_interval = Duration::from_millis(200); + let poll_timeout = Duration::from_millis(200); + + for attempt in 0..poll_attempts { + tokio::time::sleep(poll_interval).await; + let request = GetTransactionRequest { + tx_execution_id: Some(tx_exec_id.clone()), + }; + match tokio::time::timeout(poll_timeout, self.sidecar_client.get_transaction(request)) + .await + { + Ok(Ok(response)) => { + let inner = response.into_inner(); + match inner.outcome { + Some(sidecar_proto::get_transaction_response::Outcome::Result(result)) => { + return result.status() != ResultStatus::AssertionFailed; + } + Some(sidecar_proto::get_transaction_response::Outcome::NotFound(_)) => { + debug!( + "GetTransaction poll attempt {}/{}: not found yet", + attempt + 1, + poll_attempts + ); + continue; + } + None => continue, + } + } + Ok(Err(status)) => { + warn!(%status, "GetTransaction poll failed, including tx (permissive)"); + return true; + } + Err(_) => { + warn!("GetTransaction poll timed out, including tx (permissive)"); + return true; + } + } + } + warn!( + "GetTransaction: no result after {poll_attempts} attempts, including tx (permissive)" + ); + true + } +} + +/// Encode a u64 as a 32-byte big-endian U256 for protobuf fields. +fn u64_to_u256_bytes(value: u64) -> Vec { + let mut buf = [0u8; 32]; + buf[24..].copy_from_slice(&value.to_be_bytes()); + buf.to_vec() +} diff --git a/crates/l2/sequencer/credible_layer/errors.rs b/crates/l2/sequencer/credible_layer/errors.rs new file mode 100644 index 00000000000..be3d313580b --- /dev/null +++ b/crates/l2/sequencer/credible_layer/errors.rs @@ -0,0 +1,15 @@ +use tonic::Status; + +#[derive(Debug, thiserror::Error)] +pub enum CredibleLayerError { + #[error("gRPC transport error: {0}")] + Transport(#[from] tonic::transport::Error), + #[error("gRPC status error: {0}")] + Status(#[from] Status), + #[error("Stream closed unexpectedly")] + StreamClosed, + #[error("Result timeout for tx {0}")] + ResultTimeout(String), + #[error("Credible Layer error: {0}")] + Internal(String), +} diff --git a/crates/l2/sequencer/credible_layer/mod.rs b/crates/l2/sequencer/credible_layer/mod.rs new file mode 100644 index 00000000000..b533632b4b6 --- /dev/null +++ b/crates/l2/sequencer/credible_layer/mod.rs @@ -0,0 +1,93 @@ +/// This module implements a gRPC client actor that communicates with the sidecar +/// during block building. Transactions that fail assertion validation are dropped +/// before block inclusion. +/// +/// The integration is opt-in via the `--credible-layer-url` CLI flag. +/// When disabled, there is zero overhead. +pub mod client; +pub mod errors; + +pub use client::CredibleLayerClient; +pub use errors::CredibleLayerError; + +/// Generated protobuf/gRPC types for sidecar.proto +pub mod sidecar_proto { + tonic::include_proto!("sidecar.transport.v1"); +} + +// Conversions from ethrex types to sidecar protobuf types. + +use ethrex_common::Address; +use ethrex_common::types::{Transaction, TxKind}; + +impl From<(Transaction, Address)> for sidecar_proto::TransactionEnv { + fn from((tx, sender): (Transaction, Address)) -> Self { + let transact_to = match tx.to() { + TxKind::Call(addr) => addr.as_bytes().to_vec(), + TxKind::Create => vec![], + }; + + let value_bytes = tx.value().to_big_endian(); + + let mut gas_price_bytes = [0u8; 16]; + let gas_price_u128 = tx.gas_price().as_u128(); + gas_price_bytes.copy_from_slice(&gas_price_u128.to_be_bytes()); + + let gas_priority_fee = tx.max_priority_fee().map(|fee| { + let mut buf = [0u8; 16]; + buf[8..].copy_from_slice(&fee.to_be_bytes()); + buf.to_vec() + }); + + let access_list = tx + .access_list() + .iter() + .map(|(addr, keys)| sidecar_proto::AccessListItem { + address: addr.as_bytes().to_vec(), + storage_keys: keys.iter().map(|k| k.as_bytes().to_vec()).collect(), + }) + .collect(); + + let authorization_list = tx + .authorization_list() + .map(|list| { + list.iter() + .map(|auth| { + let chain_id_bytes = auth.chain_id.to_big_endian(); + let r_bytes = auth.r_signature.to_big_endian(); + let s_bytes = auth.s_signature.to_big_endian(); + sidecar_proto::Authorization { + chain_id: chain_id_bytes.to_vec(), + address: auth.address.as_bytes().to_vec(), + nonce: auth.nonce, + y_parity: u32::try_from(auth.y_parity).unwrap_or(0), + r: r_bytes.to_vec(), + s: s_bytes.to_vec(), + } + }) + .collect() + }) + .unwrap_or_default(); + + Self { + tx_type: u32::from(u8::from(tx.tx_type())), + caller: sender.as_bytes().to_vec(), + gas_limit: tx.gas_limit(), + gas_price: gas_price_bytes.to_vec(), + transact_to, + value: value_bytes.to_vec(), + data: tx.data().to_vec(), + nonce: tx.nonce(), + chain_id: tx.chain_id(), + access_list, + gas_priority_fee, + blob_hashes: tx + .blob_versioned_hashes() + .iter() + .map(|h| h.as_bytes().to_vec()) + .collect(), + max_fee_per_blob_gas: vec![0u8; 16], + authorization_list, + } + } +} diff --git a/crates/l2/sequencer/mod.rs b/crates/l2/sequencer/mod.rs index e6d1fc238a2..99239f28b64 100644 --- a/crates/l2/sequencer/mod.rs +++ b/crates/l2/sequencer/mod.rs @@ -23,7 +23,7 @@ use reqwest::Url; use spawned_concurrency::tasks::ActorRef; use std::pin::Pin; use tokio_util::sync::CancellationToken; -use tracing::{error, info}; +use tracing::{error, info, warn}; use utils::get_needed_proof_types; mod admin_server; @@ -39,6 +39,7 @@ pub use ethrex_l2_common::sequencer_state::{SequencerState, SequencerStatus}; pub mod state_updater; pub mod configs; +pub mod credible_layer; pub mod errors; pub mod setup; pub mod utils; @@ -154,6 +155,22 @@ pub async fn start_l2( .inspect_err(|err| { error!("Error starting L1 Proof Sender: {err}"); }); + let credible_layer = if let Some(url) = cfg.credible_layer.sidecar_url.clone() { + match credible_layer::CredibleLayerClient::spawn(url).await { + Ok(actor_ref) => { + info!("Credible Layer sidecar client started"); + Some(actor_ref) + } + Err(e) => { + warn!( + "Failed to start Credible Layer client: {e}. Proceeding without credible layer." + ); + None + } + } + } else { + None + }; let block_producer = BlockProducer::spawn( store.clone(), rollup_store.clone(), @@ -162,6 +179,7 @@ pub async fn start_l2( shared_state.clone(), cfg.l1_watcher.router_address, l2_gas_limit, + credible_layer, subscription_manager, ) .await diff --git a/crates/l2/tee/quote-gen/Cargo.lock b/crates/l2/tee/quote-gen/Cargo.lock index 6fabeeb4efd..798a0bc8b9a 100644 --- a/crates/l2/tee/quote-gen/Cargo.lock +++ b/crates/l2/tee/quote-gen/Cargo.lock @@ -1311,6 +1311,7 @@ dependencies = [ "ethrex-crypto", "ethrex-levm", "ethrex-rlp", + "hex", "rayon", "rustc-hash", "serde 1.0.228", diff --git a/crates/networking/rpc/eth/logs.rs b/crates/networking/rpc/eth/logs.rs index 2601f0533a5..96985319adb 100644 --- a/crates/networking/rpc/eth/logs.rs +++ b/crates/networking/rpc/eth/logs.rs @@ -80,15 +80,15 @@ impl RpcHandler for LogsFilter { }) .transpose()? .flatten(); - let topics_filters = param - .get("topics") - .ok_or_else(|| RpcErr::MissingParam("topics".to_string())) - .and_then(|topics| { + let topics_filters = match param.get("topics") { + Some(topics) => { match serde_json::from_value::>>(topics.clone()) { - Ok(filters) => Ok(filters), - _ => Err(RpcErr::WrongParam("topics".to_string())), + Ok(filters) => filters, + _ => return Err(RpcErr::WrongParam("topics".to_string())), } - })?; + } + None => None, + }; Ok(LogsFilter { from_block, to_block, diff --git a/crates/vm/levm/bench/revm_comparison/Cargo.lock b/crates/vm/levm/bench/revm_comparison/Cargo.lock index 2347fa6f1bf..d9ef6447d28 100644 --- a/crates/vm/levm/bench/revm_comparison/Cargo.lock +++ b/crates/vm/levm/bench/revm_comparison/Cargo.lock @@ -1147,6 +1147,7 @@ dependencies = [ "ethrex-crypto", "ethrex-levm", "ethrex-rlp", + "hex", "rayon", "rustc-hash", "serde", diff --git a/crates/vm/levm/src/hooks/l2_hook.rs b/crates/vm/levm/src/hooks/l2_hook.rs index 7fc14fa117d..57541261b95 100644 --- a/crates/vm/levm/src/hooks/l2_hook.rs +++ b/crates/vm/levm/src/hooks/l2_hook.rs @@ -146,9 +146,9 @@ fn finalize_non_privileged_execution( // EIP-7778: pre-refund gas for block accounting let total_gas_pre_refund = ctx_result.gas_used; - // Clear the backup so that Phase 2's rollback only undoes mutations - // from apply_finalize_mutations, not the gas-overuse revert above. - vm.current_call_frame.call_frame_backup.clear(); + // Save the execution backup (contains storage slot backups from SSTORE, etc.) + // before clearing, so BackupHook can include it in the tx-level undo snapshot. + let execution_backup = std::mem::take(&mut vm.current_call_frame.call_frame_backup); // === Phase 1: Fallible computations (no state mutations) === // Perform contract calls and conversions that can fail BEFORE any @@ -176,6 +176,7 @@ fn finalize_non_privileged_execution( // Mutations record original values in call_frame_backup via // backup_account_info / backup_storage_slot. If any step fails, // we restore the cache to undo all partial mutations. + // The call_frame_backup is empty here so rollback only undoes Phase 2. let result = apply_finalize_mutations( vm, ctx_result, @@ -199,6 +200,12 @@ fn finalize_non_privileged_execution( return Err(e); } + // Merge the saved execution backup back so BackupHook can capture + // both execution-time and finalize-time state for tx-level undo. + vm.current_call_frame + .call_frame_backup + .extend(execution_backup); + Ok(()) } diff --git a/docs/l2/credible_layer.md b/docs/l2/credible_layer.md new file mode 100644 index 00000000000..4dfbcf454db --- /dev/null +++ b/docs/l2/credible_layer.md @@ -0,0 +1,456 @@ +# Credible Layer Integration + +## Overview + +ethrex L2 integrates with the [Credible Layer](https://docs.phylax.systems/credible/credible-introduction) — a pre-execution security infrastructure by Phylax Systems that validates transactions against on-chain assertions before block inclusion. Transactions that violate an assertion are silently dropped before they land on-chain. + +### Architecture + +![Credible Layer Architecture](img/credible_layer_architecture.svg) + +### Key Concepts + +- **Assertion**: A Solidity contract that defines a security invariant (e.g., "ownership of contract X must not change"). Written using the [`credible-std`](https://github.com/phylaxsystems/credible-std) library. +- **Assertion Enforcer (Sidecar)**: A separate process that receives candidate transactions from the block builder, simulates them, runs applicable assertions through the PhEVM, and returns pass/fail verdicts. +- **State Oracle**: An on-chain contract that maps protected contracts to their assertions. +- **Permissive on failure**: If the sidecar is unreachable or times out, transactions are included anyway. Liveness is prioritized over safety. + +### Communication Protocol + +ethrex communicates with the sidecar via **gRPC** using the protocol defined in [`sidecar.proto`](../../crates/l2/proto/sidecar.proto): + +| RPC | Type | Purpose | +|-----|------|---------| +| `StreamEvents` | Bidirectional stream | Send block lifecycle events (CommitHead, NewIteration, Transaction) | +| `SubscribeResults` | Server stream | Receive transaction verdicts as they complete | +| `GetTransaction` | Unary | Poll for a single transaction result | + +> **Note:** `SubscribeResults` is not yet consumed by ethrex. Transaction verdicts are currently obtained by polling `GetTransaction`. A future improvement should subscribe to `SubscribeResults` as the primary mechanism and use `GetTransaction` only as a fallback. + +### Block Building Flow + +Per block: + +1. **CommitHead** → sidecar (previous block finalized, with block hash and tx count) +2. **NewIteration** → sidecar (new block env: number, timestamp, coinbase, gas limit, basefee) +3. For each candidate transaction: + - **Transaction** → sidecar (pre-execution: tx data sent via StreamEvents) + - Execute the transaction in the local EVM + - ← **GetTransaction** poll (post-execution: fetch the sidecar's verdict) + - If `ASSERTION_FAILED`: undo execution and drop the transaction + - Otherwise: keep it in the block + +Privileged transactions (L1→L2 deposits) bypass the Credible Layer in this first version of the integration. + +--- + +## Implementation Details + +### Key Files + +| File | Role | +|------|------| +| `crates/l2/sequencer/credible_layer/mod.rs` | Module root, proto imports | +| `crates/l2/sequencer/credible_layer/client.rs` | `CredibleLayerClient` — GenServer actor wrapping the gRPC sidecar client (StreamEvents, GetTransaction) | +| `crates/l2/sequencer/credible_layer/errors.rs` | Error types | +| `crates/l2/proto/sidecar.proto` | Sidecar gRPC protocol definition | +| `crates/l2/sequencer/block_producer.rs` | Block producer — sends CommitHead + NewIteration | +| `crates/l2/sequencer/block_producer/payload_builder.rs` | Transaction selection — sends Transaction events | + +### Configuration + +CLI flags: + +| Flag | Description | Default | +|------|-------------|---------| +| `--credible-layer-url` | gRPC endpoint for the sidecar. Passing this flag enables the integration. | (none) | + +When `--credible-layer-url` is not set, Credible Layer is completely disabled with zero overhead. + +### Sidecar Requirements + +The sidecar connects to ethrex L2 and needs: + +| Endpoint | What it does | +|----------|--------------| +| HTTP RPC (`:1729`) | `eth_blockNumber`, `eth_getBlockByNumber`, `debug_traceBlockByNumber` | +| WebSocket (`:1730`) | `eth_subscribe("newHeads")` for live block tracking | + +The `debug_traceBlockByNumber` with `prestateTracer` in diff mode is particularly important — the sidecar uses it to build its local state database. + +--- + +## How to Run (End-to-End) + +This is a step-by-step guide to run the full Credible Layer stack locally, deploy an assertion, and verify that violating transactions are dropped. All steps have been tested and verified. + +### Prerequisites + +- [Foundry](https://book.getfoundry.sh/getting-started/installation) (`forge`) +- Docker (running) +- Node.js >= 22 and `pnpm` (for the assertion indexer) +- ethrex built (including contract compilation): + ```bash + COMPILE_CONTRACTS=true cargo build --release -p ethrex --features l2 + ``` + +### Step 1: Start ethrex L1 + +```bash +cd crates/l2 +rm -rf dev_ethrex_l1 dev_ethrex_l2 + +../../target/release/ethrex \ + --network ../../fixtures/genesis/l1.json \ + --http.port 8545 --http.addr 0.0.0.0 \ + --authrpc.port 8551 --dev \ + --datadir dev_ethrex_l1 & + +# Verify (wait ~10s — initial "payload_id is None" errors are normal and resolve themselves) +sleep 10 +rex block-number --rpc-url http://localhost:8545 +``` + +> **Note:** You may see `ERROR Failed to produce block: payload_id is None in ForkChoiceResponse` in the first few seconds. This is a known L1 dev mode timing issue — the engine API needs a moment to initialize. The errors stop after a few blocks and block production continues normally. + +### Step 2: Deploy L2 contracts on L1 + +```bash +../../target/release/ethrex l2 deploy \ + --eth-rpc-url http://localhost:8545 \ + --private-key 0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924 \ + --on-chain-proposer-owner 0x4417092b70a3e5f10dc504d0947dd256b965fc62 \ + --bridge-owner 0x4417092b70a3e5f10dc504d0947dd256b965fc62 \ + --bridge-owner-pk 0x941e103320615d394a55708be13e45994c7d93b932b064dbcb2b511fe3254e2e \ + --deposit-rich \ + --private-keys-file-path ../../fixtures/keys/private_keys_l1.txt \ + --genesis-l1-path ../../fixtures/genesis/l1.json \ + --genesis-l2-path ../../fixtures/genesis/l2.json +``` + +This uses the pre-built binary directly (no recompilation). Addresses are written to `cmd/.env`. + +### Step 3: Start ethrex L2 with Credible Layer + +```bash +export $(cat ../../cmd/.env | xargs) + +RUST_LOG=info ../../target/release/ethrex l2 \ + --no-monitor \ + --watcher.block-delay 0 \ + --network ../../fixtures/genesis/l2.json \ + --http.port 1729 --http.addr 0.0.0.0 \ + --datadir dev_ethrex_l2 \ + --l1.bridge-address $ETHREX_WATCHER_BRIDGE_ADDRESS \ + --l1.on-chain-proposer-address $ETHREX_COMMITTER_ON_CHAIN_PROPOSER_ADDRESS \ + --eth.rpc-url http://localhost:8545 \ + --block-producer.coinbase-address 0x0007a881CD95B1484fca47615B64803dad620C8d \ + --committer.l1-private-key 0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924 \ + --proof-coordinator.l1-private-key 0x39725efee3fb28614de3bacaffe4cc4bd8c436257e2c8bb887c4b5c4be45e76d \ + --credible-layer-url http://localhost:50051 \ + --ws.enabled --ws.port 1730 & + +# Verify (wait ~10s for L2 to start) +rex block-number --rpc-url http://localhost:1729 +``` + +The L2 will log `StreamEvents connect failed, retrying in 5s` until the sidecar starts. This is expected — the L2 is permissive and keeps producing blocks. + +### Step 4: Deploy the State Oracle on L2 + +The DA prover address must match the private key used by assertion-da (`0xdd7e619d...` → `0xb0d60c09103F4a5c04EE8537A22ECD6a34382B36`). + +```bash +git clone https://github.com/phylaxsystems/credible-layer-contracts /tmp/credible-layer-contracts +cd /tmp/credible-layer-contracts +forge install + +PK=0xbcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31 + +STATE_ORACLE_MAX_ASSERTIONS_PER_AA=100 \ +STATE_ORACLE_ASSERTION_TIMELOCK_BLOCKS=1 \ +STATE_ORACLE_ADMIN_ADDRESS=$(rex address --private-key $PK) \ +DA_PROVER_ADDRESS=0xb0d60c09103F4a5c04EE8537A22ECD6a34382B36 \ +DEPLOY_ADMIN_VERIFIER_OWNER=true \ +DEPLOY_ADMIN_VERIFIER_WHITELIST=false \ +ADMIN_VERIFIER_WHITELIST_ADMIN_ADDRESS=0x0000000000000000000000000000000000000001 \ +forge script script/DeployCore.s.sol:DeployCore \ + --rpc-url http://localhost:1729 \ + --private-key $PK \ + --broadcast +``` + +Note the addresses from the output (these are deterministic with CREATE2): + +| Contract | Address | +|----------|---------| +| DA Verifier (ECDSA) | `0x422A3492e218383753D8006C7Bfa97815B44373F` | +| Admin Verifier (Owner) | `0x9f9F5Fd89ad648f2C000C954d8d9C87743243eC5` | +| **State Oracle Proxy** | `0x72ae2643518179cF01bcA3278a37ceAD408DE8b2` | + +### Step 5: Start assertion DA + +The assertion DA stores assertion bytecode. It must be running before uploading assertions (step 6) and before the sidecar starts (step 7). + +```bash +docker run -d --name assertion-da -p 5001:5001 \ + -e DB_PATH=/data/assertions \ + -e DA_LISTEN_ADDR=0.0.0.0:5001 \ + -e DA_CACHE_SIZE=1000000 \ + -e DA_PRIVATE_KEY=0xdd7e619d26d7eef795e3b5144307204f2f5d7d08298d04b926e874c6d9d43e75 \ + -v /var/run/docker.sock:/var/run/docker.sock \ + ghcr.io/phylaxsystems/credible-sdk/assertion-da-dev:sha-01b3374 +``` + +### Step 6: Deploy OwnableTarget test contract + +```bash +PK=0xbcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31 + +rex deploy \ + --contract-path /tooling/l2/credible_layer/OwnableTarget.sol \ + --private-key $PK --rpc-url http://localhost:1729 --print-address --remappings "" +``` + +Note the contract address from the output. Example: `0x00c042c4d5d913277ce16611a2ce6e9003554ad5` + +### Step 7: Upload assertion to DA and register on State Oracle + +Build the assertion using the [credible-layer-starter](https://github.com/phylaxsystems/credible-layer-starter) repo: + +```bash +git clone --recurse-submodules https://github.com/phylaxsystems/credible-layer-starter /tmp/credible-layer-starter +cd /tmp/credible-layer-starter +pcl build # or: forge build +``` + +Submit the assertion's **creation bytecode** to the DA server: + +```bash +CREATION_BYTECODE=$(cat out/OwnableAssertion.a.sol/OwnableAssertion.json | \ + python3 -c "import sys,json; print(json.load(sys.stdin)['bytecode']['object'])") + +curl -s http://localhost:5001 -X POST -H "Content-Type: application/json" \ + -d "{\"jsonrpc\":\"2.0\",\"method\":\"da_submit_assertion\",\"params\":[\"$CREATION_BYTECODE\"],\"id\":1}" +``` + +Note the `id` from the response — that's the **assertion ID**. Then get the prover signature: + +```bash +ASSERTION_ID= + +DA_SIG=$(curl -s http://localhost:5001 -X POST -H "Content-Type: application/json" \ + -d "{\"jsonrpc\":\"2.0\",\"method\":\"da_get_assertion\",\"params\":[\"$ASSERTION_ID\"],\"id\":1}" | \ + python3 -c "import sys,json; print(json.load(sys.stdin)['result']['prover_signature'])") +``` + +Now register the assertion on the State Oracle: + +```bash +PK=0xbcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31 +STATE_ORACLE=0x72ae2643518179cF01bcA3278a37ceAD408DE8b2 +OWNABLE_TARGET=
+ADMIN_VERIFIER=0x9f9F5Fd89ad648f2C000C954d8d9C87743243eC5 +DA_VERIFIER=0x422A3492e218383753D8006C7Bfa97815B44373F + +# Disable whitelist (required for devnet) +rex send $STATE_ORACLE "disableWhitelist()" \ + -k $PK --rpc-url http://localhost:1729 + +# Register the target contract as an assertion adopter +rex send $STATE_ORACLE "registerAssertionAdopter(address,address,bytes)" \ + $OWNABLE_TARGET $ADMIN_VERIFIER "0x" \ + -k $PK --rpc-url http://localhost:1729 + +# Add the assertion +rex send $STATE_ORACLE "addAssertion(address,bytes32,address,bytes,bytes)" \ + $OWNABLE_TARGET $ASSERTION_ID $DA_VERIFIER "0x" $DA_SIG \ + -k $PK --rpc-url http://localhost:1729 + +# Verify +rex call $STATE_ORACLE "hasAssertion(address,bytes32)" \ + $OWNABLE_TARGET $ASSERTION_ID --rpc-url http://localhost:1729 +# Should return: 0x0000000000000000000000000000000000000000000000000000000000000001 (true) +``` + +### Step 8: Start the assertion indexer and sidecar + +The sidecar discovers assertions through a GraphQL indexer that watches State Oracle events. + +Clone and start the [sidecar-indexer](https://github.com/phylaxsystems/sidecar-indexer): + +```bash +git clone https://github.com/phylaxsystems/sidecar-indexer /tmp/sidecar-indexer +cd /tmp/sidecar-indexer + +cat > .env << EOF +RPC_ENDPOINT=http://host.docker.internal:1729 +STATE_ORACLE_ADDRESS=0x72ae2643518179cF01bcA3278a37ceAD408DE8b2 +STATE_ORACLE_DEPLOYMENT_BLOCK=0 +FINALITY_CONFIRMATION=1 +GRAPHQL_SERVER_PORT=4350 +DB_HOST=db +DB_PORT=5432 +DB_NAME=squid +DB_USER=postgres +DB_PASS=postgres +EOF + +docker compose -f infra/local/docker-compose.yaml up --build -d +``` + +Wait ~15 seconds, then verify the indexer found the assertion: + +```bash +curl -s http://localhost:4350/graphql -X POST -H "Content-Type: application/json" \ + -d '{"query":"{ assertionAddeds { totalCount nodes { assertionId assertionAdopter } } }"}' +# Should show totalCount: 1 +``` + +Create `sidecar-config.json`: + +```json +{ + "chain": { "spec_id": "CANCUN", "chain_id": 65536999 }, + "credible": { + "assertion_gas_limit": 3000000, + "cache_capacity_bytes": 256000000, + "flush_every_ms": 5000, + "assertion_da_rpc_url": "http://host.docker.internal:5001", + "event_source_url": "http://host.docker.internal:4350/graphql", + "poll_interval": 1000, + "assertion_store_db_path": "/tmp/sidecar/assertion_store_database", + "transaction_observer_db_path": "/tmp/sidecar/transaction_observer_database", + "transaction_observer_endpoint": null, + "transaction_observer_auth_token": "", + "state_oracle": "0x72ae2643518179cF01bcA3278a37ceAD408DE8b2", + "state_oracle_deployment_block": 0, + "transaction_results_max_capacity": 1000, + "accepted_txs_ttl_ms": 600000, + "assertion_store_prune_config_interval_ms": 60000, + "assertion_store_prune_config_retention_blocks": 0 + }, + "transport": { "bind_addr": "0.0.0.0:50051", "health_bind_addr": "0.0.0.0:9547" }, + "state": { + "sources": [{ + "type": "eth-rpc", + "ws_url": "ws://host.docker.internal:1730", + "http_url": "http://host.docker.internal:1729" + }], + "minimum_state_diff": 100, + "sources_sync_timeout_ms": 1000, + "sources_monitoring_period_ms": 500 + } +} +``` + +**Important:** `chain_id` must match your L2 genesis chain ID (`65536999` for the default ethrex L2 genesis). + +Pull the sidecar Docker image and start it: + +```bash +docker pull ghcr.io/phylaxsystems/credible-sdk/sidecar:main + +docker run -d --name credible-sidecar \ + -p 50051:50051 -p 9547:9547 \ + -e RUST_LOG=info,sidecar::engine=debug \ + -v $(pwd)/sidecar-config.json:/etc/credible-sidecar/config.json:ro \ + ghcr.io/phylaxsystems/credible-sdk/sidecar:main \ + --config-file-path /etc/credible-sidecar/config.json +``` + +Wait ~15 seconds, then verify the sidecar is healthy and loaded the assertion: + +```bash +curl http://localhost:9547/health # Should return "OK" + +# Check logs for assertion loading (wait ~15s after start) +docker logs credible-sidecar 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | grep "trigger_recorder" +# Should show: triggers: {Call { trigger_selector: 0xf2fde38b }: {0x7ab4397a}} +# This means: transferOwnership(address) calls will trigger the assertion +``` + +### Step 9: Test — violating transaction is DROPPED + +Send a `transferOwnership` call. The sidecar detects the assertion violation and ethrex drops the transaction. Verify the owner before and after to confirm state was not corrupted: + +```bash +PK=0xbcdf20249abf0ed6d944c0288fad489e33f66b3960d9e6229c1cd214ed3bbe31 +OWNABLE_TARGET=
+ +# Record the current owner (should be the deployer address) +rex call $OWNABLE_TARGET "owner()" --rpc-url http://localhost:1729 +# Example output: 0x0000000000000000000000008943545177806ed17b9f23f0a21ee5948ecaa776 + +# Try to transfer ownership (this should time out — tx never included!) +timeout 25 rex send $OWNABLE_TARGET "transferOwnership(address)" \ + 0x0000000000000000000000000000000000000001 \ + -k $PK --rpc-url http://localhost:1729 +# Expected: times out with no output (tx was dropped by Credible Layer) + +# Verify owner is UNCHANGED — must match the value from before +rex call $OWNABLE_TARGET "owner()" --rpc-url http://localhost:1729 +# Must return the same deployer address as above (not 0x0...01) +``` + +Check sidecar logs to confirm the assertion caught it: + +```bash +# Note: docker logs contain ANSI color codes, so pipe through sed to strip them +docker logs credible-sidecar 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | grep "is_valid=false" +# Should show: Transaction processed ... is_valid=false +docker logs credible-sidecar 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | grep "assertion_failure" +# Should show: Transaction failed assertion validation ... failed_assertions=[...] +``` + +### Step 10: Test — valid transaction is INCLUDED + +Send a non-protected call (`doSomething()`). The sidecar allows it through: + +```bash +rex send $OWNABLE_TARGET "doSomething()" \ + -k $PK --rpc-url http://localhost:1729 +# Expected: status 1 (success), included in a block +``` + +Check sidecar logs: + +```bash +docker logs credible-sidecar 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | grep "is_valid=true" +# Should show: Transaction processed ... is_valid=true +``` + +### Cleanup + +Stop all services and remove containers and data: + +```bash +# Stop ethrex L1 and L2 +pkill -f ethrex + +# Remove Docker containers +docker rm -f credible-sidecar assertion-da + +# Stop and remove indexer (PostgreSQL + indexer + API) +cd /tmp/sidecar-indexer && docker compose -f infra/local/docker-compose.yaml down -v + +# Remove ethrex data directories +cd /crates/l2 +rm -rf dev_ethrex_l1 dev_ethrex_l2 + +# (Optional) Remove cloned repos +rm -rf /tmp/credible-layer-contracts /tmp/credible-layer-starter /tmp/sidecar-indexer /tmp/cb_compiled +``` + +--- + +## References + +- [Credible Layer Introduction](https://docs.phylax.systems/credible/credible-introduction) +- [Architecture Overview](https://docs.phylax.systems/credible/architecture-overview) +- [Assertion Enforcer](https://docs.phylax.systems/credible/assertion-enforcer) +- [Network Integration](https://docs.phylax.systems/credible/network-integration) +- [Linea/Besu Integration](https://docs.phylax.systems/credible/network-integrations/architecture-linea) +- [credible-std Library](https://github.com/phylaxsystems/credible-std) +- [Besu Plugin Reference](https://github.com/phylaxsystems/credible-layer-besu-plugin) +- [sidecar.proto](../../crates/l2/proto/sidecar.proto) diff --git a/docs/l2/img/credible_layer_architecture.svg b/docs/l2/img/credible_layer_architecture.svg new file mode 100644 index 00000000000..740255b4c5e --- /dev/null +++ b/docs/l2/img/credible_layer_architecture.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + ethrex L2 Sequencer + + + User + + tx + + + RPC + + + Mempool + + + + + + + Block Producer: fill_transactions() + For each tx: + + + 1. Send Transaction + + + + 2. Execute tx in EVM + + + 3. Poll verdict + + + + ASSERTION_FAILED? + undo + drop tx + + + On block sealed: + NewIteration / CommitHead + + + + + gRPC :50051 + Credible Layer Sidecar + (Assertion Enforcer) + + + Runs PhEVM to evaluate assertions + GetTransaction for verdicts + + + Reads from ethrex: + eth_subscribe("newHeads") :1730 + debug_traceBlockByNumber :1729 + + + Reads from external: + Assertion DA (bytecode) + State Oracle indexer (GraphQL) + + + + On-chain (L2) + + + + State Oracle + (registry of assertions) + + + + Off-chain services + + + Assertion DA + (bytecode storage) + + + Indexer + (State Oracle events) + diff --git a/tooling/Cargo.lock b/tooling/Cargo.lock index e4b0fa567e7..935495dc966 100644 --- a/tooling/Cargo.lock +++ b/tooling/Cargo.lock @@ -1440,6 +1440,12 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "beef" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" + [[package]] name = "bincode" version = "1.3.3" @@ -3430,6 +3436,8 @@ dependencies = [ "hex", "jsonwebtoken", "lazy_static", + "prost", + "protox", "rand 0.8.5", "ratatui", "reqwest 0.12.28", @@ -3441,7 +3449,10 @@ dependencies = [ "spawned-rt", "thiserror 2.0.18", "tokio", + "tokio-stream", "tokio-util", + "tonic", + "tonic-build", "tracing", "tui-logger", "vergen-git2 1.0.7", @@ -3876,6 +3887,7 @@ dependencies = [ "ethrex-crypto", "ethrex-levm", "ethrex-rlp 9.0.0", + "hex", "rayon", "rustc-hash 2.1.2", "serde", @@ -4023,6 +4035,12 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "float-cmp" version = "0.10.0" @@ -5571,6 +5589,39 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "logos" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7251356ef8cb7aec833ddf598c6cb24d17b689d20b993f9d11a3d764e34e6458" +dependencies = [ + "logos-derive", +] + +[[package]] +name = "logos-codegen" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f80069600c0d66734f5ff52cc42f2dabd6b29d205f333d61fd7832e9e9963f" +dependencies = [ + "beef", + "fnv", + "lazy_static", + "proc-macro2", + "quote", + "regex-syntax", + "syn 2.0.117", +] + +[[package]] +name = "logos-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24fb722b06a9dc12adb0963ed585f19fc61dc5413e6a9be9422ef92c091e731d" +dependencies = [ + "logos-codegen", +] + [[package]] name = "lru" version = "0.12.5" @@ -5715,6 +5766,28 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d97bbf43eb4f088f8ca469930cde17fa036207c9a5e02ccc5107c4e8b17c964" +[[package]] +name = "miette" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f98efec8807c63c752b5bd61f862c165c115b0a35685bdcfd9238c7aeb592b7" +dependencies = [ + "cfg-if", + "miette-derive", + "unicode-width 0.1.14", +] + +[[package]] +name = "miette-derive" +version = "7.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5b29714e950dbb20d5e6f74f9dcec4edbcc1067bb7f8ed198c097b8c1a818b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "migrations" version = "4.0.0" @@ -5771,6 +5844,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + [[package]] name = "munge" version = "0.4.7" @@ -6835,6 +6914,16 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap 2.14.0", +] + [[package]] name = "phf" version = "0.11.3" @@ -7167,6 +7256,26 @@ dependencies = [ "prost-derive", ] +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck 0.4.1", + "itertools 0.10.5", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn 2.0.117", + "tempfile", +] + [[package]] name = "prost-derive" version = "0.13.5" @@ -7180,6 +7289,28 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "prost-reflect" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5edd582b62f5cde844716e66d92565d7faf7ab1445c8cebce6e00fba83ddb2" +dependencies = [ + "logos", + "miette", + "once_cell", + "prost", + "prost-types", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + [[package]] name = "protobuf" version = "3.7.2" @@ -7200,6 +7331,33 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "protox" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f352af331bf637b8ecc720f7c87bf903d2571fa2e14a66e9b2558846864b54a" +dependencies = [ + "bytes", + "miette", + "prost", + "prost-reflect", + "prost-types", + "protox-parse", + "thiserror 1.0.69", +] + +[[package]] +name = "protox-parse" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3a462d115462c080ae000c29a47f0b3985737e5d3a995fcdbcaa5c782068dde" +dependencies = [ + "logos", + "miette", + "prost-types", + "thiserror 1.0.69", +] + [[package]] name = "ptr_meta" version = "0.3.1" @@ -10143,6 +10301,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "tonic-build" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn 2.0.117", +] + [[package]] name = "tower" version = "0.4.13" diff --git a/tooling/l2/credible_layer/OwnableTarget.sol b/tooling/l2/credible_layer/OwnableTarget.sol new file mode 100644 index 00000000000..bcdcfd94118 --- /dev/null +++ b/tooling/l2/credible_layer/OwnableTarget.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.27; + +/// @title OwnableTarget +/// @notice A simple Ownable contract used as a target for Credible Layer testing. +/// The TestOwnershipAssertion protects this contract by preventing ownership transfers. +contract OwnableTarget { + address public owner; + + event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); + + constructor() { + owner = msg.sender; + } + + modifier onlyOwner() { + require(msg.sender == owner, "OwnableTarget: caller is not the owner"); + _; + } + + /// @notice Transfer ownership to a new address. + /// When protected by the TestOwnershipAssertion, this call will be dropped by the sidecar. + function transferOwnership(address newOwner) external onlyOwner { + require(newOwner != address(0), "OwnableTarget: new owner is the zero address"); + emit OwnershipTransferred(owner, newOwner); + owner = newOwner; + } + + /// @notice A harmless function that does not trigger any assertion. + function doSomething() external pure returns (uint256) { + return 42; + } +} diff --git a/tooling/l2/credible_layer/README.md b/tooling/l2/credible_layer/README.md new file mode 100644 index 00000000000..42dd6e0f85f --- /dev/null +++ b/tooling/l2/credible_layer/README.md @@ -0,0 +1,19 @@ +# Credible Layer Contracts + +This directory contains example/demo contracts for the Credible Layer integration: + +- `OwnableTarget.sol` — a simple Ownable contract used as a demonstration target +- `TestOwnershipAssertion.sol` — a test assertion that asserts ownership of `OwnableTarget` cannot change + +## State Oracle + +The **State Oracle** is the on-chain registry that maps protected contracts to their active +assertions. It is maintained by Phylax Systems and must be deployed separately using the +Phylax toolchain before starting the Credible Layer sidecar. + +See the [Credible Layer docs](../../../docs/l2/credible_layer.md) for deployment instructions. + +### References + +- [Credible Layer Introduction](https://docs.phylax.systems/credible/credible-introduction) +- [credible-layer-contracts](https://github.com/phylaxsystems/credible-layer-contracts) diff --git a/tooling/l2/credible_layer/TestOwnershipAssertion.sol b/tooling/l2/credible_layer/TestOwnershipAssertion.sol new file mode 100644 index 00000000000..7b870cdac69 --- /dev/null +++ b/tooling/l2/credible_layer/TestOwnershipAssertion.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.27; + +import {Assertion} from "credible-std/Assertion.sol"; + +interface IOwnableTarget { + function owner() external view returns (address); + function transferOwnership(address newOwner) external; +} + +/// @title TestOwnershipAssertion +/// @notice Protects OwnableTarget by asserting that ownership cannot change. +/// @dev Compile with `pcl build` (requires credible-std as a dependency). +/// See docs/l2/credible_layer.md for the full deployment workflow. +contract TestOwnershipAssertion is Assertion { + function triggers() external view override { + registerCallTrigger( + this.assertOwnerUnchanged.selector, + IOwnableTarget.transferOwnership.selector + ); + } + + function assertOwnerUnchanged() external { + IOwnableTarget target = IOwnableTarget(ph.getAssertionAdopter()); + ph.forkPreTx(); + address ownerBefore = target.owner(); + ph.forkPostTx(); + address ownerAfter = target.owner(); + require(ownerBefore == ownerAfter, "ownership changed"); + } +}