From 6af588bbf8de47987da521d7df25835304960f6a Mon Sep 17 00:00:00 2001 From: shana Date: Fri, 20 Sep 2024 01:53:41 +1000 Subject: [PATCH 001/429] Initial commit (flashbots/rollup-boost) --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000..a9e1fd39fa2ad --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Flashbots + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From d0181d9e6d87320bf8eaf2d6d986de3824cd222f Mon Sep 17 00:00:00 2001 From: avalonche Date: Fri, 20 Sep 2024 16:34:08 +1000 Subject: [PATCH 002/429] Add rpc server (flashbots/rollup-boost) --- .gitignore | 2 + Cargo.lock | 3828 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 22 + src/error.rs | 9 + src/main.rs | 100 ++ src/server.rs | 213 +++ 6 files changed, 4174 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/error.rs create mode 100644 src/main.rs create mode 100644 src/server.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000..0b745e2925074 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.env \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000000000..b122f4659747e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3828 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "alloy" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367891bf380210abb0d6aa30c5f85a9080cb4a066c4d5c5acadad630823751b" +dependencies = [ + "alloy-consensus", + "alloy-core", + "alloy-eips", + "alloy-genesis", + "alloy-provider", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-serde", + "alloy-transport-http", +] + +[[package]] +name = "alloy-chains" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf770dad29577cd3580f3dd09005799224a912b8cdfdd6dc04d030d42b3df4e" +dependencies = [ + "num_enum", + "strum", +] + +[[package]] +name = "alloy-consensus" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629b62e38d471cc15fea534eb7283d2f8a4e8bdb1811bcc5d66dda6cfce6fae1" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "serde", +] + +[[package]] +name = "alloy-core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b095eb0533144b4497e84a9cc3e44a5c2e3754a3983c0376a55a2f9183a53e" +dependencies = [ + "alloy-dyn-abi", + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-types", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4004925bff5ba0a11739ae84dbb6601a981ea692f3bd45b626935ee90a6b8471" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "itoa", + "serde", + "serde_json", + "winnow", +] + +[[package]] +name = "alloy-eip2930" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d319bb544ca6caeab58c39cea8921c55d924d4f68f2c60f24f914673f9a74a" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eips" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f923dd5fca5f67a43d81ed3ebad0880bd41f6dd0ada930030353ac356c54cd0f" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "c-kzg", + "derive_more", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-genesis" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a7a18afb0b318616b6b2b0e2e7ac5529d32a966c673b48091c9919e284e6aca" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-json-abi" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9996daf962fd0a90d3c93b388033228865953b92de7bb1959b891d78750a4091" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3c717b5298fad078cd3a418335b266eba91b511383ca9bd497f742d5975d5ab" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "serde", + "serde_json", + "thiserror", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3705ce7d8602132bcf5ac7a1dd293a42adc2f183abf5907c30ac535ceca049" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "thiserror", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ad40869867ed2d9cd3842b1e800889e5b49e6b92da346e93862b4a741bedf3" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-primitives" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "k256", + "keccak-asm", + "proptest", + "rand", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-provider" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927f708dd457ed63420400ee5f06945df9632d5d101851952056840426a10dc5" +dependencies = [ + "alloy-chains", + "alloy-consensus", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-client", + "alloy-rpc-types-eth", + "alloy-transport", + "alloy-transport-http", + "async-stream", + "async-trait", + "auto_impl", + "dashmap", + "futures", + "futures-utils-wasm", + "lru", + "pin-project", + "reqwest", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26154390b1d205a4a7ac7352aa2eb4f81f391399d4e2f546fb81a2f8bb383f62" +dependencies = [ + "alloy-rlp-derive", + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-rlp-derive" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "alloy-rpc-client" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d82952dca71173813d4e5733e2c986d8b04aea9e0f3b0a576664c232ad050a5" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "alloy-transport-http", + "futures", + "pin-project", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-rpc-types" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64333d639f2a0cf73491813c629a405744e16343a4bc5640931be707c345ecc5" +dependencies = [ + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-engine" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1464c4dd646e1bdfde86ae65ce5ba168dbb29180b478011fe87117ae46b1629b" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "derive_more", + "jsonwebtoken", + "rand", + "serde", +] + +[[package]] +name = "alloy-rpc-types-eth" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83aa984386deda02482660aa31cb8ca1e63d533f1c31a52d7d181ac5ec68e9b8" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "alloy-sol-types", + "cfg-if", + "derive_more", + "hashbrown", + "itertools 0.13.0", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-serde" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "731f75ec5d383107fd745d781619bd9cedf145836c51ecb991623d41278e71fa" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-signer" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307324cca94354cd654d6713629f0383ec037e1ff9e3e3d547212471209860c0" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" +dependencies = [ + "alloy-sol-macro-expander", + "alloy-sol-macro-input", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "alloy-sol-macro-expander" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" +dependencies = [ + "alloy-sol-macro-input", + "const-hex", + "heck", + "indexmap", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.77", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro-input" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" +dependencies = [ + "const-hex", + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.77", + "syn-solidity", +] + +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3edae8ea1de519ccba896b6834dec874230f72fe695ff3c9c118e90ec7cff783" +dependencies = [ + "serde", + "winnow", +] + +[[package]] +name = "alloy-sol-types" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "alloy-transport" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33616b2edf7454302a1d48084db185e52c309f73f6c10be99b0fe39354b3f1e9" +dependencies = [ + "alloy-json-rpc", + "base64 0.22.1", + "futures-util", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "alloy-transport-http" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a944f5310c690b62bbb3e7e5ce34527cbd36b2d18532a797af123271ce595a49" +dependencies = [ + "alloy-json-rpc", + "alloy-transport", + "reqwest", + "serde_json", + "tower 0.5.1", + "tracing", + "url", +] + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "async-trait" +version = "0.1.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blst" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +dependencies = [ + "serde", +] + +[[package]] +name = "c-kzg" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0307f72feab3300336fb803a57134159f6e20139af1357f36c54cb90d8e8928" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "once_cell", + "serde", +] + +[[package]] +name = "cc" +version = "1.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +dependencies = [ + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "const-hex" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "proptest", + "serde", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", + "serde", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower 0.4.13", + "tower-service", + "tracing", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpsee" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd1ead9fb95614e8dc5556d12a8681c2f6d352d0c1d3efc8708c7ccbba47bc6" +dependencies = [ + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff79651479f69ada7bda604ef2acf3f1aa50755d97cc36d25ff04c2664f9d96f" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "jsonrpsee-types", + "parking_lot", + "rand", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ed8b301b19f4dad8ddc66ed956a70fc227def5c19b3898e0a29ce8f0edee06" +dependencies = [ + "async-trait", + "base64 0.22.1", + "http-body", + "hyper", + "hyper-rustls", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "rustls", + "rustls-platform-verifier", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower 0.4.13", + "tracing", + "url", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d4c6bec4909c966f59f52db3655c0e9d4685faae8b49185973d9d7389bb884" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe2198e5fd96cf2153ecc123364f699b6e2151317ea09c7bf799c43c2fe1415" +dependencies = [ + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "route-recognizer", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower 0.4.13", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531e386460425e49679587871a056f2895a47dade21457324ad1262cd78ef6d9" +dependencies = [ + "http", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64 0.21.7", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "k256" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", +] + +[[package]] +name = "keccak-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "505d1856a39b200489082f90d897c3f07c455563880bc5952e38eabf731c83b6" +dependencies = [ + "digest 0.10.7", + "sha3-asm", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "object" +version = "0.36.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pest" +version = "2.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax 0.8.4", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.7", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "reqwest" +version = "0.12.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "rollup-boost" +version = "0.1.0" +dependencies = [ + "alloy", + "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "anyhow", + "clap", + "http", + "jsonrpsee", + "reqwest", + "serde", + "thiserror", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + +[[package]] +name = "ruint" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" +dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", + "num-traits", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.23", +] + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" + +[[package]] +name = "rustls-platform-verifier" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbb878bdfdf63a336a5e63561b1835e7a8c91524f51621db870169eac84b490" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-roots", + "winapi", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "num-bigint", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3-asm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28efc5e327c837aa837c59eae585fc250715ef939ac32881bcc11677cd02d46" +dependencies = [ + "cc", + "cfg-if", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "soketto" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures", + "http", + "httparse", + "log", + "rand", + "sha1", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.77", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" +dependencies = [ + "paste", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000..11799e622e5dc --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "rollup-boost" +version = "0.1.0" +edition = "2021" + +[dependencies] +alloy-primitives = "0.8.3" +alloy = { version = "0.3.6", features = ["eips", "rpc-types"] } +alloy-rpc-types-engine = { version = "0.3.6", features = ["serde"] } +alloy-rpc-types-eth = { version = "0.3.6", features = ["serde"] } +tokio = { version = "1", features = ["full"] } +tracing = "0.1.4" +tracing-subscriber = { version = "0.3.11", features = ["env-filter"] } +serde = { version = "1", features = ["derive"] } +thiserror = "1.0" +clap = { version = "4", features = ["derive", "env"] } +jsonrpsee = {version = "0.24.4", features = ["server", "http-client", "macros"]} +reqwest = "0.12.5" +http = "1.1.0" + +[dev-dependencies] +anyhow = "1.0" diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000000000..84df64912ca7e --- /dev/null +++ b/src/error.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum Error { + #[error("Invalid jwt token: {0}")] + InvalidJWTToken(String), + #[error("Error Initializing RPC Server: {0}")] + InitRPCServerError(String), +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000000000..2f348a46bf517 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,100 @@ +use std::net::SocketAddr; +use std::sync::Arc; + +use clap::{arg, Parser}; +use error::Error; +use http::HeaderMap; +use http::{header::AUTHORIZATION, HeaderValue}; +use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::server::Server; +use jsonrpsee::RpcModule; +use server::{EthApiServer, EthEngineApi}; +use tracing::{info, Level}; +use tracing_subscriber::EnvFilter; + +mod error; +mod server; + +#[derive(Parser, Debug)] +#[clap(author, version, about)] +struct Args { + /// JWT token to authenticate communication between the consensus and execution clients + #[arg(long, env)] + jwt_token: String, + + /// URL of the local l2 execution engine + #[arg(long, env)] + l2_url: String, + + /// URL of the builder execution engine + #[arg(long, env)] + builder_url: String, + + /// Host to run the server on + #[arg(long, env, default_value = "0.0.0.0")] + rpc_host: String, + + /// Port to run the server on + #[arg(long, env, default_value = "8081")] + rpc_port: u16, + + // Enable tracing + #[arg(long, env, default_value = "false")] + tracing: bool, + + /// Log level + #[arg(long, env, default_value = "info")] + log_level: Level, +} + +type Result = core::result::Result; + +#[tokio::main] +async fn main() -> Result<()> { + let args: Args = Args::parse(); + + // Initialize logging + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::new(args.log_level.to_string())) // Set the log level + .init(); + + // Initialize the local execution engine client + let mut headers = HeaderMap::new(); + headers.insert( + AUTHORIZATION, + match HeaderValue::try_from(format!("Bearer {}", args.jwt_token)) { + Ok(token) => token, + Err(e) => return Err(Error::InvalidJWTToken(e.to_string())), + }, + ); + let l2_client = HttpClientBuilder::new() + .set_headers(headers.clone()) + .build(args.l2_url) + .unwrap(); + + let builder_client = HttpClientBuilder::new() + .set_headers(headers) + .build(args.builder_url) + .unwrap(); + + let eth_engine_api = EthEngineApi::new(Arc::new(l2_client), Arc::new(builder_client)); + let mut module = RpcModule::new(()); + module + .merge(eth_engine_api.into_rpc()) + .map_err(|e| Error::InitRPCServerError(e.to_string()))?; + + // server setup + info!("Starting server on :{}", args.rpc_port); + let server = Server::builder() + .build( + format!("{}:{}", args.rpc_host, args.rpc_port) + .parse::() + .map_err(|e| Error::InitRPCServerError(e.to_string()))?, + ) + .await + .map_err(|e| Error::InitRPCServerError(e.to_string()))?; + let handle = server.start(module); + tokio::spawn(handle.stopped()); + + Ok(()) +} diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000000000..56ef9acfb724a --- /dev/null +++ b/src/server.rs @@ -0,0 +1,213 @@ +use std::sync::Arc; + +use alloy::eips::{BlockId, BlockNumberOrTag}; +use alloy::primitives::{Address, B256}; +use alloy::rpc::types::serde_helpers::JsonStorageKey; +use alloy_primitives::U64; +use alloy_rpc_types_engine::{ + ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadAttributes, PayloadId, + PayloadStatus, +}; +use alloy_rpc_types_eth::{Block, EIP1186AccountProofResponse}; +use jsonrpsee::core::{async_trait, ClientError, RpcResult}; +use jsonrpsee::http_client::HttpClient; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::types::ErrorCode; +use tracing::error; + +#[rpc(server, client, namespace = "eth")] +pub trait EthApi { + /// Returns the chain ID of the current network. + #[method(name = "chainId")] + async fn chain_id(&self) -> RpcResult>; + + /// Returns information about a block by number. + #[method(name = "getBlockByNumber")] + async fn block_by_number( + &self, + number: BlockNumberOrTag, + full: bool, + ) -> RpcResult>; + + /// Returns information about a block by hash. + #[method(name = "getBlockByHash")] + async fn block_by_hash(&self, hash: B256, full: bool) -> RpcResult>; + + /// Returns the account and storage values of the specified account including the Merkle-proof. + /// This call can be used to verify that the data you are pulling from is not tampered with. + #[method(name = "getProof")] + async fn get_proof( + &self, + address: Address, + keys: Vec, + block_number: Option, + ) -> RpcResult; +} + +#[rpc(server, client, namespace = "engine")] +pub trait EngineApi { + #[method(name = "forkchoiceUpdatedV3")] + async fn fork_choice_updated_v3( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> RpcResult; + + #[method(name = "getPayloadV3")] + async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult; + + #[method(name = "newPayloadV3")] + async fn new_payload_v3( + &self, + payload: ExecutionPayloadV3, + versioned_hashes: Vec, + parent_beacon_block_root: B256, + ) -> RpcResult; +} + +pub struct EthEngineApi { + l2_client: Arc, + builder_client: Arc, +} + +impl EthEngineApi { + pub fn new(l2_client: Arc, builder_client: Arc) -> Self { + Self { + l2_client, + builder_client, + } + } +} + +#[async_trait] +impl EthApiServer for EthEngineApi { + async fn chain_id(&self) -> RpcResult> { + self.l2_client.chain_id().await.map_err(|e| match e { + ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it + other_error => { + error!( + "Error calling chain_id from execution engine: {:?}", + other_error + ); + ErrorCode::InternalError.into() + } + }) + } + + async fn block_by_number( + &self, + number: BlockNumberOrTag, + full: bool, + ) -> RpcResult> { + self.l2_client + .block_by_number(number, full) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it + other_error => { + error!( + "Error calling block_by_number from execution engine: {:?}", + other_error + ); + ErrorCode::InternalError.into() + } + }) + } + + async fn block_by_hash(&self, hash: B256, full: bool) -> RpcResult> { + self.l2_client + .block_by_hash(hash, full) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it + other_error => { + error!( + "Error calling block_by_hash from execution engine: {:?}", + other_error + ); + ErrorCode::InternalError.into() + } + }) + } + + async fn get_proof( + &self, + address: Address, + keys: Vec, + block_number: Option, + ) -> RpcResult { + self.l2_client + .get_proof(address, keys, block_number) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it + other_error => { + error!( + "Error calling get_proof from execution engine: {:?}", + other_error + ); + ErrorCode::InternalError.into() + } + }) + } +} + +#[async_trait] +impl EngineApiServer for EthEngineApi { + async fn fork_choice_updated_v3( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> RpcResult { + self.l2_client + .fork_choice_updated_v3(fork_choice_state, payload_attributes) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it + other_error => { + error!( + "Error calling fork_choice_updated_v3 from execution engine: {:?}", + other_error + ); + ErrorCode::InternalError.into() + } + }) + } + + async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult { + self.l2_client + .get_payload_v3(payload_id) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it + other_error => { + error!( + "Error calling get_payload_v3 from execution engine: {:?}", + other_error + ); + ErrorCode::InternalError.into() + } + }) + } + + async fn new_payload_v3( + &self, + payload: ExecutionPayloadV3, + versioned_hashes: Vec, + parent_beacon_block_root: B256, + ) -> RpcResult { + self.l2_client + .new_payload_v3(payload, versioned_hashes, parent_beacon_block_root) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it + other_error => { + error!( + "Error calling new_payload_v3 from execution engine: {:?}", + other_error + ); + ErrorCode::InternalError.into() + } + }) + } +} From 2ae94a1e4b72c247754124d944e53be9bbf22dc6 Mon Sep 17 00:00:00 2001 From: dmarzzz Date: Fri, 20 Sep 2024 14:44:19 -0400 Subject: [PATCH 003/429] first readme (flashbots/rollup-boost) --- README.md | 67 +++++++++++++++++++++++++++++++++++++++++++++ assets/workflow.svg | 3 ++ 2 files changed, 70 insertions(+) create mode 100644 README.md create mode 100644 assets/workflow.svg diff --git a/README.md b/README.md new file mode 100644 index 0000000000000..d3fb16cb2913e --- /dev/null +++ b/README.md @@ -0,0 +1,67 @@ +# Rollup Boost + +This repo implements a proxy server for the Ethereum Engine API and is under active development. To read more about the design, check out the [design doc](https://github.com/ethereum-optimism/design-docs/pull/86). + + +## Usage + +Run the server using the following command: + +``` +cargo run -- [OPTIONS] +``` + +### Command-line Options + +- `--jwt-token `: JWT token for authentication (required) +- `--l2-url `: URL of the local L2 execution engine (required) +- `--builder-url `: URL of the builder execution engine (required) +- `--rpc-host `: Host to run the server on (default: 0.0.0.0) +- `--rpc-port `: Port to run the server on (default: 8081) +- `--tracing`: Enable tracing (default: false) +- `--log-level `: Log level (default: info) + +### Environment Variables + +You can also set the options using environment variables: + +- `JWT_TOKEN` +- `L2_URL` +- `BUILDER_URL` +- `RPC_HOST` +- `RPC_PORT` +- `TRACING` +- `LOG_LEVEL` + +### Example + +``` +cargo run --jwt-token your_jwt_token --l2-url http://localhost:8545 --builder-url http://localhost:8546 +``` + +## Core System Workflow + +1. By default, `rollup-boost` forwards all JSON-RPC API calls from `proposer-op-node` to `proposer-op-geth`. +2. When `rollup-boost` receives an `engine_FCU` with attributes (initiating block building): + - It relays the call to `proposer-op-geth` as usual. + - If `builder-op-geth` is synced to the chain tip, the call is also multiplexed to it. + - The FCU call returns a PayloadID, which should be identical for both `proposer-op-geth` and `builder-op-geth`. +3. When `rollup-boost` receives an `engine_getPayload`: + - It first queries `proposer-op-geth` for a fallback block. + - In parallel, it queries `builder-op-geth`. +4. Upon receiving the `builder-op-geth` block: + - `rollup-boost` validates the block with `proposer-op-geth` using `engine_newPayload`. + - This validation ensures the block will be valid for `proposer-op-geth`, preventing network stalls due to invalid blocks. + - If the external block is valid, it is returned to the `proposer-op-node`. +5. As per its normal workflow, the `proposer-op-node` sends another `newPayload` request to the sidecar and another FCU(without) to update the state of its op-geth node. + - In this case, the sidecar just relays the data and does not introspect anything. + - Note that since we already tested `newPayload` on the proposer-op-geth in the previous step, this process should be cached. + +![Workflow Diagram](/assets/workflow.svg) + +## License +The code in this project is free software under the [MIT License](/LICENSE). + +--- + +Made with ☀️ by the ⚡🤖 collective. \ No newline at end of file diff --git a/assets/workflow.svg b/assets/workflow.svg new file mode 100644 index 0000000000000..7ef3fb0a8368c --- /dev/null +++ b/assets/workflow.svg @@ -0,0 +1,3 @@ + + +BuilderProposerbuilder-Op-gethbuilder-Op-nodeSidecarOp-gethOp-nodebuilder-Op-gethbuilder-Op-nodeSidecarOp-gethOp-node12.par3.4.par5.eth_xxxeth_xxxFCU(with)FCU(with)PayloadIDFCU(with)PayloadIDPaylodIDWait for block timeGetPayloadV3GetPayloadV3ExecPayloadGetPayloadV3ExecPayloadNewPayloadV3ExecPayloadnewPayloadnewPayloadFCU(without)FCU(without) \ No newline at end of file From a119fcc9a1d261e1612d3d79d1af13756cab2a93 Mon Sep 17 00:00:00 2001 From: avalonche Date: Sat, 21 Sep 2024 09:48:02 +1000 Subject: [PATCH 004/429] add proxy (flashbots/rollup-boost) --- .env.example | 3 +++ Cargo.lock | 71 ++++++++++++++++++++++++++++++++++++-------------- Cargo.toml | 5 ++++ src/main.rs | 13 +++++++--- src/proxy.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/server.rs | 5 +++- 6 files changed, 146 insertions(+), 23 deletions(-) create mode 100644 .env.example create mode 100644 src/proxy.rs diff --git a/.env.example b/.env.example new file mode 100644 index 0000000000000..e8b4637cabf4f --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +JWT_TOKEN=688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a +L2_URL=http://localhost:8551 +BUILDER_URL=http://localhost:8552 \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index b122f4659747e..a7b9434ce5fe5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1118,6 +1118,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + [[package]] name = "dunce" version = "1.0.5" @@ -1416,7 +1422,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http", + "http 1.1.0", "indexmap", "slab", "tokio", @@ -1471,6 +1477,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http" version = "1.1.0" @@ -1482,6 +1499,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + [[package]] name = "http-body" version = "1.0.1" @@ -1489,7 +1517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http", + "http 1.1.0", ] [[package]] @@ -1500,8 +1528,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1527,8 +1555,8 @@ dependencies = [ "futures-channel", "futures-util", "h2", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "httparse", "httpdate", "itoa", @@ -1545,7 +1573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", - "http", + "http 1.1.0", "hyper", "hyper-util", "log", @@ -1581,8 +1609,8 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "hyper", "pin-project-lite", "socket2", @@ -1721,8 +1749,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "http-body-util", "jsonrpsee-types", "parking_lot", @@ -1743,7 +1771,7 @@ checksum = "68ed8b301b19f4dad8ddc66ed956a70fc227def5c19b3898e0a29ce8f0edee06" dependencies = [ "async-trait", "base64 0.22.1", - "http-body", + "http-body 1.0.1", "hyper", "hyper-rustls", "hyper-util", @@ -1780,8 +1808,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebe2198e5fd96cf2153ecc123364f699b6e2151317ea09c7bf799c43c2fe1415" dependencies = [ "futures-util", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "http-body-util", "hyper", "hyper-util", @@ -1806,7 +1834,7 @@ version = "0.24.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "531e386460425e49679587871a056f2895a47dade21457324ad1262cd78ef6d9" dependencies = [ - "http", + "http 1.1.0", "serde", "serde_json", "thiserror", @@ -2439,8 +2467,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 1.1.0", + "http-body 1.0.1", "http-body-util", "hyper", "hyper-rustls", @@ -2515,12 +2543,17 @@ dependencies = [ "alloy-rpc-types-eth", "anyhow", "clap", - "http", + "dotenv", + "http 1.1.0", + "http-body 0.4.6", + "hyper", + "hyper-util", "jsonrpsee", "reqwest", "serde", "thiserror", "tokio", + "tower 0.4.13", "tracing", "tracing-subscriber", ] @@ -2958,7 +2991,7 @@ dependencies = [ "base64 0.22.1", "bytes", "futures", - "http", + "http 1.1.0", "httparse", "log", "rand", diff --git a/Cargo.toml b/Cargo.toml index 11799e622e5dc..d32c33748d14e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,11 @@ clap = { version = "4", features = ["derive", "env"] } jsonrpsee = {version = "0.24.4", features = ["server", "http-client", "macros"]} reqwest = "0.12.5" http = "1.1.0" +dotenv = "0.15.0" +tower = "0.4.13" +http-body = "0.4.5" +hyper = { version = "1.4.1", features = ["full"] } +hyper-util = { version = "0.1", features = ["full"] } [dev-dependencies] anyhow = "1.0" diff --git a/src/main.rs b/src/main.rs index 2f348a46bf517..c80cc79d28790 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,17 +2,20 @@ use std::net::SocketAddr; use std::sync::Arc; use clap::{arg, Parser}; +use dotenv::dotenv; use error::Error; use http::HeaderMap; use http::{header::AUTHORIZATION, HeaderValue}; use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::server::Server; use jsonrpsee::RpcModule; +use proxy::ProxyLayer; use server::{EthApiServer, EthEngineApi}; use tracing::{info, Level}; use tracing_subscriber::EnvFilter; mod error; +mod proxy; mod server; #[derive(Parser, Debug)] @@ -51,6 +54,8 @@ type Result = core::result::Result; #[tokio::main] async fn main() -> Result<()> { + // Load .env file + dotenv().ok(); let args: Args = Args::parse(); // Initialize logging @@ -69,7 +74,7 @@ async fn main() -> Result<()> { ); let l2_client = HttpClientBuilder::new() .set_headers(headers.clone()) - .build(args.l2_url) + .build(args.l2_url.clone()) .unwrap(); let builder_client = HttpClientBuilder::new() @@ -78,14 +83,16 @@ async fn main() -> Result<()> { .unwrap(); let eth_engine_api = EthEngineApi::new(Arc::new(l2_client), Arc::new(builder_client)); - let mut module = RpcModule::new(()); + let mut module: RpcModule<()> = RpcModule::new(()); module .merge(eth_engine_api.into_rpc()) .map_err(|e| Error::InitRPCServerError(e.to_string()))?; // server setup info!("Starting server on :{}", args.rpc_port); + let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new(args.l2_url)); let server = Server::builder() + .set_http_middleware(service_builder) .build( format!("{}:{}", args.rpc_host, args.rpc_port) .parse::() @@ -94,7 +101,7 @@ async fn main() -> Result<()> { .await .map_err(|e| Error::InitRPCServerError(e.to_string()))?; let handle = server.start(module); - tokio::spawn(handle.stopped()); + handle.stopped().await; Ok(()) } diff --git a/src/proxy.rs b/src/proxy.rs new file mode 100644 index 0000000000000..965077710a89d --- /dev/null +++ b/src/proxy.rs @@ -0,0 +1,72 @@ +use hyper::Response; +use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::client::legacy::Client; +use hyper_util::rt::TokioExecutor; +use jsonrpsee::core::BoxError; +use jsonrpsee::http_client::{HttpBody, HttpRequest, HttpResponse}; +use std::task::{Context, Poll}; +use std::{future::Future, pin::Pin}; +use tower::{Layer, Service}; +use tracing::error; + +#[derive(Debug, Clone)] +pub struct ProxyLayer { + target_url: String, +} + +impl ProxyLayer { + pub fn new(target_url: String) -> Self { + ProxyLayer { + target_url, + } + } +} + +impl Layer for ProxyLayer { + type Service = ProxyService; + + fn layer(&self, inner: S) -> Self::Service { + ProxyService { + inner, + client: Client::builder(TokioExecutor::new()).build_http(), + target_url: self.target_url.clone(), + } + } +} + +#[derive(Debug, Clone)] +pub struct ProxyService { + inner: S, + client: Client, + target_url: String, +} + +impl Service> for ProxyService +where + S: Service, Response = Response>, + S::Response: 'static, + S::Error: Into + 'static, + S::Future: Send + 'static, +{ + type Response = HttpResponse; + type Error = BoxError; + type Future = + Pin> + Send + 'static>>; + + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.inner.poll_ready(cx).map_err(Into::into) + } + + fn call(&mut self, mut req: HttpRequest) -> Self::Future { + let target_url = self.target_url.clone(); + let client = self.client.clone(); + let fut = async move { + *req.uri_mut() = target_url.parse().unwrap(); + client.request(req).await.map_err(|e| { + error!("Error proxying request: {}", e); + Box::new(e) as Box + }) + }; + Box::pin(fut) + } +} diff --git a/src/server.rs b/src/server.rs index 56ef9acfb724a..95639a2cf9562 100644 --- a/src/server.rs +++ b/src/server.rs @@ -13,7 +13,7 @@ use jsonrpsee::core::{async_trait, ClientError, RpcResult}; use jsonrpsee::http_client::HttpClient; use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::ErrorCode; -use tracing::error; +use tracing::{error, info}; #[rpc(server, client, namespace = "eth")] pub trait EthApi { @@ -159,6 +159,7 @@ impl EngineApiServer for EthEngineApi { fork_choice_state: ForkchoiceState, payload_attributes: Option, ) -> RpcResult { + info!("fork_choice_updated_v3"); self.l2_client .fork_choice_updated_v3(fork_choice_state, payload_attributes) .await @@ -175,6 +176,7 @@ impl EngineApiServer for EthEngineApi { } async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult { + info!("get_payload_v3"); self.l2_client .get_payload_v3(payload_id) .await @@ -196,6 +198,7 @@ impl EngineApiServer for EthEngineApi { versioned_hashes: Vec, parent_beacon_block_root: B256, ) -> RpcResult { + info!("new_payload_v3"); self.l2_client .new_payload_v3(payload, versioned_hashes, parent_beacon_block_root) .await From 87250c31511f853c1ba55a4f6c421b0a913a304a Mon Sep 17 00:00:00 2001 From: avalonche Date: Sat, 21 Sep 2024 09:51:57 +1000 Subject: [PATCH 005/429] add ci (flashbots/rollup-boost) --- .github/workflows/lint.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000000000..bfbb13a2970e7 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,31 @@ +name: Linting + +on: + push: + branches: + - main + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + components: rustfmt + + - name: Build + run: cargo build --verbose + + - name: Lint + run: cargo clippy -- -D warnings + + - name: Format code + run: cargo fmt -- --check \ No newline at end of file From a2ef81c8061d816c3e1b7dd8897bcebe3d86b3ad Mon Sep 17 00:00:00 2001 From: avalonche Date: Sat, 21 Sep 2024 09:56:08 +1000 Subject: [PATCH 006/429] fix lint (flashbots/rollup-boost) --- src/proxy.rs | 4 +--- src/server.rs | 11 ++++------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/proxy.rs b/src/proxy.rs index 965077710a89d..8c49a23d633f0 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -16,9 +16,7 @@ pub struct ProxyLayer { impl ProxyLayer { pub fn new(target_url: String) -> Self { - ProxyLayer { - target_url, - } + ProxyLayer { target_url } } } diff --git a/src/server.rs b/src/server.rs index 95639a2cf9562..1abe90a06b227 100644 --- a/src/server.rs +++ b/src/server.rs @@ -13,7 +13,7 @@ use jsonrpsee::core::{async_trait, ClientError, RpcResult}; use jsonrpsee::http_client::HttpClient; use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::ErrorCode; -use tracing::{error, info}; +use tracing::error; #[rpc(server, client, namespace = "eth")] pub trait EthApi { @@ -159,8 +159,7 @@ impl EngineApiServer for EthEngineApi { fork_choice_state: ForkchoiceState, payload_attributes: Option, ) -> RpcResult { - info!("fork_choice_updated_v3"); - self.l2_client + self.builder_client .fork_choice_updated_v3(fork_choice_state, payload_attributes) .await .map_err(|e| match e { @@ -176,8 +175,7 @@ impl EngineApiServer for EthEngineApi { } async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult { - info!("get_payload_v3"); - self.l2_client + self.builder_client .get_payload_v3(payload_id) .await .map_err(|e| match e { @@ -198,8 +196,7 @@ impl EngineApiServer for EthEngineApi { versioned_hashes: Vec, parent_beacon_block_root: B256, ) -> RpcResult { - info!("new_payload_v3"); - self.l2_client + self.builder_client .new_payload_v3(payload, versioned_hashes, parent_beacon_block_root) .await .map_err(|e| match e { From 57ca4646538993ce8c9ccfc33f9ad1b8a16aab90 Mon Sep 17 00:00:00 2001 From: avalonche Date: Thu, 26 Sep 2024 03:56:57 +1000 Subject: [PATCH 007/429] Multiplex engine calls to builder (flashbots/rollup-boost) --- .env.example | 7 +- Cargo.lock | 2371 +++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 10 +- README.md | 10 +- src/error.rs | 8 +- src/main.rs | 101 ++- src/proxy.rs | 63 +- src/server.rs | 283 +++--- 8 files changed, 2571 insertions(+), 282 deletions(-) diff --git a/.env.example b/.env.example index e8b4637cabf4f..5ce40efd9a686 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,8 @@ JWT_TOKEN=688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a L2_URL=http://localhost:8551 -BUILDER_URL=http://localhost:8552 \ No newline at end of file +BUILDER_URL=http://localhost:8552 +RPC_HOST=0.0.0.0 +RPC_PORT=8081 +TRACING=false +LOG_LEVEL=info +BOOST_SYNC=false \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a7b9434ce5fe5..7363039b16960 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,6 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", "zerocopy", @@ -67,7 +68,9 @@ version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abf770dad29577cd3580f3dd09005799224a912b8cdfdd6dc04d030d42b3df4e" dependencies = [ + "alloy-rlp", "num_enum", + "serde", "strum", ] @@ -81,6 +84,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-serde", + "arbitrary", "c-kzg", "serde", ] @@ -122,6 +126,8 @@ checksum = "0069cf0642457f87a01a014f6dc29d5d893cd4fd8fddf0c3cdfad1bb3ebafc41" dependencies = [ "alloy-primitives", "alloy-rlp", + "arbitrary", + "rand", "serde", ] @@ -133,6 +139,9 @@ checksum = "37d319bb544ca6caeab58c39cea8921c55d924d4f68f2c60f24f914673f9a74a" dependencies = [ "alloy-primitives", "alloy-rlp", + "arbitrary", + "k256", + "rand", "serde", ] @@ -147,6 +156,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-serde", + "arbitrary", "c-kzg", "derive_more", "once_cell", @@ -231,15 +241,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" dependencies = [ "alloy-rlp", + "arbitrary", "bytes", "cfg-if", "const-hex", + "derive_arbitrary", "derive_more", + "getrandom", "hex-literal", "itoa", "k256", "keccak-asm", "proptest", + "proptest-derive", "rand", "ruint", "serde", @@ -329,6 +343,7 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64333d639f2a0cf73491813c629a405744e16343a4bc5640931be707c345ecc5" dependencies = [ + "alloy-rpc-types-engine", "alloy-rpc-types-eth", "alloy-serde", "serde", @@ -346,6 +361,7 @@ dependencies = [ "alloy-rlp", "alloy-serde", "derive_more", + "jsonrpsee-types", "jsonwebtoken", "rand", "serde", @@ -366,8 +382,9 @@ dependencies = [ "alloy-sol-types", "cfg-if", "derive_more", - "hashbrown", + "hashbrown 0.14.5", "itertools 0.13.0", + "jsonrpsee-types", "serde", "serde_json", ] @@ -379,6 +396,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "731f75ec5d383107fd745d781619bd9cedf145836c51ecb991623d41278e71fa" dependencies = [ "alloy-primitives", + "arbitrary", "serde", "serde_json", ] @@ -420,7 +438,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap", + "indexmap 2.5.0", "proc-macro-error2", "proc-macro2", "quote", @@ -501,6 +519,37 @@ dependencies = [ "url", ] +[[package]] +name = "alloy-trie" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a46c9c4fdccda7982e7928904bd85fe235a0404ee3d7e197fff13d61eac8b4f" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more", + "hashbrown 0.14.5", + "nybbles", + "serde", + "smallvec", + "tracing", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.15" @@ -556,6 +605,29 @@ version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +[[package]] +name = "aquamarine" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e" +dependencies = [ + "include_dir", + "itertools 0.10.5", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "ark-ff" version = "0.3.0" @@ -725,6 +797,16 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "aurora-engine-modexp" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aef7712851e524f35fbbb74fa6599c5cd8692056a1c36f9ca0d2001b670e7e5" +dependencies = [ + "hex", + "num", +] + [[package]] name = "auto_impl" version = "1.2.0" @@ -754,7 +836,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -781,6 +863,35 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.10.5", + "lazy_static", + "lazycell", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.77", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -796,11 +907,20 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] name = "bitvec" @@ -810,6 +930,7 @@ checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", + "serde", "tap", "wyz", ] @@ -847,6 +968,12 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" +[[package]] +name = "bytemuck" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" + [[package]] name = "byteorder" version = "1.5.0" @@ -883,6 +1010,8 @@ version = "1.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" dependencies = [ + "jobserver", + "libc", "shlex", ] @@ -892,12 +1021,47 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.17" @@ -973,6 +1137,15 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -998,6 +1171,49 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crossbeam-channel" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.20" @@ -1032,6 +1248,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.77", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.77", +] + [[package]] name = "dashmap" version = "6.1.0" @@ -1040,7 +1291,7 @@ checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -1063,6 +1314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -1076,6 +1328,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "derive_more" version = "1.0.0" @@ -1091,6 +1354,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ + "convert_case", "proc-macro2", "quote", "syn 2.0.77", @@ -1130,6 +1394,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dyn-clone" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" + [[package]] name = "ecdsa" version = "0.16.9" @@ -1178,6 +1448,33 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enr" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "972070166c68827e64bd1ebc8159dd8e32d9bc2da7ebe8f20b61308f7974ad30" +dependencies = [ + "alloy-rlp", + "base64 0.21.7", + "bytes", + "hex", + "log", + "rand", + "sha3", + "zeroize", +] + +[[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1194,6 +1491,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + [[package]] name = "fastrand" version = "2.1.1" @@ -1221,6 +1528,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "fixed-hash" version = "0.8.0" @@ -1263,6 +1582,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "funty" version = "2.0.0" @@ -1423,13 +1751,25 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap", + "indexmap 2.5.0", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + [[package]] name = "hashbrown" version = "0.14.5" @@ -1620,6 +1960,35 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.5.0" @@ -1651,23 +2020,80 @@ dependencies = [ ] [[package]] -name = "indexmap" -version = "2.5.0" +name = "include_dir" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ - "equivalent", - "hashbrown", + "include_dir_macros", ] [[package]] -name = "ipnet" -version = "2.10.0" +name = "include_dir_macros" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" - -[[package]] -name = "is_terminal_polyfill" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", + "serde", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "ipnet" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" + +[[package]] +name = "is_terminal_polyfill" version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" @@ -1716,6 +2142,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" version = "0.3.70" @@ -1755,7 +2190,7 @@ dependencies = [ "jsonrpsee-types", "parking_lot", "rand", - "rustc-hash", + "rustc-hash 2.0.0", "serde", "serde_json", "thiserror", @@ -1868,6 +2303,15 @@ dependencies = [ "sha2", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "keccak-asm" version = "0.1.4" @@ -1878,11 +2322,40 @@ dependencies = [ "sha3-asm", ] +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" @@ -1890,12 +2363,33 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets 0.52.6", +] + [[package]] name = "libm" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", + "redox_syscall", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -1924,9 +2418,15 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] +[[package]] +name = "lz4_flex" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" + [[package]] name = "matchers" version = "0.1.0" @@ -1942,12 +2442,37 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "metrics" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884adb57038347dfbaf2d5065887b6cf4312330dc8e94bc30a1a839bd79d3261" +dependencies = [ + "ahash", + "portable-atomic", +] + [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -1957,6 +2482,18 @@ dependencies = [ "adler2", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "mio" version = "1.0.2" @@ -1969,6 +2506,27 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -1986,6 +2544,43 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "notify" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" +dependencies = [ + "bitflags 2.6.0", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio 0.8.11", + "walkdir", + "windows-sys 0.48.0", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1996,6 +2591,20 @@ dependencies = [ "winapi", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -2006,6 +2615,15 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -2021,6 +2639,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -2061,6 +2701,19 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "nybbles" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95f06be0417d97f81fe4e5c86d7d01b392655a9cac9c19a848aa033e18937b23" +dependencies = [ + "alloy-rlp", + "const-hex", + "proptest", + "serde", + "smallvec", +] + [[package]] name = "object" version = "0.36.4" @@ -2077,55 +2730,160 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "openssl" -version = "0.10.66" +name = "op-alloy-consensus" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "21aad1fbf80d2bcd7406880efc7ba109365f44bbb72896758ddcbfa46bf1592c" dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "arbitrary", + "derive_more", + "serde", + "spin", ] [[package]] -name = "openssl-macros" -version = "0.1.1" +name = "op-alloy-genesis" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +checksum = "6e1b8a9b70da0e027242ec1762f0f3a386278b6291d00d12ff5a64929dc19f68" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.77", + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-sol-types", + "serde", + "serde_repr", ] [[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.103" +name = "op-alloy-protocol" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "bf300a82ae2d30e2255bfea87a2259da49f63a25a44db561ae64cc9e3084139f" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "hashbrown 0.14.5", + "op-alloy-consensus", + "op-alloy-genesis", + "serde", ] [[package]] -name = "overload" -version = "0.1.1" +name = "op-alloy-rpc-types" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e281fbfc2198b7c0c16457d6524f83d192662bc9f3df70f24c3038d4521616df" +dependencies = [ + "alloy-eips", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "cfg-if", + "hashbrown 0.14.5", + "op-alloy-consensus", + "serde", + "serde_json", +] + +[[package]] +name = "op-alloy-rpc-types-engine" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2947272a81ebf988f4804b6f0f6a7c0b2f6f89a908cb410e36f8f3828f81c778" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-serde", + "derive_more", + "op-alloy-consensus", + "op-alloy-genesis", + "op-alloy-protocol", + "serde", +] + +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "overload" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "parity-scale-codec" version = "3.6.12" @@ -2135,6 +2893,7 @@ dependencies = [ "arrayvec", "bitvec", "byte-slice-cast", + "bytes", "impl-trait-for-tuples", "parity-scale-codec-derive", "serde", @@ -2172,7 +2931,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2256,6 +3015,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +[[package]] +name = "portable-atomic" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce" + [[package]] name = "powerfmt" version = "0.2.0" @@ -2271,6 +3036,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "primitive-types" version = "0.12.2" @@ -2291,6 +3065,30 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -2330,7 +3128,7 @@ checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" dependencies = [ "bit-set", "bit-vec", - "bitflags", + "bitflags 2.6.0", "lazy_static", "num-traits", "rand", @@ -2342,6 +3140,17 @@ dependencies = [ "unarray", ] +[[package]] +name = "proptest-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -2402,13 +3211,33 @@ dependencies = [ "rand_core", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" dependencies = [ - "bitflags", + "bitflags 2.6.0", ] [[package]] @@ -2484,18 +3313,1045 @@ dependencies = [ "pin-project-lite", "rustls-pemfile", "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 1.0.1", - "system-configuration", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-registry", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "reth-basic-payload-builder" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "futures-core", + "futures-util", + "metrics", + "reth-chainspec", + "reth-metrics", + "reth-payload-builder", + "reth-payload-primitives", + "reth-primitives", + "reth-provider", + "reth-revm", + "reth-tasks", + "reth-transaction-pool", + "revm", + "tokio", + "tracing", +] + +[[package]] +name = "reth-blockchain-tree-api" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "reth-consensus", + "reth-execution-errors", + "reth-primitives", + "reth-storage-errors", + "thiserror", +] + +[[package]] +name = "reth-chain-state" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "auto_impl", + "derive_more", + "metrics", + "parking_lot", + "pin-project", + "reth-chainspec", + "reth-errors", + "reth-execution-types", + "reth-metrics", + "reth-primitives", + "reth-storage-api", + "reth-trie", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-chainspec" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-chains", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-trie", + "auto_impl", + "derive_more", + "once_cell", + "op-alloy-rpc-types", + "reth-ethereum-forks", + "reth-network-peers", + "reth-optimism-forks", + "reth-primitives-traits", + "reth-trie-common", + "serde", + "serde_json", +] + +[[package]] +name = "reth-codecs" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-trie", + "bytes", + "modular-bitfield", + "op-alloy-consensus", + "reth-codecs-derive", + "serde", +] + +[[package]] +name = "reth-codecs-derive" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "reth-consensus" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "auto_impl", + "derive_more", + "reth-primitives", +] + +[[package]] +name = "reth-consensus-common" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "reth-chainspec", + "reth-consensus", + "reth-primitives", +] + +[[package]] +name = "reth-db" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "bytes", + "derive_more", + "eyre", + "metrics", + "page_size", + "paste", + "reth-db-api", + "reth-fs-util", + "reth-libmdbx", + "reth-metrics", + "reth-nippy-jar", + "reth-primitives", + "reth-primitives-traits", + "reth-prune-types", + "reth-stages-types", + "reth-storage-errors", + "reth-tracing", + "reth-trie-common", + "rustc-hash 2.0.0", + "serde", + "strum", + "sysinfo", + "thiserror", +] + +[[package]] +name = "reth-db-api" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-genesis", + "alloy-primitives", + "bytes", + "derive_more", + "metrics", + "modular-bitfield", + "parity-scale-codec", + "reth-codecs", + "reth-db-models", + "reth-primitives", + "reth-primitives-traits", + "reth-prune-types", + "reth-stages-types", + "reth-storage-errors", + "reth-trie-common", + "serde", +] + +[[package]] +name = "reth-db-models" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "bytes", + "modular-bitfield", + "reth-codecs", + "reth-primitives", + "serde", +] + +[[package]] +name = "reth-engine-primitives" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "reth-execution-types", + "reth-payload-primitives", + "reth-primitives", + "reth-trie", + "serde", +] + +[[package]] +name = "reth-errors" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "reth-blockchain-tree-api", + "reth-consensus", + "reth-execution-errors", + "reth-fs-util", + "reth-storage-errors", + "thiserror", +] + +[[package]] +name = "reth-eth-wire-types" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-chains", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "bytes", + "derive_more", + "reth-chainspec", + "reth-codecs-derive", + "reth-primitives", + "thiserror", +] + +[[package]] +name = "reth-ethereum-engine-primitives" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "reth-chain-state", + "reth-chainspec", + "reth-engine-primitives", + "reth-payload-primitives", + "reth-primitives", + "reth-rpc-types", + "reth-rpc-types-compat", + "serde", + "sha2", +] + +[[package]] +name = "reth-ethereum-forks" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-chains", + "alloy-primitives", + "alloy-rlp", + "auto_impl", + "crc", + "dyn-clone", + "once_cell", + "rustc-hash 2.0.0", + "serde", + "thiserror-no-std", +] + +[[package]] +name = "reth-evm" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-eips", + "auto_impl", + "futures-util", + "metrics", + "reth-chainspec", + "reth-execution-errors", + "reth-execution-types", + "reth-metrics", + "reth-primitives", + "reth-primitives-traits", + "reth-prune-types", + "reth-storage-errors", + "revm", + "revm-primitives", +] + +[[package]] +name = "reth-evm-optimism" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "reth-chainspec", + "reth-ethereum-forks", + "reth-evm", + "reth-execution-errors", + "reth-execution-types", + "reth-optimism-chainspec", + "reth-optimism-consensus", + "reth-optimism-forks", + "reth-primitives", + "reth-prune-types", + "reth-revm", + "revm", + "revm-primitives", + "thiserror", + "tracing", +] + +[[package]] +name = "reth-execution-errors" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "derive_more", + "nybbles", + "reth-consensus", + "reth-prune-types", + "reth-storage-errors", + "revm-primitives", +] + +[[package]] +name = "reth-execution-types" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "reth-chainspec", + "reth-execution-errors", + "reth-primitives", + "reth-trie", + "revm", +] + +[[package]] +name = "reth-fs-util" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "reth-libmdbx" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "bitflags 2.6.0", + "byteorder", + "dashmap", + "derive_more", + "indexmap 2.5.0", + "parking_lot", + "reth-mdbx-sys", + "thiserror", + "tracing", +] + +[[package]] +name = "reth-mdbx-sys" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "bindgen", + "cc", +] + +[[package]] +name = "reth-metrics" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "metrics", + "reth-metrics-derive", +] + +[[package]] +name = "reth-metrics-derive" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.77", +] + +[[package]] +name = "reth-net-banlist" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", +] + +[[package]] +name = "reth-network-p2p" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "auto_impl", + "derive_more", + "futures", + "reth-consensus", + "reth-eth-wire-types", + "reth-network-peers", + "reth-network-types", + "reth-primitives", + "reth-storage-errors", + "tokio", + "tracing", +] + +[[package]] +name = "reth-network-peers" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "enr", + "serde_with", + "thiserror", + "url", +] + +[[package]] +name = "reth-network-types" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "reth-ethereum-forks", + "reth-net-banlist", + "reth-network-peers", + "serde_json", + "tracing", +] + +[[package]] +name = "reth-nippy-jar" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "anyhow", + "bincode", + "derive_more", + "lz4_flex", + "memmap2", + "reth-fs-util", + "serde", + "thiserror", + "tracing", + "zstd", +] + +[[package]] +name = "reth-node-types" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "reth-chainspec", + "reth-db-api", + "reth-engine-primitives", +] + +[[package]] +name = "reth-optimism-chainspec" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-chains", + "alloy-primitives", + "derive_more", + "once_cell", + "reth-chainspec", + "reth-ethereum-forks", + "reth-optimism-forks", + "reth-primitives-traits", + "serde_json", +] + +[[package]] +name = "reth-optimism-consensus" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "reth-chainspec", + "reth-consensus", + "reth-consensus-common", + "reth-optimism-forks", + "reth-primitives", + "reth-trie-common", + "tracing", +] + +[[package]] +name = "reth-optimism-forks" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-chains", + "alloy-primitives", + "once_cell", + "reth-ethereum-forks", +] + +[[package]] +name = "reth-optimism-payload-builder" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "op-alloy-rpc-types-engine", + "reth-basic-payload-builder", + "reth-chain-state", + "reth-chainspec", + "reth-evm", + "reth-evm-optimism", + "reth-execution-types", + "reth-optimism-forks", + "reth-payload-builder", + "reth-payload-primitives", + "reth-primitives", + "reth-provider", + "reth-revm", + "reth-rpc-types", + "reth-rpc-types-compat", + "reth-transaction-pool", + "reth-trie", + "revm", + "sha2", + "thiserror", + "tracing", +] + +[[package]] +name = "reth-optimism-primitives" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "reth-primitives", + "reth-primitives-traits", +] + +[[package]] +name = "reth-payload-builder" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types", + "async-trait", + "futures-util", + "metrics", + "reth-ethereum-engine-primitives", + "reth-metrics", + "reth-payload-primitives", + "reth-primitives", + "reth-provider", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-payload-primitives" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types", + "async-trait", + "op-alloy-rpc-types-engine", + "pin-project", + "reth-chain-state", + "reth-chainspec", + "reth-errors", + "reth-primitives", + "reth-transaction-pool", + "serde", + "thiserror", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-primitives" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "bytes", + "c-kzg", + "derive_more", + "k256", + "modular-bitfield", + "once_cell", + "op-alloy-consensus", + "rayon", + "reth-chainspec", + "reth-codecs", + "reth-ethereum-forks", + "reth-optimism-chainspec", + "reth-optimism-forks", + "reth-primitives-traits", + "reth-static-file-types", + "reth-trie-common", + "revm-primitives", + "secp256k1", + "serde", + "zstd", +] + +[[package]] +name = "reth-primitives-traits" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "byteorder", + "bytes", + "derive_more", + "modular-bitfield", + "reth-codecs", + "revm-primitives", + "roaring", + "serde", +] + +[[package]] +name = "reth-provider" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "auto_impl", + "dashmap", + "itertools 0.13.0", + "metrics", + "notify", + "parking_lot", + "rayon", + "reth-blockchain-tree-api", + "reth-chain-state", + "reth-chainspec", + "reth-codecs", + "reth-db", + "reth-db-api", + "reth-errors", + "reth-evm", + "reth-execution-types", + "reth-fs-util", + "reth-metrics", + "reth-network-p2p", + "reth-nippy-jar", + "reth-node-types", + "reth-optimism-primitives", + "reth-primitives", + "reth-prune-types", + "reth-stages-types", + "reth-storage-api", + "reth-storage-errors", + "reth-trie", + "reth-trie-db", + "revm", + "strum", + "tokio", + "tracing", +] + +[[package]] +name = "reth-prune-types" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "bytes", + "derive_more", + "modular-bitfield", + "reth-codecs", + "serde", + "thiserror", +] + +[[package]] +name = "reth-revm" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "reth-chainspec", + "reth-consensus-common", + "reth-execution-errors", + "reth-primitives", + "reth-prune-types", + "reth-storage-api", + "reth-storage-errors", + "revm", +] + +[[package]] +name = "reth-rpc-layer" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-rpc-types-engine", + "http 1.1.0", + "jsonrpsee-http-client", + "pin-project", + "tower 0.4.13", + "tracing", +] + +[[package]] +name = "reth-rpc-types" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types", + "alloy-rpc-types-engine", + "jsonrpsee-types", +] + +[[package]] +name = "reth-rpc-types-compat" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types", + "alloy-rpc-types-eth", + "alloy-serde", + "reth-primitives", + "reth-rpc-types", + "reth-trie-common", +] + +[[package]] +name = "reth-stages-types" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "bytes", + "modular-bitfield", + "reth-codecs", + "reth-trie-common", + "serde", +] + +[[package]] +name = "reth-static-file-types" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "derive_more", + "serde", + "strum", +] + +[[package]] +name = "reth-storage-api" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "auto_impl", + "reth-chainspec", + "reth-db-api", + "reth-db-models", + "reth-execution-types", + "reth-primitives", + "reth-prune-types", + "reth-stages-types", + "reth-storage-errors", + "reth-trie", +] + +[[package]] +name = "reth-storage-errors" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "derive_more", + "reth-fs-util", + "reth-primitives", +] + +[[package]] +name = "reth-tasks" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "auto_impl", + "dyn-clone", + "futures-util", + "metrics", + "reth-metrics", + "thiserror", + "tokio", + "tracing", + "tracing-futures", +] + +[[package]] +name = "reth-tracing" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "clap", + "eyre", + "rolling-file", + "tracing", + "tracing-appender", + "tracing-journald", + "tracing-logfmt", + "tracing-subscriber", +] + +[[package]] +name = "reth-transaction-pool" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "aquamarine", + "auto_impl", + "bitflags 2.6.0", + "futures-util", + "metrics", + "parking_lot", + "reth-chain-state", + "reth-chainspec", + "reth-eth-wire-types", + "reth-execution-types", + "reth-fs-util", + "reth-metrics", + "reth-primitives", + "reth-storage-api", + "reth-tasks", + "revm", + "rustc-hash 2.0.0", + "schnellru", + "serde", + "smallvec", + "thiserror", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-trie" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "auto_impl", + "derive_more", + "itertools 0.13.0", + "metrics", + "rayon", + "reth-execution-errors", + "reth-metrics", + "reth-primitives", + "reth-stages-types", + "reth-storage-errors", + "reth-trie-common", + "revm", + "tracing", +] + +[[package]] +name = "reth-trie-common" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-consensus", + "alloy-genesis", + "alloy-primitives", + "alloy-rlp", + "alloy-trie", + "bytes", + "derive_more", + "itertools 0.13.0", + "nybbles", + "reth-codecs", + "reth-primitives-traits", + "revm-primitives", + "serde", +] + +[[package]] +name = "reth-trie-db" +version = "1.0.7" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "auto_impl", + "derive_more", + "itertools 0.13.0", + "metrics", + "rayon", + "reth-db", + "reth-db-api", + "reth-execution-errors", + "reth-metrics", + "reth-primitives", + "reth-stages-types", + "reth-storage-errors", + "reth-trie", + "reth-trie-common", + "revm", + "tracing", +] + +[[package]] +name = "revm" +version = "14.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9f3f55d0414c3d73902d876ba3d55a654f05fe937089fbf5f34b1ced26d78d5" +dependencies = [ + "auto_impl", + "cfg-if", + "dyn-clone", + "revm-interpreter", + "revm-precompile", + "serde", + "serde_json", +] + +[[package]] +name = "revm-interpreter" +version = "10.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "713dbb271acd13afb06dcd460c1dc43da211e7ac9bc73cdf13528f615f55f96b" +dependencies = [ + "revm-primitives", + "serde", +] + +[[package]] +name = "revm-precompile" +version = "11.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f73010c271d53fa7904e9845338e95f3955eb1200a0355e0abfdb89c41aaa9cd" +dependencies = [ + "aurora-engine-modexp", + "blst", + "c-kzg", + "cfg-if", + "k256", + "once_cell", + "p256", + "revm-primitives", + "ripemd", + "secp256k1", + "sha2", + "substrate-bn", +] + +[[package]] +name = "revm-primitives" +version = "9.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a6bff9dbde3370a5ac9555104117f7e6039b3cc76e8d5d9d01899088beca2a" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "auto_impl", + "bitflags 2.6.0", + "bitvec", + "c-kzg", + "cfg-if", + "dyn-clone", + "enumn", + "hashbrown 0.14.5", + "hex", + "serde", ] [[package]] @@ -2523,6 +4379,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "rlp" version = "0.5.2" @@ -2533,6 +4398,25 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "roaring" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4b84ba6e838ceb47b41de5194a60244fac43d9fe03b71dbe8c5a201081d6d1" +dependencies = [ + "bytemuck", + "byteorder", +] + +[[package]] +name = "rolling-file" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8395b4f860856b740f20a296ea2cd4d823e81a2658cf05ef61be22916026a906" +dependencies = [ + "chrono", +] + [[package]] name = "rollup-boost" version = "0.1.0" @@ -2546,11 +4430,17 @@ dependencies = [ "dotenv", "http 1.1.0", "http-body 0.4.6", + "http-body-util", "hyper", "hyper-util", "jsonrpsee", + "op-alloy-rpc-types", + "op-alloy-rpc-types-engine", "reqwest", + "reth-optimism-payload-builder", + "reth-rpc-layer", "serde", + "serde_json", "thiserror", "tokio", "tower 0.4.13", @@ -2571,6 +4461,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ "alloy-rlp", + "arbitrary", "ark-ff 0.3.0", "ark-ff 0.4.2", "bytes", @@ -2600,6 +4491,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-hash" version = "2.0.0" @@ -2636,7 +4533,7 @@ version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "bitflags", + "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", @@ -2767,6 +4664,17 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schnellru" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" +dependencies = [ + "ahash", + "cfg-if", + "hashbrown 0.13.2", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2787,13 +4695,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "secp256k1" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +dependencies = [ + "rand", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4387882333d3aa8cb20530a17c69a3752e97837832f34f6dccc760e715001d9" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -2861,12 +4788,24 @@ version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ + "indexmap 2.5.0", "itoa", "memchr", "ryu", "serde", ] +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -2879,6 +4818,36 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.5.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2901,6 +4870,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "sha3-asm" version = "0.1.4" @@ -2971,6 +4950,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -3003,6 +4985,9 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] [[package]] name = "spki" @@ -3048,6 +5033,19 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "substrate-bn" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b5bbfa79abbae15dd642ea8176a21a635ff3c00059961d1ea27ad04e5b441c" +dependencies = [ + "byteorder", + "crunchy", + "lazy_static", + "rand", + "rustc-hex", +] + [[package]] name = "subtle" version = "2.6.1" @@ -3103,13 +5101,27 @@ dependencies = [ "futures-core", ] +[[package]] +name = "sysinfo" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "windows", +] + [[package]] name = "system-configuration" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags", + "bitflags 2.6.0", "core-foundation", "system-configuration-sys", ] @@ -3163,6 +5175,26 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "thiserror-impl-no-std" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "thiserror-no-std" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea" +dependencies = [ + "thiserror-impl-no-std", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -3246,7 +5278,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio", + "mio 1.0.2", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -3325,7 +5357,7 @@ version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ - "indexmap", + "indexmap 2.5.0", "toml_datetime", "winnow", ] @@ -3384,6 +5416,18 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" +dependencies = [ + "crossbeam-channel", + "thiserror", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" version = "0.1.27" @@ -3405,6 +5449,27 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "tracing-journald" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba316a74e8fc3c3896a850dba2375928a9fa171b085ecddfc7c054d39970f3fd" +dependencies = [ + "libc", + "tracing-core", + "tracing-subscriber", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -3416,6 +5481,28 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-logfmt" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1f47d22deb79c3f59fcf2a1f00f60cbdc05462bf17d1cd356c1fefa3f444bd" +dependencies = [ + "time", + "tracing", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -3426,12 +5513,15 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] @@ -3491,6 +5581,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -3689,6 +5785,25 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-registry" version = "0.2.0" @@ -3697,7 +5812,7 @@ checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ "windows-result", "windows-strings", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3706,7 +5821,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3716,7 +5831,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ "windows-result", - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", ] [[package]] @@ -3725,7 +5849,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3734,7 +5858,22 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -3743,28 +5882,46 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3777,24 +5934,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3859,3 +6040,31 @@ dependencies = [ "quote", "syn 2.0.77", ] + +[[package]] +name = "zstd" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index d32c33748d14e..cd1183eb37b06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,10 @@ edition = "2021" [dependencies] alloy-primitives = "0.8.3" alloy = { version = "0.3.6", features = ["eips", "rpc-types"] } -alloy-rpc-types-engine = { version = "0.3.6", features = ["serde"] } -alloy-rpc-types-eth = { version = "0.3.6", features = ["serde"] } +op-alloy-rpc-types-engine = "0.2.12" +op-alloy-rpc-types = "0.2.12" +alloy-rpc-types-engine = "0.3.6" +alloy-rpc-types-eth = "0.3.6" tokio = { version = "1", features = ["full"] } tracing = "0.1.4" tracing-subscriber = { version = "0.3.11", features = ["env-filter"] } @@ -20,8 +22,12 @@ http = "1.1.0" dotenv = "0.15.0" tower = "0.4.13" http-body = "0.4.5" +http-body-util = "0.1.2" hyper = { version = "1.4.1", features = ["full"] } hyper-util = { version = "0.1", features = ["full"] } +serde_json = "1.0.96" +reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", branch = "main" } +reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", branch = "main", features = ["optimism"] } [dev-dependencies] anyhow = "1.0" diff --git a/README.md b/README.md index d3fb16cb2913e..f49ba6ff846af 100644 --- a/README.md +++ b/README.md @@ -23,15 +23,7 @@ cargo run -- [OPTIONS] ### Environment Variables -You can also set the options using environment variables: - -- `JWT_TOKEN` -- `L2_URL` -- `BUILDER_URL` -- `RPC_HOST` -- `RPC_PORT` -- `TRACING` -- `LOG_LEVEL` +You can also set the options using environment variables. See .env.example to use the default values. ### Example diff --git a/src/error.rs b/src/error.rs index 84df64912ca7e..ee0e4a5cd408e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,8 +2,10 @@ use thiserror::Error; #[derive(Error, Debug)] pub enum Error { - #[error("Invalid jwt token: {0}")] - InvalidJWTToken(String), + #[error("Invalid arguments: {0}")] + InvalidArgs(String), + #[error("Error Initializing RPC Client: {0}")] + InitRPCClient(String), #[error("Error Initializing RPC Server: {0}")] - InitRPCServerError(String), + InitRPCServer(String), } diff --git a/src/main.rs b/src/main.rs index c80cc79d28790..c909484d6071f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,17 @@ -use std::net::SocketAddr; -use std::sync::Arc; - -use clap::{arg, Parser}; +use alloy_rpc_types_engine::JwtSecret; +use clap::{arg, ArgGroup, Parser}; use dotenv::dotenv; use error::Error; -use http::HeaderMap; -use http::{header::AUTHORIZATION, HeaderValue}; -use jsonrpsee::http_client::HttpClientBuilder; +use http::Uri; +use jsonrpsee::http_client::transport::HttpBackend; +use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::server::Server; use jsonrpsee::RpcModule; use proxy::ProxyLayer; -use server::{EthApiServer, EthEngineApi}; +use reth_rpc_layer::{AuthClientLayer, AuthClientService}; +use server::{EngineApiServer, EthEngineApi}; +use std::sync::Arc; +use std::{net::SocketAddr, path::PathBuf}; use tracing::{info, Level}; use tracing_subscriber::EnvFilter; @@ -20,10 +21,15 @@ mod server; #[derive(Parser, Debug)] #[clap(author, version, about)] +#[clap(group(ArgGroup::new("jwt").required(true).multiple(false).args(&["jwt_token", "jwt_path"])))] struct Args { - /// JWT token to authenticate communication between the consensus and execution clients + /// JWT token for authentication #[arg(long, env)] - jwt_token: String, + jwt_token: Option, + + /// Path to the JWT secret file + #[arg(long, env)] + jwt_path: Option, /// URL of the local l2 execution engine #[arg(long, env)] @@ -33,6 +39,10 @@ struct Args { #[arg(long, env)] builder_url: String, + /// Use the proposer to sync the builder node + #[arg(long, env, default_value = "false")] + boost_sync: bool, + /// Host to run the server on #[arg(long, env, default_value = "0.0.0.0")] rpc_host: String, @@ -58,50 +68,77 @@ async fn main() -> Result<()> { dotenv().ok(); let args: Args = Args::parse(); + // Handle JWT secret + let jwt_secret = match (args.jwt_path, args.jwt_token) { + (Some(file), None) => { + // Read JWT secret from file + JwtSecret::from_file(&file).map_err(|e| Error::InvalidArgs(e.to_string()))? + } + (None, Some(secret)) => { + // Use the provided JWT secret + JwtSecret::from_hex(secret).map_err(|e| Error::InvalidArgs(e.to_string()))? + } + _ => { + // This case should not happen due to ArgGroup + return Err(Error::InvalidArgs( + "Either jwt_file or jwt_secret must be provided".into(), + )); + } + }; + // Initialize logging tracing_subscriber::fmt() .with_env_filter(EnvFilter::new(args.log_level.to_string())) // Set the log level .init(); - // Initialize the local execution engine client - let mut headers = HeaderMap::new(); - headers.insert( - AUTHORIZATION, - match HeaderValue::try_from(format!("Bearer {}", args.jwt_token)) { - Ok(token) => token, - Err(e) => return Err(Error::InvalidJWTToken(e.to_string())), - }, - ); - let l2_client = HttpClientBuilder::new() - .set_headers(headers.clone()) - .build(args.l2_url.clone()) - .unwrap(); + // Initialize the l2 client + let l2_client = create_client(&args.l2_url, jwt_secret)?; - let builder_client = HttpClientBuilder::new() - .set_headers(headers) - .build(args.builder_url) - .unwrap(); + // Initialize the builder client + let builder_client = create_client(&args.builder_url, jwt_secret)?; - let eth_engine_api = EthEngineApi::new(Arc::new(l2_client), Arc::new(builder_client)); + let eth_engine_api = EthEngineApi::new( + Arc::new(l2_client), + Arc::new(builder_client), + args.boost_sync, + ); let mut module: RpcModule<()> = RpcModule::new(()); module .merge(eth_engine_api.into_rpc()) - .map_err(|e| Error::InitRPCServerError(e.to_string()))?; + .map_err(|e| Error::InitRPCServer(e.to_string()))?; // server setup info!("Starting server on :{}", args.rpc_port); - let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new(args.l2_url)); + let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new( + args.l2_url + .parse::() + .map_err(|e| Error::InvalidArgs(e.to_string()))?, + )); let server = Server::builder() .set_http_middleware(service_builder) .build( format!("{}:{}", args.rpc_host, args.rpc_port) .parse::() - .map_err(|e| Error::InitRPCServerError(e.to_string()))?, + .map_err(|e| Error::InitRPCServer(e.to_string()))?, ) .await - .map_err(|e| Error::InitRPCServerError(e.to_string()))?; + .map_err(|e| Error::InitRPCServer(e.to_string()))?; let handle = server.start(module); handle.stopped().await; Ok(()) } + +fn create_client( + url: &str, + jwt_secret: JwtSecret, +) -> Result>> { + // Create a middleware that adds a new JWT token to every request. + let auth_layer = AuthClientLayer::new(jwt_secret); + let client_middleware = tower::ServiceBuilder::new().layer(auth_layer); + + HttpClientBuilder::new() + .set_http_middleware(client_middleware) + .build(url) + .map_err(|e| Error::InitRPCClient(e.to_string())) +} diff --git a/src/proxy.rs b/src/proxy.rs index 8c49a23d633f0..94d57bbb49321 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,21 +1,21 @@ -use hyper::Response; +use http::Uri; use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client; use hyper_util::rt::TokioExecutor; -use jsonrpsee::core::BoxError; +use jsonrpsee::core::{http_helpers, BoxError}; use jsonrpsee::http_client::{HttpBody, HttpRequest, HttpResponse}; use std::task::{Context, Poll}; use std::{future::Future, pin::Pin}; use tower::{Layer, Service}; -use tracing::error; +use tracing::debug; #[derive(Debug, Clone)] pub struct ProxyLayer { - target_url: String, + target_url: Uri, } impl ProxyLayer { - pub fn new(target_url: String) -> Self { + pub fn new(target_url: Uri) -> Self { ProxyLayer { target_url } } } @@ -36,17 +36,17 @@ impl Layer for ProxyLayer { pub struct ProxyService { inner: S, client: Client, - target_url: String, + target_url: Uri, } impl Service> for ProxyService where - S: Service, Response = Response>, + S: Service, Response = HttpResponse> + Send + Clone + 'static, S::Response: 'static, S::Error: Into + 'static, S::Future: Send + 'static, { - type Response = HttpResponse; + type Response = S::Response; type Error = BoxError; type Future = Pin> + Send + 'static>>; @@ -55,15 +55,50 @@ where self.inner.poll_ready(cx).map_err(Into::into) } - fn call(&mut self, mut req: HttpRequest) -> Self::Future { + fn call(&mut self, req: HttpRequest) -> Self::Future { let target_url = self.target_url.clone(); let client = self.client.clone(); + let mut inner = self.inner.clone(); + + #[derive(serde::Deserialize, Debug)] + struct RpcRequest<'a> { + #[serde(borrow)] + method: &'a str, + } + let fut = async move { - *req.uri_mut() = target_url.parse().unwrap(); - client.request(req).await.map_err(|e| { - error!("Error proxying request: {}", e); - Box::new(e) as Box - }) + let (parts, body) = req.into_parts(); + + let (body, _is_single) = + http_helpers::read_body(&parts.headers, body, u32::MAX).await?; + // Deserialize the bytes to find the method + let method: RpcRequest = serde_json::from_slice(&body)?; + + // Create a new body from the bytes + let new_body = HttpBody::from(body.clone()); + + // Reconstruct the request + let mut req = HttpRequest::from_parts(parts, new_body); + + debug!( + message = "received json rpc request for", + method = method.method + ); + if method.method.starts_with("engine_") { + // let rpc server handle engine rpc requests + let res = inner.call(req).await.map_err(|e| e.into())?; + Ok(res) + } else { + // Modify the URI + *req.uri_mut() = target_url; + + // Forward the request + let res = client + .request(req) + .await + .map(|res| res.map(HttpBody::new))?; + Ok(res) + } }; Box::pin(fut) } diff --git a/src/server.rs b/src/server.rs index 1abe90a06b227..3190e2fdc6371 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,48 +1,20 @@ -use std::sync::Arc; - -use alloy::eips::{BlockId, BlockNumberOrTag}; -use alloy::primitives::{Address, B256}; -use alloy::rpc::types::serde_helpers::JsonStorageKey; -use alloy_primitives::U64; +use alloy::primitives::B256; use alloy_rpc_types_engine::{ - ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadAttributes, PayloadId, + ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, }; -use alloy_rpc_types_eth::{Block, EIP1186AccountProofResponse}; use jsonrpsee::core::{async_trait, ClientError, RpcResult}; +use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::HttpClient; use jsonrpsee::proc_macros::rpc; -use jsonrpsee::types::ErrorCode; -use tracing::error; - -#[rpc(server, client, namespace = "eth")] -pub trait EthApi { - /// Returns the chain ID of the current network. - #[method(name = "chainId")] - async fn chain_id(&self) -> RpcResult>; - - /// Returns information about a block by number. - #[method(name = "getBlockByNumber")] - async fn block_by_number( - &self, - number: BlockNumberOrTag, - full: bool, - ) -> RpcResult>; - - /// Returns information about a block by hash. - #[method(name = "getBlockByHash")] - async fn block_by_hash(&self, hash: B256, full: bool) -> RpcResult>; - - /// Returns the account and storage values of the specified account including the Merkle-proof. - /// This call can be used to verify that the data you are pulling from is not tampered with. - #[method(name = "getProof")] - async fn get_proof( - &self, - address: Address, - keys: Vec, - block_number: Option, - ) -> RpcResult; -} +use jsonrpsee::types::error::INVALID_REQUEST_CODE; +use jsonrpsee::types::{ErrorCode, ErrorObject}; +use op_alloy_rpc_types_engine::{ + AsInnerPayload, OptimismExecutionPayloadEnvelopeV3, OptimismPayloadAttributes, +}; +use reth_rpc_layer::AuthClientService; +use std::sync::Arc; +use tracing::{error, info}; #[rpc(server, client, namespace = "engine")] pub trait EngineApi { @@ -50,11 +22,14 @@ pub trait EngineApi { async fn fork_choice_updated_v3( &self, fork_choice_state: ForkchoiceState, - payload_attributes: Option, + payload_attributes: Option, ) -> RpcResult; #[method(name = "getPayloadV3")] - async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult; + async fn get_payload_v3( + &self, + payload_id: PayloadId, + ) -> RpcResult; #[method(name = "newPayloadV3")] async fn new_payload_v3( @@ -65,129 +40,133 @@ pub trait EngineApi { ) -> RpcResult; } -pub struct EthEngineApi { - l2_client: Arc, - builder_client: Arc, +pub struct EthEngineApi> { + l2_client: Arc>, + builder_client: Arc>, + boost_sync: bool, } -impl EthEngineApi { - pub fn new(l2_client: Arc, builder_client: Arc) -> Self { +impl EthEngineApi { + pub fn new( + l2_client: Arc>, + builder_client: Arc>, + boost_sync: bool, + ) -> Self { Self { l2_client, builder_client, + boost_sync, } } } -#[async_trait] -impl EthApiServer for EthEngineApi { - async fn chain_id(&self) -> RpcResult> { - self.l2_client.chain_id().await.map_err(|e| match e { - ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it - other_error => { - error!( - "Error calling chain_id from execution engine: {:?}", - other_error - ); - ErrorCode::InternalError.into() - } - }) - } - - async fn block_by_number( - &self, - number: BlockNumberOrTag, - full: bool, - ) -> RpcResult> { - self.l2_client - .block_by_number(number, full) - .await - .map_err(|e| match e { - ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it - other_error => { - error!( - "Error calling block_by_number from execution engine: {:?}", - other_error - ); - ErrorCode::InternalError.into() - } - }) - } - - async fn block_by_hash(&self, hash: B256, full: bool) -> RpcResult> { - self.l2_client - .block_by_hash(hash, full) - .await - .map_err(|e| match e { - ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it - other_error => { - error!( - "Error calling block_by_hash from execution engine: {:?}", - other_error - ); - ErrorCode::InternalError.into() - } - }) - } - - async fn get_proof( - &self, - address: Address, - keys: Vec, - block_number: Option, - ) -> RpcResult { - self.l2_client - .get_proof(address, keys, block_number) - .await - .map_err(|e| match e { - ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it - other_error => { - error!( - "Error calling get_proof from execution engine: {:?}", - other_error - ); - ErrorCode::InternalError.into() - } - }) - } -} - #[async_trait] impl EngineApiServer for EthEngineApi { async fn fork_choice_updated_v3( &self, fork_choice_state: ForkchoiceState, - payload_attributes: Option, + payload_attributes: Option, ) -> RpcResult { - self.builder_client + info!( + message = "received fork_choice_updated_v3", + "head_block_hash" = %fork_choice_state.head_block_hash, + "has_attributes" = payload_attributes.is_some(), + ); + + let use_tx_pool = payload_attributes + .as_ref() + .map(|attr| !attr.no_tx_pool.unwrap_or_default()); + let should_send_to_builder = if self.boost_sync { + // don't send to builder only if no_tx_pool is set + use_tx_pool.unwrap_or(true) + } else { + // send to builder if there are payload attributes and no_tx_pool is not set + use_tx_pool.is_some() + }; + + if should_send_to_builder { + // async call to builder to trigger payload building and sync + let builder = self.builder_client.clone(); + let attr = payload_attributes.clone(); + tokio::spawn(async move { + builder.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { + let payload_id_str = response.payload_id.map(|id| id.to_string()).unwrap_or_default(); + if response.is_invalid() { + error!(message = "builder rejected fork_choice_updated_v3 with attributes", "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); + } else { + info!(message = "called fork_choice_updated_v3 to builder with payload attributes", "payload_status" = %response.payload_status.status, "payload_id" = payload_id_str); + } + }).map_err(|e| { + error!(message = "error calling fork_choice_updated_v3 to builder", "error" = %e, "head_block_hash" = %fork_choice_state.head_block_hash); + }) + }); + } else { + info!(message = "no payload attributes provided or no_tx_pool is set", "head_block_hash" = %fork_choice_state.head_block_hash); + } + + self.l2_client .fork_choice_updated_v3(fork_choice_state, payload_attributes) .await .map_err(|e| match e { ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it other_error => { error!( - "Error calling fork_choice_updated_v3 from execution engine: {:?}", - other_error + message = "error calling fork_choice_updated_v3 for l2 client", + "error" = %other_error, + "head_block_hash" = %fork_choice_state.head_block_hash, ); ErrorCode::InternalError.into() } }) } - async fn get_payload_v3(&self, payload_id: PayloadId) -> RpcResult { - self.builder_client - .get_payload_v3(payload_id) - .await - .map_err(|e| match e { - ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it - other_error => { - error!( - "Error calling get_payload_v3 from execution engine: {:?}", - other_error - ); - ErrorCode::InternalError.into() - } - }) + async fn get_payload_v3( + &self, + payload_id: PayloadId, + ) -> RpcResult { + info!(message = "received get_payload_v3", "payload_id" = %payload_id); + let l2_client_future = self.l2_client.get_payload_v3(payload_id); + let builder_client_future = Box::pin(async { + let payload = self.builder_client.get_payload_v3(payload_id).await.map_err(|e| { + error!(message = "error calling get_payload_v3 from builder", "error" = %e, "payload_id" = %payload_id); + e + })?; + + info!(message = "received payload from builder", "payload_id" = %payload_id, "block_hash" = %payload.as_v1_payload().block_hash); + + // Send the payload to the local execution engine with engine_newPayload to validate the block from the builder. + // Otherwise, we do not want to risk the network to a halt since op-node will not be able to propose the block. + // If validation fails, return the local block since that one has already been validated. + let payload_status = self.l2_client.new_payload_v3(payload.execution_payload.clone(), vec![], payload.parent_beacon_block_root).await.map_err(|e| { + error!(message = "error calling new_payload_v3 to validate builder payload", "error" = %e, "payload_id" = %payload_id); + e + })?; + if payload_status.is_invalid() { + error!(message = "builder payload was not valid", "payload_status" = %payload_status.status, "payload_id" = %payload_id); + Err(ClientError::Call(ErrorObject::owned( + INVALID_REQUEST_CODE, + "Builder payload was not valid", + None::, + ))) + } else { + info!(message = "received payload status from local execution engine validating builder payload", "payload_id" = %payload_id); + Ok(payload) + } + }); + + let (l2_payload, builder_payload) = tokio::join!(l2_client_future, builder_client_future); + + builder_payload.or(l2_payload).map_err(|e| match e { + ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it + other_error => { + error!( + message = "error calling get_payload_v3", + "error" = %other_error, + "payload_id" = %payload_id + ); + ErrorCode::InternalError.into() + } + }) } async fn new_payload_v3( @@ -196,15 +175,39 @@ impl EngineApiServer for EthEngineApi { versioned_hashes: Vec, parent_beacon_block_root: B256, ) -> RpcResult { - self.builder_client + let block_hash = ExecutionPayload::from(payload.clone()).block_hash(); + info!(message = "received new_payload_v3", "block_hash" = %block_hash); + + // async call to builder to sync the builder node + if self.boost_sync { + let builder = self.builder_client.clone(); + let builder_payload = payload.clone(); + let builder_versioned_hashes = versioned_hashes.clone(); + tokio::spawn(async move { + builder.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await + .map(|response: PayloadStatus| { + if response.is_invalid() { + error!(message = "builder rejected new_payload_v3", "block_hash" = %block_hash); + } else { + info!(message = "called new_payload_v3 to builder", "payload_status" = %response.status, "block_hash" = %block_hash); + } + }).map_err(|e| { + error!(message = "error calling new_payload_v3 to builder", "error" = %e, "block_hash" = %block_hash); + e + }) + }); + } + + self.l2_client .new_payload_v3(payload, versioned_hashes, parent_beacon_block_root) .await .map_err(|e| match e { ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it other_error => { error!( - "Error calling new_payload_v3 from execution engine: {:?}", - other_error + message = "error calling new_payload_v3", + "error" = %other_error, + "block_hash" = %block_hash ); ErrorCode::InternalError.into() } From 85f348ed2d5e436d4665ec1bb95315cf06acc043 Mon Sep 17 00:00:00 2001 From: avalonche Date: Sat, 28 Sep 2024 10:03:23 +1000 Subject: [PATCH 008/429] Add unit tests (flashbots/rollup-boost) --- .github/workflows/test.yml | 26 +++ Cargo.lock | 349 ++++++++++++++++++++++++++----------- Cargo.toml | 6 +- src/main.rs | 108 +++++++++++- src/proxy.rs | 110 ++++++++++++ 5 files changed, 493 insertions(+), 106 deletions(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000000000..fed30a42abb73 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,26 @@ +name: Tests + +on: + push: + branches: + - main + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + + - name: Build + run: cargo build + + - name: Run tests + run: cargo test \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 7363039b16960..e0aa34381d2bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,6 +349,53 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-rpc-types-admin" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fefd12e99dd6b7de387ed13ad047ce2c90d8950ca62fc48b8a457ebb8f936c61" +dependencies = [ + "alloy-genesis", + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-rpc-types-anvil" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25cb45ad7c0930dd62eecf164d2afe4c3d2dd2c82af85680ad1f118e1e5cb83" +dependencies = [ + "alloy-primitives", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-beacon" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7081d2206dca51ce23a06338d78d9b536931cc3f15134fc1c6535eb2b77f18" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "serde", + "serde_with", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types-debug" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9f9033796bb3078d11cc9c839f00e277431ef997db2849a46045fcffee3835" +dependencies = [ + "alloy-primitives", + "serde", +] + [[package]] name = "alloy-rpc-types-engine" version = "0.3.6" @@ -389,6 +436,45 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-rpc-types-mev" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "922d92389e5022650c4c60ffd2f9b2467c3f853764f0f74ff16a23106f9017d5" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-rpc-types-trace" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98db35cd42c90b484377e6bc44d95377a7a38a5ebee996e67754ac0446d542ab" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "alloy-rpc-types-txpool" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bac37082c3b21283b3faf5cc0e08974272aee2f756ce1adeb26db56a5fce0d5" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-serde" version = "0.3.6" @@ -758,6 +844,22 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "assert_cmd" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1835b7f27878de8525dc71410b5a31cdcc5f230aed5ba5df968e09c201b23d" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "libc", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "async-stream" version = "0.3.5" @@ -956,6 +1058,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bstr" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +dependencies = [ + "memchr", + "regex-automata 0.4.7", + "serde", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -1361,6 +1474,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + [[package]] name = "digest" version = "0.9.0" @@ -1382,6 +1501,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "dotenv" version = "0.15.0" @@ -1552,6 +1677,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "fnv" version = "1.0.7" @@ -2554,6 +2688,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "notify" version = "6.1.1" @@ -3036,6 +3176,36 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "predicates" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931" + +[[package]] +name = "predicates-tree" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -3330,7 +3500,7 @@ dependencies = [ [[package]] name = "reth-basic-payload-builder" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -3354,7 +3524,7 @@ dependencies = [ [[package]] name = "reth-blockchain-tree-api" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "reth-consensus", @@ -3367,7 +3537,7 @@ dependencies = [ [[package]] name = "reth-chain-state" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -3391,7 +3561,7 @@ dependencies = [ [[package]] name = "reth-chainspec" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-chains", "alloy-eips", @@ -3404,7 +3574,6 @@ dependencies = [ "op-alloy-rpc-types", "reth-ethereum-forks", "reth-network-peers", - "reth-optimism-forks", "reth-primitives-traits", "reth-trie-common", "serde", @@ -3414,7 +3583,7 @@ dependencies = [ [[package]] name = "reth-codecs" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3431,7 +3600,7 @@ dependencies = [ [[package]] name = "reth-codecs-derive" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "convert_case", "proc-macro2", @@ -3442,7 +3611,7 @@ dependencies = [ [[package]] name = "reth-consensus" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "auto_impl", @@ -3453,7 +3622,7 @@ dependencies = [ [[package]] name = "reth-consensus-common" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "reth-chainspec", @@ -3464,7 +3633,7 @@ dependencies = [ [[package]] name = "reth-db" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "bytes", @@ -3495,9 +3664,8 @@ dependencies = [ [[package]] name = "reth-db-api" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ - "alloy-genesis", "alloy-primitives", "bytes", "derive_more", @@ -3518,7 +3686,7 @@ dependencies = [ [[package]] name = "reth-db-models" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "bytes", @@ -3531,9 +3699,9 @@ dependencies = [ [[package]] name = "reth-engine-primitives" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ - "alloy-primitives", + "reth-chainspec", "reth-execution-types", "reth-payload-primitives", "reth-primitives", @@ -3544,7 +3712,7 @@ dependencies = [ [[package]] name = "reth-errors" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "reth-blockchain-tree-api", "reth-consensus", @@ -3557,10 +3725,9 @@ dependencies = [ [[package]] name = "reth-eth-wire-types" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-chains", - "alloy-eips", "alloy-genesis", "alloy-primitives", "alloy-rlp", @@ -3575,10 +3742,8 @@ dependencies = [ [[package]] name = "reth-ethereum-engine-primitives" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ - "alloy-eips", - "alloy-primitives", "alloy-rlp", "reth-chain-state", "reth-chainspec", @@ -3594,7 +3759,7 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-chains", "alloy-primitives", @@ -3611,7 +3776,7 @@ dependencies = [ [[package]] name = "reth-evm" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-eips", "auto_impl", @@ -3632,7 +3797,7 @@ dependencies = [ [[package]] name = "reth-evm-optimism" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "reth-chainspec", @@ -3642,7 +3807,6 @@ dependencies = [ "reth-execution-types", "reth-optimism-chainspec", "reth-optimism-consensus", - "reth-optimism-forks", "reth-primitives", "reth-prune-types", "reth-revm", @@ -3655,7 +3819,7 @@ dependencies = [ [[package]] name = "reth-execution-errors" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -3671,7 +3835,7 @@ dependencies = [ [[package]] name = "reth-execution-types" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "reth-chainspec", "reth-execution-errors", @@ -3683,7 +3847,7 @@ dependencies = [ [[package]] name = "reth-fs-util" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "serde", "serde_json", @@ -3693,7 +3857,7 @@ dependencies = [ [[package]] name = "reth-libmdbx" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "bitflags 2.6.0", "byteorder", @@ -3709,7 +3873,7 @@ dependencies = [ [[package]] name = "reth-mdbx-sys" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "bindgen", "cc", @@ -3718,7 +3882,7 @@ dependencies = [ [[package]] name = "reth-metrics" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "metrics", "reth-metrics-derive", @@ -3727,7 +3891,7 @@ dependencies = [ [[package]] name = "reth-metrics-derive" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "proc-macro2", "quote", @@ -3738,7 +3902,7 @@ dependencies = [ [[package]] name = "reth-net-banlist" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", ] @@ -3746,9 +3910,8 @@ dependencies = [ [[package]] name = "reth-network-p2p" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ - "alloy-eips", "alloy-primitives", "auto_impl", "derive_more", @@ -3766,7 +3929,7 @@ dependencies = [ [[package]] name = "reth-network-peers" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -3779,7 +3942,7 @@ dependencies = [ [[package]] name = "reth-network-types" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "reth-ethereum-forks", "reth-net-banlist", @@ -3791,7 +3954,7 @@ dependencies = [ [[package]] name = "reth-nippy-jar" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "anyhow", "bincode", @@ -3808,7 +3971,7 @@ dependencies = [ [[package]] name = "reth-node-types" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "reth-chainspec", "reth-db-api", @@ -3818,7 +3981,7 @@ dependencies = [ [[package]] name = "reth-optimism-chainspec" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-chains", "alloy-primitives", @@ -3826,7 +3989,6 @@ dependencies = [ "once_cell", "reth-chainspec", "reth-ethereum-forks", - "reth-optimism-forks", "reth-primitives-traits", "serde_json", ] @@ -3834,44 +3996,30 @@ dependencies = [ [[package]] name = "reth-optimism-consensus" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "reth-chainspec", "reth-consensus", "reth-consensus-common", - "reth-optimism-forks", "reth-primitives", "reth-trie-common", "tracing", ] -[[package]] -name = "reth-optimism-forks" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" -dependencies = [ - "alloy-chains", - "alloy-primitives", - "once_cell", - "reth-ethereum-forks", -] - [[package]] name = "reth-optimism-payload-builder" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "alloy-rlp", - "op-alloy-rpc-types-engine", "reth-basic-payload-builder", "reth-chain-state", "reth-chainspec", "reth-evm", "reth-evm-optimism", "reth-execution-types", - "reth-optimism-forks", "reth-payload-builder", "reth-payload-primitives", "reth-primitives", @@ -3887,31 +4035,24 @@ dependencies = [ "tracing", ] -[[package]] -name = "reth-optimism-primitives" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" -dependencies = [ - "alloy-primitives", - "reth-primitives", - "reth-primitives-traits", -] - [[package]] name = "reth-payload-builder" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", - "alloy-rpc-types", - "async-trait", "futures-util", "metrics", + "pin-project", + "reth-errors", "reth-ethereum-engine-primitives", "reth-metrics", "reth-payload-primitives", "reth-primitives", "reth-provider", + "reth-rpc-types", + "reth-transaction-pool", + "thiserror", "tokio", "tokio-stream", "tracing", @@ -3920,32 +4061,28 @@ dependencies = [ [[package]] name = "reth-payload-primitives" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", - "alloy-rpc-types", - "async-trait", - "op-alloy-rpc-types-engine", - "pin-project", "reth-chain-state", "reth-chainspec", "reth-errors", "reth-primitives", + "reth-rpc-types", "reth-transaction-pool", "serde", "thiserror", "tokio", - "tokio-stream", - "tracing", ] [[package]] name = "reth-primitives" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-consensus", "alloy-eips", + "alloy-genesis", "alloy-primitives", "alloy-rlp", "bytes", @@ -3960,7 +4097,6 @@ dependencies = [ "reth-codecs", "reth-ethereum-forks", "reth-optimism-chainspec", - "reth-optimism-forks", "reth-primitives-traits", "reth-static-file-types", "reth-trie-common", @@ -3973,7 +4109,7 @@ dependencies = [ [[package]] name = "reth-primitives-traits" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-consensus", "alloy-eips", @@ -3993,7 +4129,7 @@ dependencies = [ [[package]] name = "reth-provider" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -4019,7 +4155,6 @@ dependencies = [ "reth-network-p2p", "reth-nippy-jar", "reth-node-types", - "reth-optimism-primitives", "reth-primitives", "reth-prune-types", "reth-stages-types", @@ -4036,7 +4171,7 @@ dependencies = [ [[package]] name = "reth-prune-types" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "bytes", @@ -4050,7 +4185,7 @@ dependencies = [ [[package]] name = "reth-revm" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "reth-chainspec", "reth-consensus-common", @@ -4065,7 +4200,7 @@ dependencies = [ [[package]] name = "reth-rpc-layer" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-rpc-types-engine", "http 1.1.0", @@ -4078,24 +4213,32 @@ dependencies = [ [[package]] name = "reth-rpc-types" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "alloy-rpc-types", + "alloy-rpc-types-admin", + "alloy-rpc-types-anvil", + "alloy-rpc-types-beacon", + "alloy-rpc-types-debug", "alloy-rpc-types-engine", + "alloy-rpc-types-mev", + "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", + "alloy-serde", "jsonrpsee-types", + "op-alloy-rpc-types", + "op-alloy-rpc-types-engine", ] [[package]] name = "reth-rpc-types-compat" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-rpc-types", - "alloy-rpc-types-eth", - "alloy-serde", "reth-primitives", "reth-rpc-types", "reth-trie-common", @@ -4104,7 +4247,7 @@ dependencies = [ [[package]] name = "reth-stages-types" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "bytes", @@ -4117,7 +4260,7 @@ dependencies = [ [[package]] name = "reth-static-file-types" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "derive_more", @@ -4128,7 +4271,7 @@ dependencies = [ [[package]] name = "reth-storage-api" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -4147,7 +4290,7 @@ dependencies = [ [[package]] name = "reth-storage-errors" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -4160,7 +4303,7 @@ dependencies = [ [[package]] name = "reth-tasks" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "auto_impl", "dyn-clone", @@ -4176,7 +4319,7 @@ dependencies = [ [[package]] name = "reth-tracing" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "clap", "eyre", @@ -4191,7 +4334,7 @@ dependencies = [ [[package]] name = "reth-transaction-pool" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-eips", "alloy-primitives", @@ -4225,7 +4368,7 @@ dependencies = [ [[package]] name = "reth-trie" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -4247,7 +4390,7 @@ dependencies = [ [[package]] name = "reth-trie-common" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-consensus", "alloy-genesis", @@ -4267,7 +4410,7 @@ dependencies = [ [[package]] name = "reth-trie-db" version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#ab66f58e5cf4f6030621ca3a63fd5a5af1ac89ed" +source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -4426,6 +4569,7 @@ dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", "anyhow", + "assert_cmd", "clap", "dotenv", "http 1.1.0", @@ -4436,6 +4580,7 @@ dependencies = [ "jsonrpsee", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", + "predicates", "reqwest", "reth-optimism-payload-builder", "reth-rpc-layer", @@ -5155,6 +5300,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "thiserror" version = "1.0.63" diff --git a/Cargo.toml b/Cargo.toml index cd1183eb37b06..7fc589e01201f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,8 +26,10 @@ http-body-util = "0.1.2" hyper = { version = "1.4.1", features = ["full"] } hyper-util = { version = "0.1", features = ["full"] } serde_json = "1.0.96" -reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", branch = "main" } -reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", branch = "main", features = ["optimism"] } +reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.7" } +reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.7", features = ["optimism"] } [dev-dependencies] anyhow = "1.0" +assert_cmd = "2.0.10" +predicates = "3.1.2" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c909484d6071f..771c6410fddc1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -68,6 +68,11 @@ async fn main() -> Result<()> { dotenv().ok(); let args: Args = Args::parse(); + // Initialize logging + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::new(args.log_level.to_string())) // Set the log level + .init(); + // Handle JWT secret let jwt_secret = match (args.jwt_path, args.jwt_token) { (Some(file), None) => { @@ -86,11 +91,6 @@ async fn main() -> Result<()> { } }; - // Initialize logging - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::new(args.log_level.to_string())) // Set the log level - .init(); - // Initialize the l2 client let l2_client = create_client(&args.l2_url, jwt_secret)?; @@ -142,3 +142,101 @@ fn create_client( .build(url) .map_err(|e| Error::InitRPCClient(e.to_string())) } + +#[cfg(test)] +mod tests { + use assert_cmd::Command; + use jsonrpsee::core::client::ClientT; + use jsonrpsee::http_client::transport::Error as TransportError; + use jsonrpsee::{ + core::ClientError, + rpc_params, + server::{ServerBuilder, ServerHandle}, + }; + use predicates::prelude::*; + use reth_rpc_layer::{AuthLayer, JwtAuthValidator}; + use std::result::Result; + + use super::*; + + const AUTH_PORT: u32 = 8551; + const AUTH_ADDR: &str = "0.0.0.0"; + const SECRET: &str = "f79ae8046bc11c9927afe911db7143c51a806c4a537cc08e0d37140b0192f430"; + + #[test] + fn test_invalid_args() { + let mut cmd = Command::cargo_bin("rollup-boost").unwrap(); + cmd.arg("--invalid-arg"); + + cmd.assert().failure().stderr(predicate::str::contains( + "error: unexpected argument '--invalid-arg' found", + )); + } + + #[tokio::test] + async fn test_create_client() { + valid_jwt().await; + invalid_jwt().await; + } + + async fn valid_jwt() { + let secret = JwtSecret::from_hex(SECRET).unwrap(); + let url = format!("http://{}:{}", AUTH_ADDR, AUTH_PORT); + let client = create_client(url.as_str(), secret); + let response = send_request(client.unwrap()).await; + assert!(response.is_ok()); + assert_eq!(response.unwrap(), "You are the dark lord"); + } + + async fn invalid_jwt() { + let secret = JwtSecret::random(); + let url = format!("http://{}:{}", AUTH_ADDR, AUTH_PORT); + let client = create_client(url.as_str(), secret); + let response = send_request(client.unwrap()).await; + assert!(response.is_err()); + assert!(matches!( + response.unwrap_err(), + ClientError::Transport(e) + if matches!(e.downcast_ref::(), Some(TransportError::Rejected { status_code: 401 })) + )); + } + + async fn send_request( + client: HttpClient>, + ) -> Result { + let server = spawn_server().await; + + let response = client + .request::("greet_melkor", rpc_params![]) + .await; + + server.stop().unwrap(); + server.stopped().await; + + response + } + + /// Spawn a new RPC server equipped with a `JwtLayer` auth middleware. + async fn spawn_server() -> ServerHandle { + let secret = JwtSecret::from_hex(SECRET).unwrap(); + let addr = format!("{AUTH_ADDR}:{AUTH_PORT}"); + let validator = JwtAuthValidator::new(secret); + let layer = AuthLayer::new(validator); + let middleware = tower::ServiceBuilder::default().layer(layer); + + // Create a layered server + let server = ServerBuilder::default() + .set_http_middleware(middleware) + .build(addr.parse::().unwrap()) + .await + .unwrap(); + + // Create a mock rpc module + let mut module = RpcModule::new(()); + module + .register_method("greet_melkor", |_, _, _| "You are the dark lord") + .unwrap(); + + server.start(module) + } +} diff --git a/src/proxy.rs b/src/proxy.rs index 94d57bbb49321..2e8f80b38a6a1 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -103,3 +103,113 @@ where Box::pin(fut) } } + +#[cfg(test)] +mod tests { + use std::net::SocketAddr; + + use jsonrpsee::{ + core::{client::ClientT, ClientError}, + http_client::HttpClient, + rpc_params, + server::{ServerBuilder, ServerHandle}, + types::{ErrorCode, ErrorObject}, + RpcModule, + }; + + use super::*; + + const PORT: u32 = 8552; + const ADDR: &str = "0.0.0.0"; + const PROXY_PORT: u32 = 8553; + + #[tokio::test] + async fn test_proxy_service() { + proxy_success().await; + proxy_failure().await; + does_not_proxy_engine_method().await; + } + + async fn proxy_success() { + let response = send_request("greet_melkor").await; + assert!(response.is_ok()); + assert_eq!(response.unwrap(), "You are the dark lord"); + } + + async fn proxy_failure() { + let response = send_request("non_existent_method").await; + assert!(response.is_err()); + let expected_error = ErrorObject::from(ErrorCode::MethodNotFound).into_owned(); + assert!(matches!( + response.unwrap_err(), + ClientError::Call(e) if e == expected_error + )); + } + + async fn does_not_proxy_engine_method() { + let response = send_request("engine_method").await; + assert!(response.is_ok()); + assert_eq!(response.unwrap(), "engine response"); + } + + async fn send_request(method: &str) -> Result { + let server = spawn_server().await; + let proxy_server = spawn_proxy_server().await; + let proxy_client = HttpClient::builder() + .build(format!("http://{ADDR}:{PORT}")) + .unwrap(); + + let response = proxy_client + .request::(method, rpc_params![]) + .await; + + server.stop().unwrap(); + server.stopped().await; + proxy_server.stop().unwrap(); + proxy_server.stopped().await; + + response + } + + async fn spawn_server() -> ServerHandle { + let server = ServerBuilder::default() + .build( + format!("{ADDR}:{PROXY_PORT}") + .parse::() + .unwrap(), + ) + .await + .unwrap(); + + // Create a mock rpc module + let mut module = RpcModule::new(()); + module + .register_method("greet_melkor", |_, _, _| "You are the dark lord") + .unwrap(); + + server.start(module) + } + + /// Spawn a new RPC server with a proxy layer. + async fn spawn_proxy_server() -> ServerHandle { + let addr = format!("{ADDR}:{PORT}"); + let proxy_layer = ProxyLayer::new(format!("http://{ADDR}:{PROXY_PORT}").parse().unwrap()); + // Create a layered server + let server = ServerBuilder::default() + .set_http_middleware(tower::ServiceBuilder::new().layer(proxy_layer)) + .build(addr.parse::().unwrap()) + .await + .unwrap(); + + // Create a mock rpc module + let mut module = RpcModule::new(()); + module + .register_method("engine_method", |_, _, _| "engine response") + .unwrap(); + module + .register_method("non_existent_method", |_, _, _| "no proxy response") + .unwrap(); + + server.start(module) + } +} From 31f02419a0554e0e600b153bc85b40fa3aec9163 Mon Sep 17 00:00:00 2001 From: avalonche Date: Sat, 28 Sep 2024 14:52:36 +1000 Subject: [PATCH 009/429] Add docker and release workflow (flashbots/rollup-boost) --- .github/workflows/release.yml | 78 +++++++++++++++++++++++++++++++++++ Dockerfile | 20 +++++++++ 2 files changed, 98 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 Dockerfile diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000000..06b0a92327f3c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,78 @@ +name: Release + +on: + workflow_dispatch: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + docker-image: + name: Publish Docker Image + runs-on: ubuntu-latest + + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Get tag version + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Print version + run: | + echo $RELEASE_VERSION + echo ${{ env.RELEASE_VERSION }} + + - name: Extract metadata (tags, labels) for Docker images + id: meta + uses: docker/metadata-action@v4 + with: + images: flashbots/rollup-boost + tags: | + type=sha + type=pep440,pattern={{version}} + type=pep440,pattern={{major}}.{{minor}} + type=raw,value=latest,enable=${{ !contains(env.RELEASE_VERSION, '-') }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + username: ${{ secrets.FLASHBOTS_DOCKERHUB_USERNAME }} + password: ${{ secrets.FLASHBOTS_DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v3 + with: + context: . + push: true + build-args: | + VERSION=${{ env.RELEASE_VERSION }} + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + github-release: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Create release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: ${{ github.ref }} + draft: true + prerelease: false diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000..d7636168d6e73 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM lukemathwalker/cargo-chef:latest AS chef +WORKDIR /app + +RUN apt-get update && apt-get -y upgrade && apt-get install -y libclang-dev pkg-config + +FROM chef AS planner +COPY ./Cargo.toml ./Cargo.lock ./ +COPY ./src ./src +RUN cargo chef prepare + +FROM chef AS builder +COPY --from=planner /app/recipe.json . +RUN cargo chef cook --release +COPY . . +RUN cargo build --release + +FROM debian:stable-slim AS runtime +WORKDIR /app +COPY --from=builder /app/target/release/rollup-boost /usr/local/bin/ +ENTRYPOINT ["/usr/local/bin/rollup-boost"] \ No newline at end of file From 507131e57093bfd3331553e374aee0b73620d7de Mon Sep 17 00:00:00 2001 From: Daniel Sukoneck Date: Mon, 30 Sep 2024 12:53:36 -0700 Subject: [PATCH 010/429] use warp runner for release action (flashbots/rollup-boost) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 06b0a92327f3c..98977e728cf6e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ permissions: jobs: docker-image: name: Publish Docker Image - runs-on: ubuntu-latest + runs-on: warp-ubuntu-latest-x64-16x steps: - name: Checkout sources From 4bcbb5d30154434e741e92f4b4495f48ce37cdb4 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Wed, 9 Oct 2024 16:31:04 +0100 Subject: [PATCH 011/429] Add boost-sync flag to README (flashbots/rollup-boost) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f49ba6ff846af..43fe1f827149f 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ cargo run -- [OPTIONS] - `--rpc-port `: Port to run the server on (default: 8081) - `--tracing`: Enable tracing (default: false) - `--log-level `: Log level (default: info) +- `--boost-sync`: Enable syncing the builder with the proposer op-node (default: false) ### Environment Variables @@ -56,4 +57,4 @@ The code in this project is free software under the [MIT License](/LICENSE). --- -Made with ☀️ by the ⚡🤖 collective. \ No newline at end of file +Made with ☀️ by the ⚡🤖 collective. From 8dc00a18299e3bb36a14c4fe459da32b4f7bacc8 Mon Sep 17 00:00:00 2001 From: avalonche Date: Fri, 25 Oct 2024 07:21:07 +1100 Subject: [PATCH 012/429] Add optional builder jwt token param (flashbots/rollup-boost) --- src/main.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 771c6410fddc1..09137b3234986 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,14 @@ struct Args { #[arg(long, env)] jwt_path: Option, + /// JWT token for authentication for the builder + #[arg(long, env)] + builder_jwt_token: Option, + + /// Path to the JWT secret file for the builder + #[arg(long, env)] + builder_jwt_path: Option, + /// URL of the local l2 execution engine #[arg(long, env)] l2_url: String, @@ -91,11 +99,21 @@ async fn main() -> Result<()> { } }; + let builder_jwt_secret = match (args.builder_jwt_path, args.builder_jwt_token) { + (Some(file), None) => { + JwtSecret::from_file(&file).map_err(|e| Error::InvalidArgs(e.to_string()))? + } + (None, Some(secret)) => { + JwtSecret::from_hex(secret).map_err(|e| Error::InvalidArgs(e.to_string()))? + } + _ => jwt_secret, + }; + // Initialize the l2 client let l2_client = create_client(&args.l2_url, jwt_secret)?; // Initialize the builder client - let builder_client = create_client(&args.builder_url, jwt_secret)?; + let builder_client = create_client(&args.builder_url, builder_jwt_secret)?; let eth_engine_api = EthEngineApi::new( Arc::new(l2_client), From a40e0511dc59009ef23a9fadb6f2d4e38a99870b Mon Sep 17 00:00:00 2001 From: avalonche Date: Fri, 25 Oct 2024 16:52:40 +1100 Subject: [PATCH 013/429] Proxy eth_sendRawTransaction to builder (flashbots/rollup-boost) --- src/proxy.rs | 4 +++- src/server.rs | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/proxy.rs b/src/proxy.rs index 2e8f80b38a6a1..c2b2cdb65b45b 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -9,6 +9,8 @@ use std::{future::Future, pin::Pin}; use tower::{Layer, Service}; use tracing::debug; +const PROXY_METHODS: [&str; 2] = ["engine_", "eth_sendRawTransaction"]; + #[derive(Debug, Clone)] pub struct ProxyLayer { target_url: Uri, @@ -84,7 +86,7 @@ where message = "received json rpc request for", method = method.method ); - if method.method.starts_with("engine_") { + if PROXY_METHODS.iter().any(|&m| method.method.starts_with(m)) { // let rpc server handle engine rpc requests let res = inner.call(req).await.map_err(|e| e.into())?; Ok(res) diff --git a/src/server.rs b/src/server.rs index 3190e2fdc6371..4fcb12593edef 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,4 +1,5 @@ use alloy::primitives::B256; +use alloy_primitives::Bytes; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, @@ -14,7 +15,7 @@ use op_alloy_rpc_types_engine::{ }; use reth_rpc_layer::AuthClientService; use std::sync::Arc; -use tracing::{error, info}; +use tracing::{debug, error, info}; #[rpc(server, client, namespace = "engine")] pub trait EngineApi { @@ -40,6 +41,12 @@ pub trait EngineApi { ) -> RpcResult; } +#[rpc(server, client, namespace = "eth")] +pub trait EthApi { + #[method(name = "sendRawTransaction")] + async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult; +} + pub struct EthEngineApi> { l2_client: Arc>, builder_client: Arc>, @@ -60,6 +67,36 @@ impl EthEngineApi { } } +#[async_trait] +impl EthApiServer for EthEngineApi { + async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult { + debug!( + message = "received send_raw_transaction", + "bytes_len" = bytes.len() + ); + let builder = self.builder_client.clone(); + let tx_bytes = bytes.clone(); + tokio::spawn(async move { + builder.send_raw_transaction(tx_bytes).await.map_err(|e| { + error!(message = "error calling send_raw_transaction for builder", "error" = %e); + }) + }); + self.l2_client + .send_raw_transaction(bytes) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it + other_error => { + error!( + message = "error calling send_raw_transaction for l2 client", + "error" = %other_error, + ); + ErrorCode::InternalError.into() + } + }) + } +} + #[async_trait] impl EngineApiServer for EthEngineApi { async fn fork_choice_updated_v3( From 0b5a5f54e56d3d7df1534a206551f8c280301f4f Mon Sep 17 00:00:00 2001 From: shana Date: Sat, 26 Oct 2024 02:57:08 +1100 Subject: [PATCH 014/429] Update src/proxy.rs (flashbots/rollup-boost) Co-authored-by: Ferran Borreguero --- src/proxy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proxy.rs b/src/proxy.rs index c2b2cdb65b45b..3b1c674eed9af 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -9,7 +9,7 @@ use std::{future::Future, pin::Pin}; use tower::{Layer, Service}; use tracing::debug; -const PROXY_METHODS: [&str; 2] = ["engine_", "eth_sendRawTransaction"]; +const MULTIPLEX_METHODS: [&str; 2] = ["engine_", "eth_sendRawTransaction"]; #[derive(Debug, Clone)] pub struct ProxyLayer { From a5a0190a2dc57f17c9db74d06c1910e7fd313cde Mon Sep 17 00:00:00 2001 From: avalonche Date: Sat, 26 Oct 2024 02:52:31 +1100 Subject: [PATCH 015/429] Add health check endpoint (flashbots/rollup-boost) --- src/proxy.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/proxy.rs b/src/proxy.rs index 3b1c674eed9af..70529a588db41 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -58,6 +58,11 @@ where } fn call(&mut self, req: HttpRequest) -> Self::Future { + match req.uri().path() { + "/healthz" => return Box::pin(async { Ok(Self::Response::new(HttpBody::from("OK"))) }), + _ => {} + }; + let target_url = self.target_url.clone(); let client = self.client.clone(); let mut inner = self.inner.clone(); @@ -110,6 +115,7 @@ where mod tests { use std::net::SocketAddr; + use http_body_util::BodyExt; use jsonrpsee::{ core::{client::ClientT, ClientError}, http_client::HttpClient, @@ -130,6 +136,7 @@ mod tests { proxy_success().await; proxy_failure().await; does_not_proxy_engine_method().await; + health_check().await; } async fn proxy_success() { @@ -154,6 +161,25 @@ mod tests { assert_eq!(response.unwrap(), "engine response"); } + async fn health_check() { + let proxy_server = spawn_proxy_server().await; + // Create a new HTTP client + let client: Client = + Client::builder(TokioExecutor::new()).build_http(); + + // Test the health check endpoint + let health_check_url = format!("http://{ADDR}:{PORT}/healthz"); + let health_response = client.get(health_check_url.parse::().unwrap()).await; + assert!(health_response.is_ok()); + let b = health_response.unwrap().into_body().collect().await.unwrap().to_bytes(); + // Convert the collected bytes to a string + let body_string = String::from_utf8(b.to_vec()).unwrap(); + assert_eq!(body_string, "OK"); + + proxy_server.stop().unwrap(); + proxy_server.stopped().await; + } + async fn send_request(method: &str) -> Result { let server = spawn_server().await; let proxy_server = spawn_proxy_server().await; From 3f8bc47e961b2681f9760db381c763c62486bca9 Mon Sep 17 00:00:00 2001 From: avalonche Date: Sat, 26 Oct 2024 07:30:55 +1100 Subject: [PATCH 016/429] fix build (flashbots/rollup-boost) --- src/proxy.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/proxy.rs b/src/proxy.rs index 70529a588db41..a1e21567a61cf 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -60,6 +60,7 @@ where fn call(&mut self, req: HttpRequest) -> Self::Future { match req.uri().path() { "/healthz" => return Box::pin(async { Ok(Self::Response::new(HttpBody::from("OK"))) }), + "/metrics" => {} _ => {} }; @@ -91,7 +92,10 @@ where message = "received json rpc request for", method = method.method ); - if PROXY_METHODS.iter().any(|&m| method.method.starts_with(m)) { + if MULTIPLEX_METHODS + .iter() + .any(|&m| method.method.starts_with(m)) + { // let rpc server handle engine rpc requests let res = inner.call(req).await.map_err(|e| e.into())?; Ok(res) @@ -136,6 +140,7 @@ mod tests { proxy_success().await; proxy_failure().await; does_not_proxy_engine_method().await; + does_not_proxy_eth_send_raw_transaction_method().await; health_check().await; } @@ -161,6 +166,12 @@ mod tests { assert_eq!(response.unwrap(), "engine response"); } + async fn does_not_proxy_eth_send_raw_transaction_method() { + let response = send_request("eth_sendRawTransaction").await; + assert!(response.is_ok()); + assert_eq!(response.unwrap(), "raw transaction response"); + } + async fn health_check() { let proxy_server = spawn_proxy_server().await; // Create a new HTTP client @@ -171,7 +182,13 @@ mod tests { let health_check_url = format!("http://{ADDR}:{PORT}/healthz"); let health_response = client.get(health_check_url.parse::().unwrap()).await; assert!(health_response.is_ok()); - let b = health_response.unwrap().into_body().collect().await.unwrap().to_bytes(); + let b = health_response + .unwrap() + .into_body() + .collect() + .await + .unwrap() + .to_bytes(); // Convert the collected bytes to a string let body_string = String::from_utf8(b.to_vec()).unwrap(); assert_eq!(body_string, "OK"); @@ -234,6 +251,11 @@ mod tests { module .register_method("engine_method", |_, _, _| "engine response") .unwrap(); + module + .register_method("eth_sendRawTransaction", |_, _, _| { + "raw transaction response" + }) + .unwrap(); module .register_method("non_existent_method", |_, _, _| "no proxy response") .unwrap(); From dc01d32b7f3a967684984f1785b6695a192b5c3d Mon Sep 17 00:00:00 2001 From: avalonche Date: Fri, 25 Oct 2024 17:58:14 +1100 Subject: [PATCH 017/429] Add metrics (flashbots/rollup-boost) --- Cargo.lock | 85 ++++++++++++++++++++++++++++++++------------------ Cargo.toml | 3 ++ src/main.rs | 5 +++ src/metrics.rs | 18 +++++++++++ src/server.rs | 59 ++++++++++++++++++++++------------- 5 files changed, 119 insertions(+), 51 deletions(-) create mode 100644 src/metrics.rs diff --git a/Cargo.lock b/Cargo.lock index e0aa34381d2bb..3fc72ff5a2577 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1733,9 +1733,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1748,9 +1748,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1758,15 +1758,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1775,15 +1775,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -1792,21 +1792,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -2595,6 +2595,28 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "metrics" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae428771d17306715c5091d446327d1cfdedc82185c65ba8423ab404e45bf10" +dependencies = [ + "ahash", + "portable-atomic", +] + +[[package]] +name = "metrics-derive" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3dbdd96ed57d565ec744cba02862d707acf373c5772d152abae6ec5c4e24f6c" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.77", +] + [[package]] name = "mime" version = "0.3.17" @@ -3506,7 +3528,7 @@ dependencies = [ "alloy-rlp", "futures-core", "futures-util", - "metrics", + "metrics 0.23.0", "reth-chainspec", "reth-metrics", "reth-payload-builder", @@ -3543,7 +3565,7 @@ dependencies = [ "alloy-primitives", "auto_impl", "derive_more", - "metrics", + "metrics 0.23.0", "parking_lot", "pin-project", "reth-chainspec", @@ -3639,7 +3661,7 @@ dependencies = [ "bytes", "derive_more", "eyre", - "metrics", + "metrics 0.23.0", "page_size", "paste", "reth-db-api", @@ -3669,7 +3691,7 @@ dependencies = [ "alloy-primitives", "bytes", "derive_more", - "metrics", + "metrics 0.23.0", "modular-bitfield", "parity-scale-codec", "reth-codecs", @@ -3781,7 +3803,7 @@ dependencies = [ "alloy-eips", "auto_impl", "futures-util", - "metrics", + "metrics 0.23.0", "reth-chainspec", "reth-execution-errors", "reth-execution-types", @@ -3884,7 +3906,7 @@ name = "reth-metrics" version = "1.0.7" source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ - "metrics", + "metrics 0.23.0", "reth-metrics-derive", ] @@ -4042,7 +4064,7 @@ source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4f dependencies = [ "alloy-primitives", "futures-util", - "metrics", + "metrics 0.23.0", "pin-project", "reth-errors", "reth-ethereum-engine-primitives", @@ -4137,7 +4159,7 @@ dependencies = [ "auto_impl", "dashmap", "itertools 0.13.0", - "metrics", + "metrics 0.23.0", "notify", "parking_lot", "rayon", @@ -4308,7 +4330,7 @@ dependencies = [ "auto_impl", "dyn-clone", "futures-util", - "metrics", + "metrics 0.23.0", "reth-metrics", "thiserror", "tokio", @@ -4343,7 +4365,7 @@ dependencies = [ "auto_impl", "bitflags 2.6.0", "futures-util", - "metrics", + "metrics 0.23.0", "parking_lot", "reth-chain-state", "reth-chainspec", @@ -4375,7 +4397,7 @@ dependencies = [ "auto_impl", "derive_more", "itertools 0.13.0", - "metrics", + "metrics 0.23.0", "rayon", "reth-execution-errors", "reth-metrics", @@ -4417,7 +4439,7 @@ dependencies = [ "auto_impl", "derive_more", "itertools 0.13.0", - "metrics", + "metrics 0.23.0", "rayon", "reth-db", "reth-db-api", @@ -4572,12 +4594,15 @@ dependencies = [ "assert_cmd", "clap", "dotenv", + "futures", "http 1.1.0", "http-body 0.4.6", "http-body-util", "hyper", "hyper-util", "jsonrpsee", + "metrics 0.24.0", + "metrics-derive", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", "predicates", diff --git a/Cargo.toml b/Cargo.toml index 7fc589e01201f..e7f768c1a1024 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,9 @@ hyper-util = { version = "0.1", features = ["full"] } serde_json = "1.0.96" reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.7" } reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.7", features = ["optimism"] } +futures = "0.3.31" +metrics-derive = "0.1" +metrics = "0.24.0" [dev-dependencies] anyhow = "1.0" diff --git a/src/main.rs b/src/main.rs index 09137b3234986..b46c792d6851d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::server::Server; use jsonrpsee::RpcModule; +use metrics::ServerMetrics; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService}; use server::{EngineApiServer, EthEngineApi}; @@ -16,6 +17,7 @@ use tracing::{info, Level}; use tracing_subscriber::EnvFilter; mod error; +mod metrics; mod proxy; mod server; @@ -81,6 +83,8 @@ async fn main() -> Result<()> { .with_env_filter(EnvFilter::new(args.log_level.to_string())) // Set the log level .init(); + let metrics = ServerMetrics::default(); + // Handle JWT secret let jwt_secret = match (args.jwt_path, args.jwt_token) { (Some(file), None) => { @@ -119,6 +123,7 @@ async fn main() -> Result<()> { Arc::new(l2_client), Arc::new(builder_client), args.boost_sync, + metrics, ); let mut module: RpcModule<()> = RpcModule::new(()); module diff --git a/src/metrics.rs b/src/metrics.rs new file mode 100644 index 0000000000000..c648252ff35d0 --- /dev/null +++ b/src/metrics.rs @@ -0,0 +1,18 @@ +use metrics::Counter; +use metrics_derive::Metrics; + +#[derive(Metrics)] +#[metrics(scope = "metrics_custom")] +pub struct ServerMetrics { + #[metric(describe = "Count of fork_choice_updated_v3 calls proxied to the builder")] + pub fcu_count: Counter, + + #[metric(describe = "Count of new_payload_v3 calls proxied to the builder")] + pub new_payload_count: Counter, + + #[metric(describe = "Count of get_payload_v3 calls proxied to the builder")] + pub get_payload_count: Counter, + + #[metric(describe = "Count of send_raw_transaction calls proxied to the builder")] + pub send_raw_tx_count: Counter, +} diff --git a/src/server.rs b/src/server.rs index 4fcb12593edef..cf488b6d64ac6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -17,6 +17,9 @@ use reth_rpc_layer::AuthClientService; use std::sync::Arc; use tracing::{debug, error, info}; +use crate::metrics::ServerMetrics; +use crate::selector::{DefaultPayloadSelector, PayloadSelector}; + #[rpc(server, client, namespace = "engine")] pub trait EngineApi { #[method(name = "forkchoiceUpdatedV3")] @@ -51,6 +54,7 @@ pub struct EthEngineApi> { l2_client: Arc>, builder_client: Arc>, boost_sync: bool, + metrics: ServerMetrics, } impl EthEngineApi { @@ -58,11 +62,14 @@ impl EthEngineApi { l2_client: Arc>, builder_client: Arc>, boost_sync: bool, + metrics: ServerMetrics, ) -> Self { Self { l2_client, builder_client, boost_sync, + metrics, + payload_selector: Arc::new(DefaultPayloadSelector), } } } @@ -74,13 +81,16 @@ impl EthApiServer for EthEngineApi { message = "received send_raw_transaction", "bytes_len" = bytes.len() ); - let builder = self.builder_client.clone(); - let tx_bytes = bytes.clone(); - tokio::spawn(async move { - builder.send_raw_transaction(tx_bytes).await.map_err(|e| { + for builder in self.builder_clients.iter() { + self.metrics.send_raw_tx_count.increment(1); + let builder = builder.clone(); + let tx_bytes = bytes.clone(); + tokio::spawn(async move { + builder.send_raw_transaction(tx_bytes).await.map_err(|e| { error!(message = "error calling send_raw_transaction for builder", "error" = %e); - }) - }); + }) + }); + } self.l2_client .send_raw_transaction(bytes) .await @@ -122,11 +132,13 @@ impl EngineApiServer for EthEngineApi { }; if should_send_to_builder { - // async call to builder to trigger payload building and sync - let builder = self.builder_client.clone(); - let attr = payload_attributes.clone(); - tokio::spawn(async move { - builder.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { + // async call to each builder to trigger payload building and sync + for builder in self.builder_clients.iter() { + self.metrics.fcu_count.increment(1); + let builder = builder.clone(); + let attr = payload_attributes.clone(); + tokio::spawn(async move { + builder.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { let payload_id_str = response.payload_id.map(|id| id.to_string()).unwrap_or_default(); if response.is_invalid() { error!(message = "builder rejected fork_choice_updated_v3 with attributes", "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); @@ -163,11 +175,14 @@ impl EngineApiServer for EthEngineApi { ) -> RpcResult { info!(message = "received get_payload_v3", "payload_id" = %payload_id); let l2_client_future = self.l2_client.get_payload_v3(payload_id); - let builder_client_future = Box::pin(async { - let payload = self.builder_client.get_payload_v3(payload_id).await.map_err(|e| { - error!(message = "error calling get_payload_v3 from builder", "error" = %e, "payload_id" = %payload_id); - e - })?; + let builder_client_futures = self.builder_clients.iter().map(|builder| { + let builder = builder.clone(); + Box::pin(async move { + self.metrics.get_payload_count.increment(1); + let payload = builder.get_payload_v3(payload_id).await.map_err(|e| { + error!(message = "error calling get_payload_v3 from builder", "error" = %e, "payload_id" = %payload_id); + e + })?; info!(message = "received payload from builder", "payload_id" = %payload_id, "block_hash" = %payload.as_v1_payload().block_hash); @@ -217,11 +232,13 @@ impl EngineApiServer for EthEngineApi { // async call to builder to sync the builder node if self.boost_sync { - let builder = self.builder_client.clone(); - let builder_payload = payload.clone(); - let builder_versioned_hashes = versioned_hashes.clone(); - tokio::spawn(async move { - builder.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await + for builder in self.builder_clients.iter() { + self.metrics.new_payload_count.increment(1); + let builder = builder.clone(); + let builder_payload = payload.clone(); + let builder_versioned_hashes = versioned_hashes.clone(); + tokio::spawn(async move { + builder.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await .map(|response: PayloadStatus| { if response.is_invalid() { error!(message = "builder rejected new_payload_v3", "block_hash" = %block_hash); From 2cb55366cbb2002340e82c0c5d290354d24d26bb Mon Sep 17 00:00:00 2001 From: avalonche Date: Sat, 26 Oct 2024 02:06:42 +1100 Subject: [PATCH 018/429] Add prometheus (flashbots/rollup-boost) --- Cargo.lock | 315 ++++++++++++++++++++++++++++++++++++++++++++++----- Cargo.toml | 1 + src/error.rs | 2 + src/main.rs | 4 + 4 files changed, 293 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3fc72ff5a2577..62c7e942b9e33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,17 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.11" @@ -30,6 +41,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -280,7 +300,7 @@ dependencies = [ "async-stream", "async-trait", "auto_impl", - "dashmap", + "dashmap 6.1.0", "futures", "futures-utils-wasm", "lru", @@ -893,6 +913,15 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "atomic-shim" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67cd4b51d303cf3501c301e8125df442128d3c6d7c69f71b27833d253de47e77" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1396,6 +1425,16 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if", + "num_cpus", +] + [[package]] name = "dashmap" version = "6.1.0" @@ -1407,7 +1446,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core", + "parking_lot_core 0.9.10", ] [[package]] @@ -1573,6 +1612,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + [[package]] name = "enr" version = "0.12.1" @@ -1846,7 +1891,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1892,6 +1937,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash 0.7.8", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1910,7 +1964,7 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash", + "ahash 0.8.11", "allocator-api2", "serde", ] @@ -2019,6 +2073,29 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hyper" +version = "0.14.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "1.4.1" @@ -2048,7 +2125,7 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper", + "hyper 1.4.1", "hyper-util", "log", "rustls", @@ -2066,7 +2143,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper", + "hyper 1.4.1", "hyper-util", "native-tls", "tokio", @@ -2085,7 +2162,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper", + "hyper 1.4.1", "pin-project-lite", "socket2", "tokio", @@ -2220,6 +2297,15 @@ dependencies = [ "libc", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "ipnet" version = "2.10.0" @@ -2322,7 +2408,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "jsonrpsee-types", - "parking_lot", + "parking_lot 0.12.3", "rand", "rustc-hash 2.0.0", "serde", @@ -2341,7 +2427,7 @@ dependencies = [ "async-trait", "base64 0.22.1", "http-body 1.0.1", - "hyper", + "hyper 1.4.1", "hyper-rustls", "hyper-util", "jsonrpsee-core", @@ -2380,7 +2466,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.4.1", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", @@ -2521,7 +2607,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall", + "redox_syscall 0.5.4", ] [[package]] @@ -2561,6 +2647,15 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + [[package]] name = "matchers" version = "0.1.0" @@ -2585,13 +2680,23 @@ dependencies = [ "libc", ] +[[package]] +name = "metrics" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55586aa936c35f34ba8aa5d97356d554311206e1ce1f9e68fe7b07288e5ad827" +dependencies = [ + "ahash 0.7.8", + "metrics-macros", +] + [[package]] name = "metrics" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884adb57038347dfbaf2d5065887b6cf4312330dc8e94bc30a1a839bd79d3261" dependencies = [ - "ahash", + "ahash 0.8.11", "portable-atomic", ] @@ -2601,7 +2706,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae428771d17306715c5091d446327d1cfdedc82185c65ba8423ab404e45bf10" dependencies = [ - "ahash", + "ahash 0.8.11", "portable-atomic", ] @@ -2617,6 +2722,58 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "metrics-exporter-prometheus" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aee04ed06085ea4eb1e1378c4fd973d95e0d3e32897913cd58aedc3b10e71452" +dependencies = [ + "hyper 0.14.31", + "ipnet", + "metrics 0.17.1", + "metrics-util", + "parking_lot 0.11.2", + "quanta", + "thiserror", + "tokio", +] + +[[package]] +name = "metrics-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0daa0ab3a0ae956d0e2c1f42511422850e577d36a255357d1a7d08d45ee3a2f1" +dependencies = [ + "lazy_static", + "proc-macro2", + "quote", + "regex", + "syn 1.0.109", +] + +[[package]] +name = "metrics-util" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1174223789e331d9d47a4a953dac36e397db60fa8d2a111ac505388c6c7fe32e" +dependencies = [ + "ahash 0.7.8", + "aho-corasick 0.7.20", + "atomic-shim", + "crossbeam-epoch", + "crossbeam-utils", + "dashmap 4.0.2", + "hashbrown 0.11.2", + "indexmap 1.9.3", + "metrics 0.17.1", + "num_cpus", + "ordered-float", + "parking_lot 0.11.2", + "quanta", + "radix_trie", + "sketches-ddsketch", +] + [[package]] name = "mime" version = "0.3.17" @@ -2646,7 +2803,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.48.0", ] @@ -2658,7 +2815,7 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -2700,6 +2857,15 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + [[package]] name = "nom" version = "7.1.3" @@ -3018,6 +3184,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + [[package]] name = "overload" version = "0.1.1" @@ -3073,6 +3248,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -3080,7 +3266,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", ] [[package]] @@ -3091,7 +3291,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.4", "smallvec", "windows-targets 0.52.6", ] @@ -3343,6 +3543,22 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "quanta" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8" +dependencies = [ + "crossbeam-utils", + "libc", + "mach", + "once_cell", + "raw-cpuid", + "wasi 0.10.2+wasi-snapshot-preview1", + "web-sys", + "winapi", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -3364,6 +3580,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + [[package]] name = "rand" version = "0.8.5" @@ -3403,6 +3629,15 @@ dependencies = [ "rand_core", ] +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "rayon" version = "1.10.0" @@ -3423,6 +3658,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.4" @@ -3438,7 +3682,7 @@ version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ - "aho-corasick", + "aho-corasick 1.1.3", "memchr", "regex-automata 0.4.7", "regex-syntax 0.8.4", @@ -3459,7 +3703,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ - "aho-corasick", + "aho-corasick 1.1.3", "memchr", "regex-syntax 0.8.4", ] @@ -3491,7 +3735,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper", + "hyper 1.4.1", "hyper-rustls", "hyper-tls", "hyper-util", @@ -3566,7 +3810,7 @@ dependencies = [ "auto_impl", "derive_more", "metrics 0.23.0", - "parking_lot", + "parking_lot 0.12.3", "pin-project", "reth-chainspec", "reth-errors", @@ -3883,10 +4127,10 @@ source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4f dependencies = [ "bitflags 2.6.0", "byteorder", - "dashmap", + "dashmap 6.1.0", "derive_more", "indexmap 2.5.0", - "parking_lot", + "parking_lot 0.12.3", "reth-mdbx-sys", "thiserror", "tracing", @@ -4157,11 +4401,11 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", "auto_impl", - "dashmap", + "dashmap 6.1.0", "itertools 0.13.0", "metrics 0.23.0", "notify", - "parking_lot", + "parking_lot 0.12.3", "rayon", "reth-blockchain-tree-api", "reth-chain-state", @@ -4366,7 +4610,7 @@ dependencies = [ "bitflags 2.6.0", "futures-util", "metrics 0.23.0", - "parking_lot", + "parking_lot 0.12.3", "reth-chain-state", "reth-chainspec", "reth-eth-wire-types", @@ -4598,11 +4842,12 @@ dependencies = [ "http 1.1.0", "http-body 0.4.6", "http-body-util", - "hyper", + "hyper 1.4.1", "hyper-util", "jsonrpsee", "metrics 0.24.0", "metrics-derive", + "metrics-exporter-prometheus", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", "predicates", @@ -4840,7 +5085,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" dependencies = [ - "ahash", + "ahash 0.8.11", "cfg-if", "hashbrown 0.13.2", ] @@ -5106,6 +5351,12 @@ dependencies = [ "time", ] +[[package]] +name = "sketches-ddsketch" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d2ecae5fcf33b122e2e6bd520a57ccf152d2dde3b38c71039df1a6867264ee" + [[package]] name = "slab" version = "0.4.9" @@ -5455,7 +5706,7 @@ dependencies = [ "bytes", "libc", "mio 1.0.2", - "parking_lot", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", "socket2", @@ -5838,6 +6089,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index e7f768c1a1024..d34540527f036 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git futures = "0.3.31" metrics-derive = "0.1" metrics = "0.24.0" +metrics-exporter-prometheus = "0.7" [dev-dependencies] anyhow = "1.0" diff --git a/src/error.rs b/src/error.rs index ee0e4a5cd408e..adff823742c0e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,4 +8,6 @@ pub enum Error { InitRPCClient(String), #[error("Error Initializing RPC Server: {0}")] InitRPCServer(String), + #[error("Error Initializing Metrics: {0}")] + InitMetrics(String), } diff --git a/src/main.rs b/src/main.rs index b46c792d6851d..b353ee19c342d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::server::Server; use jsonrpsee::RpcModule; use metrics::ServerMetrics; +use metrics_exporter_prometheus::PrometheusBuilder; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService}; use server::{EngineApiServer, EthEngineApi}; @@ -83,6 +84,9 @@ async fn main() -> Result<()> { .with_env_filter(EnvFilter::new(args.log_level.to_string())) // Set the log level .init(); + PrometheusBuilder::new() + .install() + .map_err(|e| Error::InitMetrics(e.to_string()))?; let metrics = ServerMetrics::default(); // Handle JWT secret From eed632ecc309fa97a6cdd1ea43c4f3c227fb0cf9 Mon Sep 17 00:00:00 2001 From: avalonche Date: Sat, 26 Oct 2024 07:44:25 +1100 Subject: [PATCH 019/429] fix metrics server (flashbots/rollup-boost) --- .env.example | 2 + Cargo.lock | 596 ++++++++++++++++++++++++++++--------------------- Cargo.toml | 4 +- README.md | 4 + src/error.rs | 2 +- src/main.rs | 24 +- src/metrics.rs | 4 +- src/proxy.rs | 29 ++- src/server.rs | 76 ++++--- 9 files changed, 441 insertions(+), 300 deletions(-) diff --git a/.env.example b/.env.example index 5ce40efd9a686..dd8b0d6bc4cd1 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,10 @@ JWT_TOKEN=688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a +BUILDER_JWT_TOKEN=688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a L2_URL=http://localhost:8551 BUILDER_URL=http://localhost:8552 RPC_HOST=0.0.0.0 RPC_PORT=8081 TRACING=false LOG_LEVEL=info +METRICS=false BOOST_SYNC=false \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 62c7e942b9e33..442dc1b4c8076 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,17 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.11" @@ -41,15 +30,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -300,7 +280,7 @@ dependencies = [ "async-stream", "async-trait", "auto_impl", - "dashmap 6.1.0", + "dashmap", "futures", "futures-utils-wasm", "lru", @@ -333,7 +313,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -532,7 +512,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -544,11 +524,11 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.5.0", + "indexmap 2.6.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", "syn-solidity", "tiny-keccak", ] @@ -564,7 +544,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", "syn-solidity", ] @@ -722,7 +702,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -899,7 +879,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -910,16 +890,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", -] - -[[package]] -name = "atomic-shim" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67cd4b51d303cf3501c301e8125df442128d3c6d7c69f71b27833d253de47e77" -dependencies = [ - "crossbeam-utils", + "syn 2.0.85", ] [[package]] @@ -946,7 +917,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -955,6 +926,33 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "aws-lc-rs" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd82dba44d209fddb11c190e0a94b78651f95299598e472215667417a03ff1d" +dependencies = [ + "aws-lc-sys", + "mirai-annotations", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7a4168111d7eb622a31b214057b8509c0a7e1794f44c546d742330dc793972" +dependencies = [ + "bindgen 0.69.4", + "cc", + "cmake", + "dunce", + "fs_extra", + "libc", + "paste", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -1015,12 +1013,33 @@ dependencies = [ "itertools 0.10.5", "lazy_static", "lazycell", + "log", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.77", + "syn 2.0.85", + "which", +] + +[[package]] +name = "bindgen" +version = "0.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.85", ] [[package]] @@ -1235,7 +1254,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -1244,6 +1263,15 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +[[package]] +name = "cmake" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb1e43aa7fd152b1f968787f7dbcdeb306d1867ff373c69955211876c053f91a" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.2" @@ -1411,7 +1439,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -1422,17 +1450,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.77", -] - -[[package]] -name = "dashmap" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" -dependencies = [ - "cfg-if", - "num_cpus", + "syn 2.0.85", ] [[package]] @@ -1446,7 +1464,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.10", + "parking_lot_core", ] [[package]] @@ -1488,7 +1506,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -1509,7 +1527,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", "unicode-xid", ] @@ -1642,7 +1660,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -1737,6 +1755,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1761,6 +1785,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -1832,7 +1862,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -1891,7 +1921,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -1930,22 +1960,13 @@ dependencies = [ "futures-core", "futures-sink", "http 1.1.0", - "indexmap 2.5.0", + "indexmap 2.6.0", "slab", "tokio", "tokio-util", "tracing", ] -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash 0.7.8", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -1964,11 +1985,20 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash 0.8.11", + "ahash", "allocator-api2", "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "foldhash", +] + [[package]] name = "heck" version = "0.5.0" @@ -2005,6 +2035,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "http" version = "0.2.12" @@ -2073,29 +2112,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hyper" -version = "0.14.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c08302e8fa335b151b788c775ff56e7a03ae64ff85c548ee820fecb70356e85" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.4.1" @@ -2125,10 +2141,11 @@ checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.4.1", + "hyper", "hyper-util", "log", "rustls", + "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", "tokio-rustls", @@ -2143,7 +2160,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "native-tls", "tokio", @@ -2162,7 +2179,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.1", - "hyper 1.4.1", + "hyper", "pin-project-lite", "socket2", "tokio", @@ -2182,7 +2199,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -2268,12 +2285,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.0", "serde", ] @@ -2297,15 +2314,6 @@ dependencies = [ "libc", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipnet" version = "2.10.0" @@ -2408,7 +2416,7 @@ dependencies = [ "http-body 1.0.1", "http-body-util", "jsonrpsee-types", - "parking_lot 0.12.3", + "parking_lot", "rand", "rustc-hash 2.0.0", "serde", @@ -2427,7 +2435,7 @@ dependencies = [ "async-trait", "base64 0.22.1", "http-body 1.0.1", - "hyper 1.4.1", + "hyper", "hyper-rustls", "hyper-util", "jsonrpsee-core", @@ -2453,7 +2461,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -2466,7 +2474,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "jsonrpsee-core", "jsonrpsee-types", @@ -2579,9 +2587,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.158" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "libloading" @@ -2599,6 +2607,17 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libproc" +version = "0.14.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78a09b56be5adbcad5aa1197371688dc6bb249a26da3bca2011ee2fb987ebfb" +dependencies = [ + "bindgen 0.70.1", + "errno", + "libc", +] + [[package]] name = "libredox" version = "0.1.3" @@ -2607,7 +2626,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", - "redox_syscall 0.5.4", + "redox_syscall", ] [[package]] @@ -2648,10 +2667,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" [[package]] -name = "mach" -version = "0.3.2" +name = "mach2" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" dependencies = [ "libc", ] @@ -2680,23 +2699,13 @@ dependencies = [ "libc", ] -[[package]] -name = "metrics" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55586aa936c35f34ba8aa5d97356d554311206e1ce1f9e68fe7b07288e5ad827" -dependencies = [ - "ahash 0.7.8", - "metrics-macros", -] - [[package]] name = "metrics" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884adb57038347dfbaf2d5065887b6cf4312330dc8e94bc30a1a839bd79d3261" dependencies = [ - "ahash 0.8.11", + "ahash", "portable-atomic", ] @@ -2706,7 +2715,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae428771d17306715c5091d446327d1cfdedc82185c65ba8423ab404e45bf10" dependencies = [ - "ahash 0.8.11", + "ahash", "portable-atomic", ] @@ -2719,56 +2728,59 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] name = "metrics-exporter-prometheus" -version = "0.7.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aee04ed06085ea4eb1e1378c4fd973d95e0d3e32897913cd58aedc3b10e71452" +checksum = "85b6f8152da6d7892ff1b7a1c0fa3f435e92b5918ad67035c3bb432111d9a29b" dependencies = [ - "hyper 0.14.31", + "base64 0.22.1", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "indexmap 2.6.0", "ipnet", - "metrics 0.17.1", + "metrics 0.24.0", "metrics-util", - "parking_lot 0.11.2", "quanta", "thiserror", "tokio", + "tracing", ] [[package]] -name = "metrics-macros" -version = "0.4.1" +name = "metrics-process" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0daa0ab3a0ae956d0e2c1f42511422850e577d36a255357d1a7d08d45ee3a2f1" +checksum = "57ca8ecd85575fbb143b2678cb123bb818779391ec0f745b1c4a9dbabadde407" dependencies = [ - "lazy_static", - "proc-macro2", - "quote", - "regex", - "syn 1.0.109", + "libc", + "libproc", + "mach2", + "metrics 0.24.0", + "once_cell", + "procfs", + "rlimit", + "windows 0.58.0", ] [[package]] name = "metrics-util" -version = "0.10.2" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1174223789e331d9d47a4a953dac36e397db60fa8d2a111ac505388c6c7fe32e" +checksum = "15b482df36c13dd1869d73d14d28cd4855fbd6cfc32294bee109908a9f4a4ed7" dependencies = [ - "ahash 0.7.8", - "aho-corasick 0.7.20", - "atomic-shim", + "aho-corasick", "crossbeam-epoch", "crossbeam-utils", - "dashmap 4.0.2", - "hashbrown 0.11.2", - "indexmap 1.9.3", - "metrics 0.17.1", - "num_cpus", + "hashbrown 0.15.0", + "indexmap 2.6.0", + "metrics 0.24.0", "ordered-float", - "parking_lot 0.11.2", "quanta", "radix_trie", "sketches-ddsketch", @@ -2803,7 +2815,7 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] @@ -2815,10 +2827,16 @@ checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ "hermit-abi", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.52.0", ] +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + [[package]] name = "modular-bitfield" version = "0.11.2" @@ -3026,7 +3044,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -3163,7 +3181,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -3186,9 +3204,9 @@ dependencies = [ [[package]] name = "ordered-float" -version = "2.10.1" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +checksum = "83e7ccb95e240b7c9506a3d544f10d935e142cc90b0a1d56954fb44d89ad6b97" dependencies = [ "num-traits", ] @@ -3248,17 +3266,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.3" @@ -3266,21 +3273,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -3291,7 +3284,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.4", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -3346,7 +3339,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -3428,6 +3421,16 @@ dependencies = [ "termtree", ] +[[package]] +name = "prettyplease" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" +dependencies = [ + "proc-macro2", + "syn 2.0.85", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -3500,7 +3503,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -3512,6 +3515,28 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "procfs" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f" +dependencies = [ + "bitflags 2.6.0", + "hex", + "procfs-core", + "rustix", +] + +[[package]] +name = "procfs-core" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec" +dependencies = [ + "bitflags 2.6.0", + "hex", +] + [[package]] name = "proptest" version = "1.5.0" @@ -3540,21 +3565,20 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] name = "quanta" -version = "0.9.3" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8" +checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" dependencies = [ "crossbeam-utils", "libc", - "mach", "once_cell", "raw-cpuid", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", "web-sys", "winapi", ] @@ -3631,11 +3655,11 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "10.7.0" +version = "11.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +checksum = "1ab240315c661615f2ee9f0f2cd32d5a7343a84d5ebcccb99d46e6637565e7b0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] @@ -3658,15 +3682,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.5.4" @@ -3682,7 +3697,7 @@ version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ - "aho-corasick 1.1.3", + "aho-corasick", "memchr", "regex-automata 0.4.7", "regex-syntax 0.8.4", @@ -3703,7 +3718,7 @@ version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ - "aho-corasick 1.1.3", + "aho-corasick", "memchr", "regex-syntax 0.8.4", ] @@ -3735,7 +3750,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-rustls", "hyper-tls", "hyper-util", @@ -3810,7 +3825,7 @@ dependencies = [ "auto_impl", "derive_more", "metrics 0.23.0", - "parking_lot 0.12.3", + "parking_lot", "pin-project", "reth-chainspec", "reth-errors", @@ -3871,7 +3886,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -4127,10 +4142,10 @@ source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4f dependencies = [ "bitflags 2.6.0", "byteorder", - "dashmap 6.1.0", + "dashmap", "derive_more", - "indexmap 2.5.0", - "parking_lot 0.12.3", + "indexmap 2.6.0", + "parking_lot", "reth-mdbx-sys", "thiserror", "tracing", @@ -4141,7 +4156,7 @@ name = "reth-mdbx-sys" version = "1.0.7" source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" dependencies = [ - "bindgen", + "bindgen 0.69.4", "cc", ] @@ -4162,7 +4177,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -4401,11 +4416,11 @@ dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", "auto_impl", - "dashmap 6.1.0", + "dashmap", "itertools 0.13.0", "metrics 0.23.0", "notify", - "parking_lot 0.12.3", + "parking_lot", "rayon", "reth-blockchain-tree-api", "reth-chain-state", @@ -4610,7 +4625,7 @@ dependencies = [ "bitflags 2.6.0", "futures-util", "metrics 0.23.0", - "parking_lot 0.12.3", + "parking_lot", "reth-chain-state", "reth-chainspec", "reth-eth-wire-types", @@ -4797,6 +4812,15 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "rlimit" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7043b63bd0cd1aaa628e476b80e6d4023a3b50eb32789f2728908107bd0c793a" +dependencies = [ + "libc", +] + [[package]] name = "rlp" version = "0.5.2" @@ -4842,12 +4866,14 @@ dependencies = [ "http 1.1.0", "http-body 0.4.6", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "jsonrpsee", "metrics 0.24.0", "metrics-derive", "metrics-exporter-prometheus", + "metrics-process", + "metrics-util", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", "predicates", @@ -4961,6 +4987,7 @@ version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ + "aws-lc-rs", "log", "once_cell", "ring", @@ -4983,6 +5010,19 @@ dependencies = [ "security-framework", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "2.1.3" @@ -5011,7 +5051,7 @@ dependencies = [ "log", "once_cell", "rustls", - "rustls-native-certs", + "rustls-native-certs 0.7.3", "rustls-platform-verifier-android", "rustls-webpki", "security-framework", @@ -5032,6 +5072,7 @@ version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ + "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -5085,7 +5126,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9a8ef13a93c54d20580de1e5c413e624e53121d42fc7e2c11d10ef7f8b02367" dependencies = [ - "ahash 0.8.11", + "ahash", "cfg-if", "hashbrown 0.13.2", ] @@ -5194,7 +5235,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -5203,7 +5244,7 @@ version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "itoa", "memchr", "ryu", @@ -5218,7 +5259,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -5243,7 +5284,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.5.0", + "indexmap 2.6.0", "serde", "serde_derive", "serde_json", @@ -5260,7 +5301,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -5353,9 +5394,9 @@ dependencies = [ [[package]] name = "sketches-ddsketch" -version = "0.1.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04d2ecae5fcf33b122e2e6bd520a57ccf152d2dde3b38c71039df1a6867264ee" +checksum = "c1e9a774a6c28142ac54bb25d25562e6bcf957493a184f15ad4eebccb23e410a" [[package]] name = "slab" @@ -5451,7 +5492,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -5486,9 +5527,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ "proc-macro2", "quote", @@ -5504,7 +5545,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -5533,7 +5574,7 @@ dependencies = [ "libc", "ntapi", "once_cell", - "windows", + "windows 0.52.0", ] [[package]] @@ -5599,7 +5640,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -5706,7 +5747,7 @@ dependencies = [ "bytes", "libc", "mio 1.0.2", - "parking_lot 0.12.3", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -5722,7 +5763,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -5784,7 +5825,7 @@ version = "0.22.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.6.0", "toml_datetime", "winnow", ] @@ -5863,7 +5904,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -6089,12 +6130,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -6123,7 +6158,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", "wasm-bindgen-shared", ] @@ -6157,7 +6192,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6187,6 +6222,18 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + [[package]] name = "winapi" version = "0.3.9" @@ -6224,7 +6271,17 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ - "windows-core", + "windows-core 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", "windows-targets 0.52.6", ] @@ -6237,6 +6294,41 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "windows-registry" version = "0.2.0" @@ -6451,7 +6543,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] @@ -6471,7 +6563,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.85", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d34540527f036..85faad4efb997 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,9 @@ reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git futures = "0.3.31" metrics-derive = "0.1" metrics = "0.24.0" -metrics-exporter-prometheus = "0.7" +metrics-exporter-prometheus = "0.16.0" +metrics-process = "2.3.1" +metrics-util = "0.18.0" [dev-dependencies] anyhow = "1.0" diff --git a/README.md b/README.md index 43fe1f827149f..a066ea2fc00b4 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,16 @@ cargo run -- [OPTIONS] ### Command-line Options - `--jwt-token `: JWT token for authentication (required) +- `--jwt-path `: Path to the JWT secret file (required if `--jwt-token` is not provided) - `--l2-url `: URL of the local L2 execution engine (required) - `--builder-url `: URL of the builder execution engine (required) +- `--builder-jwt-token `: JWT token for builder authentication. defaults to the value of `--jwt-token` if not provided +- `--builder-jwt-path `: Path to the builder JWT secret file. - `--rpc-host `: Host to run the server on (default: 0.0.0.0) - `--rpc-port `: Port to run the server on (default: 8081) - `--tracing`: Enable tracing (default: false) - `--log-level `: Log level (default: info) +- `--metrics`: Enable metrics (default: false) - `--boost-sync`: Enable syncing the builder with the proposer op-node (default: false) ### Environment Variables diff --git a/src/error.rs b/src/error.rs index adff823742c0e..734dd7c47a9ae 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,6 +8,6 @@ pub enum Error { InitRPCClient(String), #[error("Error Initializing RPC Server: {0}")] InitRPCServer(String), - #[error("Error Initializing Metrics: {0}")] + #[error("Error Initializing Prometheus Metrics: {0}")] InitMetrics(String), } diff --git a/src/main.rs b/src/main.rs index b353ee19c342d..aa2910706eb75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,7 @@ use jsonrpsee::server::Server; use jsonrpsee::RpcModule; use metrics::ServerMetrics; use metrics_exporter_prometheus::PrometheusBuilder; +use metrics_util::layers::{PrefixLayer, Stack}; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService}; use server::{EngineApiServer, EthEngineApi}; @@ -66,6 +67,10 @@ struct Args { #[arg(long, env, default_value = "false")] tracing: bool, + // Enable Prometheus metrics + #[arg(long, env, default_value = "false")] + metrics: bool, + /// Log level #[arg(long, env, default_value = "info")] log_level: Level, @@ -84,10 +89,20 @@ async fn main() -> Result<()> { .with_env_filter(EnvFilter::new(args.log_level.to_string())) // Set the log level .init(); - PrometheusBuilder::new() - .install() - .map_err(|e| Error::InitMetrics(e.to_string()))?; - let metrics = ServerMetrics::default(); + let (metrics, handler) = if args.metrics { + let recorder = PrometheusBuilder::new().build_recorder(); + let handle = recorder.handle(); + + // Build metrics stack + Stack::new(recorder) + .push(PrefixLayer::new("rollup-boost")) + .install() + .map_err(|e| Error::InitMetrics(e.to_string()))?; + + (Some(Arc::new(ServerMetrics::default())), Some(handle)) + } else { + (None, None) + }; // Handle JWT secret let jwt_secret = match (args.jwt_path, args.jwt_token) { @@ -140,6 +155,7 @@ async fn main() -> Result<()> { args.l2_url .parse::() .map_err(|e| Error::InvalidArgs(e.to_string()))?, + handler, )); let server = Server::builder() .set_http_middleware(service_builder) diff --git a/src/metrics.rs b/src/metrics.rs index c648252ff35d0..ac259052fe35a 100644 --- a/src/metrics.rs +++ b/src/metrics.rs @@ -2,9 +2,9 @@ use metrics::Counter; use metrics_derive::Metrics; #[derive(Metrics)] -#[metrics(scope = "metrics_custom")] +#[metrics(scope = "rpc")] pub struct ServerMetrics { - #[metric(describe = "Count of fork_choice_updated_v3 calls proxied to the builder")] + #[metric(describe = "Count of forkchoice_updated_v3 calls proxied to the builder")] pub fcu_count: Counter, #[metric(describe = "Count of new_payload_v3 calls proxied to the builder")] diff --git a/src/proxy.rs b/src/proxy.rs index a1e21567a61cf..aa9fb9db15848 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,9 +1,11 @@ -use http::Uri; +use http::header::CONTENT_TYPE; +use http::{HeaderValue, Uri}; use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client; use hyper_util::rt::TokioExecutor; use jsonrpsee::core::{http_helpers, BoxError}; use jsonrpsee::http_client::{HttpBody, HttpRequest, HttpResponse}; +use metrics_exporter_prometheus::PrometheusHandle; use std::task::{Context, Poll}; use std::{future::Future, pin::Pin}; use tower::{Layer, Service}; @@ -14,11 +16,12 @@ const MULTIPLEX_METHODS: [&str; 2] = ["engine_", "eth_sendRawTransaction"]; #[derive(Debug, Clone)] pub struct ProxyLayer { target_url: Uri, + handle: Option, } impl ProxyLayer { - pub fn new(target_url: Uri) -> Self { - ProxyLayer { target_url } + pub fn new(target_url: Uri, handle: Option) -> Self { + ProxyLayer { target_url, handle } } } @@ -30,15 +33,17 @@ impl Layer for ProxyLayer { inner, client: Client::builder(TokioExecutor::new()).build_http(), target_url: self.target_url.clone(), + handle: self.handle.clone(), } } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ProxyService { inner: S, client: Client, target_url: Uri, + handle: Option, } impl Service> for ProxyService @@ -60,7 +65,18 @@ where fn call(&mut self, req: HttpRequest) -> Self::Future { match req.uri().path() { "/healthz" => return Box::pin(async { Ok(Self::Response::new(HttpBody::from("OK"))) }), - "/metrics" => {} + "/metrics" => { + if let Some(handle) = self.handle.as_ref() { + let metrics = handle.render(); + return Box::pin(async { + let mut response = Self::Response::new(HttpBody::from(metrics)); + response + .headers_mut() + .insert(CONTENT_TYPE, HeaderValue::from_static("text/plain")); + Ok::(response) + }); + } + } _ => {} }; @@ -238,7 +254,8 @@ mod tests { /// Spawn a new RPC server with a proxy layer. async fn spawn_proxy_server() -> ServerHandle { let addr = format!("{ADDR}:{PORT}"); - let proxy_layer = ProxyLayer::new(format!("http://{ADDR}:{PROXY_PORT}").parse().unwrap()); + let proxy_layer = + ProxyLayer::new(format!("http://{ADDR}:{PROXY_PORT}").parse().unwrap(), None); // Create a layered server let server = ServerBuilder::default() .set_http_middleware(tower::ServiceBuilder::new().layer(proxy_layer)) diff --git a/src/server.rs b/src/server.rs index cf488b6d64ac6..4445e6f5f7a1d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -18,7 +18,6 @@ use std::sync::Arc; use tracing::{debug, error, info}; use crate::metrics::ServerMetrics; -use crate::selector::{DefaultPayloadSelector, PayloadSelector}; #[rpc(server, client, namespace = "engine")] pub trait EngineApi { @@ -54,7 +53,7 @@ pub struct EthEngineApi> { l2_client: Arc>, builder_client: Arc>, boost_sync: bool, - metrics: ServerMetrics, + metrics: Option>, } impl EthEngineApi { @@ -62,14 +61,13 @@ impl EthEngineApi { l2_client: Arc>, builder_client: Arc>, boost_sync: bool, - metrics: ServerMetrics, + metrics: Option>, ) -> Self { Self { l2_client, builder_client, boost_sync, metrics, - payload_selector: Arc::new(DefaultPayloadSelector), } } } @@ -81,16 +79,19 @@ impl EthApiServer for EthEngineApi { message = "received send_raw_transaction", "bytes_len" = bytes.len() ); - for builder in self.builder_clients.iter() { - self.metrics.send_raw_tx_count.increment(1); - let builder = builder.clone(); - let tx_bytes = bytes.clone(); - tokio::spawn(async move { - builder.send_raw_transaction(tx_bytes).await.map_err(|e| { - error!(message = "error calling send_raw_transaction for builder", "error" = %e); - }) - }); + + if let Some(metrics) = &self.metrics { + metrics.send_raw_tx_count.increment(1); } + + let builder = self.builder_client.clone(); + let tx_bytes = bytes.clone(); + tokio::spawn(async move { + builder.send_raw_transaction(tx_bytes).await.map_err(|e| { + error!(message = "error calling send_raw_transaction for builder", "error" = %e); + }) + }); + self.l2_client .send_raw_transaction(bytes) .await @@ -132,13 +133,14 @@ impl EngineApiServer for EthEngineApi { }; if should_send_to_builder { - // async call to each builder to trigger payload building and sync - for builder in self.builder_clients.iter() { - self.metrics.fcu_count.increment(1); - let builder = builder.clone(); - let attr = payload_attributes.clone(); - tokio::spawn(async move { - builder.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { + // async call to builder to trigger payload building and sync + if let Some(metrics) = &self.metrics { + metrics.fcu_count.increment(1); + } + let builder = self.builder_client.clone(); + let attr = payload_attributes.clone(); + tokio::spawn(async move { + builder.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { let payload_id_str = response.payload_id.map(|id| id.to_string()).unwrap_or_default(); if response.is_invalid() { error!(message = "builder rejected fork_choice_updated_v3 with attributes", "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); @@ -175,13 +177,14 @@ impl EngineApiServer for EthEngineApi { ) -> RpcResult { info!(message = "received get_payload_v3", "payload_id" = %payload_id); let l2_client_future = self.l2_client.get_payload_v3(payload_id); - let builder_client_futures = self.builder_clients.iter().map(|builder| { - let builder = builder.clone(); - Box::pin(async move { - self.metrics.get_payload_count.increment(1); - let payload = builder.get_payload_v3(payload_id).await.map_err(|e| { - error!(message = "error calling get_payload_v3 from builder", "error" = %e, "payload_id" = %payload_id); - e + let builder_client_future = Box::pin(async move { + if let Some(metrics) = &self.metrics { + metrics.get_payload_count.increment(1); + } + let builder = self.builder_client.clone(); + let payload = builder.get_payload_v3(payload_id).await.map_err(|e| { + error!(message = "error calling get_payload_v3 from builder", "error" = %e, "payload_id" = %payload_id); + e })?; info!(message = "received payload from builder", "payload_id" = %payload_id, "block_hash" = %payload.as_v1_payload().block_hash); @@ -189,6 +192,9 @@ impl EngineApiServer for EthEngineApi { // Send the payload to the local execution engine with engine_newPayload to validate the block from the builder. // Otherwise, we do not want to risk the network to a halt since op-node will not be able to propose the block. // If validation fails, return the local block since that one has already been validated. + if let Some(metrics) = &self.metrics { + metrics.new_payload_count.increment(1); + } let payload_status = self.l2_client.new_payload_v3(payload.execution_payload.clone(), vec![], payload.parent_beacon_block_root).await.map_err(|e| { error!(message = "error calling new_payload_v3 to validate builder payload", "error" = %e, "payload_id" = %payload_id); e @@ -232,13 +238,15 @@ impl EngineApiServer for EthEngineApi { // async call to builder to sync the builder node if self.boost_sync { - for builder in self.builder_clients.iter() { - self.metrics.new_payload_count.increment(1); - let builder = builder.clone(); - let builder_payload = payload.clone(); - let builder_versioned_hashes = versioned_hashes.clone(); - tokio::spawn(async move { - builder.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await + if let Some(metrics) = &self.metrics { + metrics.new_payload_count.increment(1); + } + + let builder = self.builder_client.clone(); + let builder_payload = payload.clone(); + let builder_versioned_hashes = versioned_hashes.clone(); + tokio::spawn(async move { + builder.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await .map(|response: PayloadStatus| { if response.is_invalid() { error!(message = "builder rejected new_payload_v3", "block_hash" = %block_hash); From 303db16e18a9727b4a00f16edd1224392aedfe7d Mon Sep 17 00:00:00 2001 From: avalonche Date: Tue, 29 Oct 2024 22:50:26 +1100 Subject: [PATCH 020/429] add tracing (flashbots/rollup-boost) --- Cargo.lock | 1705 ++++++++++++++++++++++++++++++------------------- Cargo.toml | 23 +- Dockerfile | 6 + src/main.rs | 40 +- src/server.rs | 545 +++++++++++++++- 5 files changed, 1632 insertions(+), 687 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 442dc1b4c8076..22f8df035e5cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -47,27 +47,28 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8367891bf380210abb0d6aa30c5f85a9080cb4a066c4d5c5acadad630823751b" +checksum = "056f2c01b2aed86e15b43c47d109bfc8b82553dc34e66452875e51247ec31ab2" dependencies = [ - "alloy-consensus", + "alloy-consensus 0.4.2", "alloy-core", - "alloy-eips", - "alloy-genesis", + "alloy-eips 0.4.2", + "alloy-genesis 0.4.2", "alloy-provider", "alloy-rpc-client", - "alloy-rpc-types", - "alloy-serde", + "alloy-rpc-types 0.4.2", + "alloy-serde 0.4.2", "alloy-transport-http", ] [[package]] name = "alloy-chains" -version = "0.1.32" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abf770dad29577cd3580f3dd09005799224a912b8cdfdd6dc04d030d42b3df4e" +checksum = "d2d630350f57e7ffcd181fe48990d6a87d962910d4e441affde6685cd16febf0" dependencies = [ + "alloy-primitives", "alloy-rlp", "num_enum", "serde", @@ -76,36 +77,55 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.3.6" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "705687d5bfd019fee57cf9e206b27b30a9a9617535d5590a02b171e813208f8e" +dependencies = [ + "alloy-eips 0.4.2", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.4.2", + "auto_impl", + "c-kzg", + "derive_more", + "serde", +] + +[[package]] +name = "alloy-consensus" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629b62e38d471cc15fea534eb7283d2f8a4e8bdb1811bcc5d66dda6cfce6fae1" +checksum = "41ed961a48297c732a5d97ee321aa8bb5009ecadbcb077d8bec90cb54e651629" dependencies = [ - "alloy-eips", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.5.4", "arbitrary", + "auto_impl", "c-kzg", + "derive_more", "serde", ] [[package]] name = "alloy-core" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b095eb0533144b4497e84a9cc3e44a5c2e3754a3983c0376a55a2f9183a53e" +checksum = "b72bf30967a232bec83809bea1623031f6285a013096229330c68c406192a4ca" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", + "alloy-rlp", "alloy-sol-types", ] [[package]] name = "alloy-dyn-abi" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4004925bff5ba0a11739ae84dbb6601a981ea692f3bd45b626935ee90a6b8471" +checksum = "f5228b189b18b85761340dc9eaac0141148a8503657b36f9bc3a869413d987ca" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -133,13 +153,25 @@ dependencies = [ [[package]] name = "alloy-eip7702" -version = "0.1.0" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "serde", +] + +[[package]] +name = "alloy-eip7702" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d319bb544ca6caeab58c39cea8921c55d924d4f68f2c60f24f914673f9a74a" +checksum = "64ffc577390ce50234e02d841214b3dc0bea6aaaae8e04bbf3cb82e9a45da9eb" dependencies = [ "alloy-primitives", "alloy-rlp", "arbitrary", + "derive_more", "k256", "rand", "serde", @@ -147,18 +179,38 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f923dd5fca5f67a43d81ed3ebad0880bd41f6dd0ada930030353ac356c54cd0f" +checksum = "6ffb906284a1e1f63c4607da2068c8197458a352d0b3e9796e67353d72a9be85" dependencies = [ "alloy-eip2930", - "alloy-eip7702", + "alloy-eip7702 0.1.1", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.4.2", + "c-kzg", + "derive_more", + "once_cell", + "serde", + "sha2", +] + +[[package]] +name = "alloy-eips" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69e06cf9c37be824b9d26d6d101114fdde6af0c87de2828b414c05c4b3daa71" +dependencies = [ + "alloy-eip2930", + "alloy-eip7702 0.3.2", + "alloy-primitives", + "alloy-rlp", + "alloy-serde 0.5.4", "arbitrary", "c-kzg", "derive_more", + "ethereum_ssz", + "ethereum_ssz_derive", "once_cell", "serde", "sha2", @@ -166,20 +218,31 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.3.6" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8429cf4554eed9b40feec7f4451113e76596086447550275e3def933faf47ce3" +dependencies = [ + "alloy-primitives", + "alloy-serde 0.4.2", + "serde", +] + +[[package]] +name = "alloy-genesis" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a7a18afb0b318616b6b2b0e2e7ac5529d32a966c673b48091c9919e284e6aca" +checksum = "dde15e14944a88bd6a57d325e9a49b75558746fe16aaccc79713ae50a6a9574c" dependencies = [ "alloy-primitives", - "alloy-serde", + "alloy-serde 0.5.4", "serde", ] [[package]] name = "alloy-json-abi" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9996daf962fd0a90d3c93b388033228865953b92de7bb1959b891d78750a4091" +checksum = "31a0f0d51db8a1a30a4d98a9f90e090a94c8f44cb4d9eafc7e03aa6d00aae984" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -189,9 +252,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3c717b5298fad078cd3a418335b266eba91b511383ca9bd497f742d5975d5ab" +checksum = "f8fa8a1a3c4cbd221f2b8e3693aeb328fca79a757fe556ed08e47bbbc2a70db7" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -203,17 +266,17 @@ dependencies = [ [[package]] name = "alloy-network" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb3705ce7d8602132bcf5ac7a1dd293a42adc2f183abf5907c30ac535ceca049" +checksum = "85fa23a6a9d612b52e402c995f2d582c25165ec03ac6edf64c861a76bc5b87cd" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", "alloy-json-rpc", - "alloy-network-primitives", + "alloy-network-primitives 0.4.2", "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", + "alloy-rpc-types-eth 0.4.2", + "alloy-serde 0.4.2", "alloy-signer", "alloy-sol-types", "async-trait", @@ -224,21 +287,35 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94ad40869867ed2d9cd3842b1e800889e5b49e6b92da346e93862b4a741bedf3" +checksum = "801492711d4392b2ccf5fc0bc69e299fa1aab15167d74dcaa9aab96a54f684bd" dependencies = [ - "alloy-eips", + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", "alloy-primitives", - "alloy-serde", + "alloy-serde 0.4.2", + "serde", +] + +[[package]] +name = "alloy-network-primitives" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514f70ee2a953db21631cd817b13a1571474ec77ddc03d47616d5e8203489fde" +dependencies = [ + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", + "alloy-primitives", + "alloy-serde 0.5.4", "serde", ] [[package]] name = "alloy-primitives" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" +checksum = "8edae627382349b56cd6a7a2106f4fd69b243a9233e560c55c2e03cabb7e1d3c" dependencies = [ "alloy-rlp", "arbitrary", @@ -247,34 +324,40 @@ dependencies = [ "const-hex", "derive_arbitrary", "derive_more", + "foldhash", "getrandom", + "hashbrown 0.15.0", "hex-literal", + "indexmap 2.6.0", "itoa", "k256", "keccak-asm", + "paste", "proptest", "proptest-derive", "rand", "ruint", + "rustc-hash 2.0.0", "serde", + "sha3", "tiny-keccak", ] [[package]] name = "alloy-provider" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927f708dd457ed63420400ee5f06945df9632d5d101851952056840426a10dc5" +checksum = "fcfaa4ffec0af04e3555686b8aacbcdf7d13638133a0672749209069750f78a6" dependencies = [ "alloy-chains", - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", "alloy-json-rpc", "alloy-network", - "alloy-network-primitives", + "alloy-network-primitives 0.4.2", "alloy-primitives", "alloy-rpc-client", - "alloy-rpc-types-eth", + "alloy-rpc-types-eth 0.4.2", "alloy-transport", "alloy-transport-http", "async-stream", @@ -283,7 +366,7 @@ dependencies = [ "dashmap", "futures", "futures-utils-wasm", - "lru", + "lru 0.12.5", "pin-project", "reqwest", "serde", @@ -296,9 +379,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26154390b1d205a4a7ac7352aa2eb4f81f391399d4e2f546fb81a2f8bb383f62" +checksum = "da0822426598f95e45dd1ea32a738dac057529a709ee645fcc516ffa4cbde08f" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -307,9 +390,9 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" +checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", @@ -318,11 +401,12 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d82952dca71173813d4e5733e2c986d8b04aea9e0f3b0a576664c232ad050a5" +checksum = "370143ed581aace6e663342d21d209c6b2e34ee6142f7d6675adb518deeaf0dc" dependencies = [ "alloy-json-rpc", + "alloy-primitives", "alloy-transport", "alloy-transport-http", "futures", @@ -339,147 +423,103 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64333d639f2a0cf73491813c629a405744e16343a4bc5640931be707c345ecc5" -dependencies = [ - "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-serde", - "serde", -] - -[[package]] -name = "alloy-rpc-types-admin" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fefd12e99dd6b7de387ed13ad047ce2c90d8950ca62fc48b8a457ebb8f936c61" -dependencies = [ - "alloy-genesis", - "alloy-primitives", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-rpc-types-anvil" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25cb45ad7c0930dd62eecf164d2afe4c3d2dd2c82af85680ad1f118e1e5cb83" +checksum = "9ffc534b7919e18f35e3aa1f507b6f3d9d92ec298463a9f6beaac112809d8d06" dependencies = [ "alloy-primitives", - "alloy-serde", + "alloy-rpc-types-eth 0.4.2", + "alloy-serde 0.4.2", "serde", ] [[package]] -name = "alloy-rpc-types-beacon" -version = "0.3.6" +name = "alloy-rpc-types" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7081d2206dca51ce23a06338d78d9b536931cc3f15134fc1c6535eb2b77f18" +checksum = "eea9bf1abdd506f985a53533f5ac01296bcd6102c5e139bbc5d40bc468d2c916" dependencies = [ - "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", - "serde", - "serde_with", - "thiserror", -] - -[[package]] -name = "alloy-rpc-types-debug" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9f9033796bb3078d11cc9c839f00e277431ef997db2849a46045fcffee3835" -dependencies = [ - "alloy-primitives", + "alloy-rpc-types-eth 0.5.4", + "alloy-serde 0.5.4", "serde", ] [[package]] name = "alloy-rpc-types-engine" -version = "0.3.6" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1464c4dd646e1bdfde86ae65ce5ba168dbb29180b478011fe87117ae46b1629b" +checksum = "886d22d41992287a235af2f3af4299b5ced2bcafb81eb835572ad35747476946" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.5.4", "derive_more", - "jsonrpsee-types", + "ethereum_ssz", + "ethereum_ssz_derive", "jsonwebtoken", "rand", "serde", + "strum", ] [[package]] name = "alloy-rpc-types-eth" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83aa984386deda02482660aa31cb8ca1e63d533f1c31a52d7d181ac5ec68e9b8" +checksum = "413f4aa3ccf2c3e4234a047c5fa4727916d7daf25a89f9b765df0ba09784fd87" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-network-primitives", + "alloy-consensus 0.4.2", + "alloy-eips 0.4.2", + "alloy-network-primitives 0.4.2", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.4.2", "alloy-sol-types", - "cfg-if", "derive_more", - "hashbrown 0.14.5", "itertools 0.13.0", - "jsonrpsee-types", "serde", "serde_json", ] [[package]] -name = "alloy-rpc-types-mev" -version = "0.3.6" +name = "alloy-rpc-types-eth" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "922d92389e5022650c4c60ffd2f9b2467c3f853764f0f74ff16a23106f9017d5" +checksum = "00b034779a4850b4b03f5be5ea674a1cf7d746b2da762b34d1860ab45e48ca27" dependencies = [ - "alloy-eips", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", + "alloy-network-primitives 0.5.4", "alloy-primitives", - "alloy-serde", + "alloy-rlp", + "alloy-serde 0.5.4", + "alloy-sol-types", + "derive_more", + "itertools 0.13.0", "serde", "serde_json", ] [[package]] -name = "alloy-rpc-types-trace" -version = "0.3.6" +name = "alloy-serde" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98db35cd42c90b484377e6bc44d95377a7a38a5ebee996e67754ac0446d542ab" +checksum = "9dff0ab1cdd43ca001e324dc27ee0e8606bd2161d6623c63e0e0b8c4dfc13600" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", "serde", "serde_json", - "thiserror", -] - -[[package]] -name = "alloy-rpc-types-txpool" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bac37082c3b21283b3faf5cc0e08974272aee2f756ce1adeb26db56a5fce0d5" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "serde", ] [[package]] name = "alloy-serde" -version = "0.3.6" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "731f75ec5d383107fd745d781619bd9cedf145836c51ecb991623d41278e71fa" +checksum = "028e72eaa9703e4882344983cfe7636ce06d8cce104a78ea62fd19b46659efc4" dependencies = [ "alloy-primitives", "arbitrary", @@ -489,9 +529,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "307324cca94354cd654d6713629f0383ec037e1ff9e3e3d547212471209860c0" +checksum = "2fd4e0ad79c81a27ca659be5d176ca12399141659fef2bcbfdc848da478f4504" dependencies = [ "alloy-primitives", "async-trait", @@ -503,9 +543,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" +checksum = "841eabaa4710f719fddbc24c95d386eae313f07e6da4babc25830ee37945be0c" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -517,9 +557,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" +checksum = "6672337f19d837b9f7073c45853aeb528ed9f7dd6a4154ce683e9e5cb7794014" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -535,9 +575,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-input" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" +checksum = "0dff37dd20bfb118b777c96eda83b2067f4226d2644c5cfa00187b3bc01770ba" dependencies = [ "const-hex", "dunce", @@ -550,9 +590,9 @@ dependencies = [ [[package]] name = "alloy-sol-type-parser" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edae8ea1de519ccba896b6834dec874230f72fe695ff3c9c118e90ec7cff783" +checksum = "5b853d42292dbb159671a3edae3b2750277ff130f32b726fe07dc2b17aa6f2b5" dependencies = [ "serde", "winnow", @@ -560,9 +600,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" +checksum = "aa828bb1b9a6dc52208fbb18084fb9ce2c30facc2bfda6a5d922349b4990354f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -573,9 +613,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33616b2edf7454302a1d48084db185e52c309f73f6c10be99b0fe39354b3f1e9" +checksum = "2ac3e97dad3d31770db0fc89bd6a63b789fbae78963086733f960cf32c483904" dependencies = [ "alloy-json-rpc", "base64 0.22.1", @@ -592,9 +632,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a944f5310c690b62bbb3e7e5ce34527cbd36b2d18532a797af123271ce595a49" +checksum = "b367dcccada5b28987c2296717ee04b9a5637aacd78eacb1726ef211678b5212" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -607,14 +647,14 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.5.3" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a46c9c4fdccda7982e7928904bd85fe235a0404ee3d7e197fff13d61eac8b4f" +checksum = "cdd7f8b3a7c65ca09b3c7bdd7c7d72d7423d026f5247eda96af53d24e58315c1" dependencies = [ "alloy-primitives", "alloy-rlp", + "arrayvec", "derive_more", - "hashbrown 0.14.5", "nybbles", "serde", "smallvec", @@ -638,9 +678,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", @@ -653,53 +693,53 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8" [[package]] name = "aquamarine" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e" +checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" dependencies = [ "include_dir", "itertools 0.10.5", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", "syn 2.0.85", @@ -843,6 +883,9 @@ name = "arrayvec" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +dependencies = [ + "serde", +] [[package]] name = "assert_cmd" @@ -862,9 +905,9 @@ dependencies = [ [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -873,9 +916,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -884,9 +927,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -922,9 +965,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-lc-rs" @@ -944,7 +987,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df7a4168111d7eb622a31b214057b8509c0a7e1794f44c546d742330dc793972" dependencies = [ - "bindgen 0.69.4", + "bindgen 0.69.5", "cc", "cmake", "dunce", @@ -953,6 +996,53 @@ dependencies = [ "paste", ] +[[package]] +name = "axum" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper 1.0.1", + "tower 0.5.1", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.1", + "tower-layer", + "tower-service", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -1003,14 +1093,14 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.4" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.12.1", "lazy_static", "lazycell", "log", @@ -1113,7 +1203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" dependencies = [ "memchr", - "regex-automata 0.4.7", + "regex-automata 0.4.8", "serde", ] @@ -1131,9 +1221,9 @@ checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "bytemuck" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" +checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" [[package]] name = "byteorder" @@ -1143,9 +1233,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" dependencies = [ "serde", ] @@ -1167,9 +1257,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.21" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ "jobserver", "libc", @@ -1225,9 +1315,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.17" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -1235,9 +1325,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.17" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -1247,9 +1337,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -1274,9 +1364,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "combine" @@ -1290,9 +1380,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.12.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8a24a26d37e1ffd45343323dc9fe6654ceea44c12f2fcb3d7ac29e610bc6" +checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" dependencies = [ "cfg-if", "cpufeatures", @@ -1356,6 +1446,12 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "critical-section" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" + [[package]] name = "crossbeam-channel" version = "0.5.13" @@ -1623,9 +1719,9 @@ dependencies = [ [[package]] name = "encoding_rs" -version = "0.8.34" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -1679,6 +1775,47 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ethereum_serde_utils" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70cbccfccf81d67bff0ab36e591fa536c8a935b078a7b0e58c1d00d418332fc9" +dependencies = [ + "alloy-primitives", + "hex", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "ethereum_ssz" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfbba28f4f3f32d92c06a64f5bf6c4537b5d4e21f28c689bd2bbaecfea4e0d3e" +dependencies = [ + "alloy-primitives", + "derivative", + "ethereum_serde_utils", + "itertools 0.13.0", + "serde", + "serde_derive", + "smallvec", + "typenum", +] + +[[package]] +name = "ethereum_ssz_derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d37845ba7c16bf4be8be4b5786f03a2ba5f2fda0d7f9e7cb2282f69cff420d7" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "eyre" version = "0.6.12" @@ -1927,9 +2064,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "glob" @@ -1978,17 +2115,15 @@ name = "hashbrown" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "allocator-api2", - "serde", -] [[package]] name = "hashbrown" @@ -1996,7 +2131,10 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ + "allocator-api2", + "equivalent", "foldhash", + "serde", ] [[package]] @@ -2102,9 +2240,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -2114,9 +2252,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -2148,7 +2286,20 @@ dependencies = [ "rustls-native-certs 0.8.0", "rustls-pki-types", "tokio", - "tokio-rustls", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", "tower-service", ] @@ -2170,9 +2321,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -2183,7 +2334,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower 0.4.13", "tower-service", "tracing", ] @@ -2289,6 +2439,7 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ + "arbitrary", "equivalent", "hashbrown 0.15.0", "serde", @@ -2316,9 +2467,9 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.10.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] name = "is_terminal_polyfill" @@ -2335,6 +2486,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.13.0" @@ -2381,18 +2541,18 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.70" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] [[package]] name = "jsonrpsee" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd1ead9fb95614e8dc5556d12a8681c2f6d352d0c1d3efc8708c7ccbba47bc6" +checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" dependencies = [ "jsonrpsee-core", "jsonrpsee-http-client", @@ -2405,9 +2565,9 @@ dependencies = [ [[package]] name = "jsonrpsee-core" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff79651479f69ada7bda604ef2acf3f1aa50755d97cc36d25ff04c2664f9d96f" +checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" dependencies = [ "async-trait", "bytes", @@ -2428,9 +2588,9 @@ dependencies = [ [[package]] name = "jsonrpsee-http-client" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ed8b301b19f4dad8ddc66ed956a70fc227def5c19b3898e0a29ce8f0edee06" +checksum = "b3638bc4617f96675973253b3a45006933bde93c2fd8a6170b33c777cc389e5b" dependencies = [ "async-trait", "base64 0.22.1", @@ -2453,9 +2613,9 @@ dependencies = [ [[package]] name = "jsonrpsee-proc-macros" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d4c6bec4909c966f59f52db3655c0e9d4685faae8b49185973d9d7389bb884" +checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" dependencies = [ "heck", "proc-macro-crate", @@ -2466,9 +2626,9 @@ dependencies = [ [[package]] name = "jsonrpsee-server" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe2198e5fd96cf2153ecc123364f699b6e2151317ea09c7bf799c43c2fe1415" +checksum = "82ad8ddc14be1d4290cd68046e7d1d37acd408efed6d3ca08aefcc3ad6da069c" dependencies = [ "futures-util", "http 1.1.0", @@ -2493,9 +2653,9 @@ dependencies = [ [[package]] name = "jsonrpsee-types" -version = "0.24.4" +version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531e386460425e49679587871a056f2895a47dade21457324ad1262cd78ef6d9" +checksum = "a178c60086f24cc35bb82f57c651d0d25d99c4742b4d335de04e97fa1f08a8a1" dependencies = [ "http 1.1.0", "serde", @@ -2520,9 +2680,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956ff9b67e26e1a6a866cb758f12c6f8746208489e3e4a4b5580802f2f0a587b" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" dependencies = [ "cfg-if", "ecdsa", @@ -2603,9 +2763,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "libproc" @@ -2643,6 +2803,7 @@ checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", + "serde", ] [[package]] @@ -2653,11 +2814,20 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.4" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.13.2", +] + +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.0", ] [[package]] @@ -2684,6 +2854,12 @@ dependencies = [ "regex-automata 0.1.10", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.4" @@ -2699,16 +2875,6 @@ dependencies = [ "libc", ] -[[package]] -name = "metrics" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884adb57038347dfbaf2d5065887b6cf4312330dc8e94bc30a1a839bd79d3261" -dependencies = [ - "ahash", - "portable-atomic", -] - [[package]] name = "metrics" version = "0.24.0" @@ -2744,7 +2910,7 @@ dependencies = [ "hyper-util", "indexmap 2.6.0", "ipnet", - "metrics 0.24.0", + "metrics", "metrics-util", "quanta", "thiserror", @@ -2761,7 +2927,7 @@ dependencies = [ "libc", "libproc", "mach2", - "metrics 0.24.0", + "metrics", "once_cell", "procfs", "rlimit", @@ -2779,7 +2945,7 @@ dependencies = [ "crossbeam-utils", "hashbrown 0.15.0", "indexmap 2.6.0", - "metrics 0.24.0", + "metrics", "ordered-float", "quanta", "radix_trie", @@ -3062,30 +3228,34 @@ dependencies = [ [[package]] name = "object" -version = "0.36.4" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +dependencies = [ + "critical-section", + "portable-atomic", +] [[package]] name = "op-alloy-consensus" -version = "0.2.12" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21aad1fbf80d2bcd7406880efc7ba109365f44bbb72896758ddcbfa46bf1592c" +checksum = "ba7c98055fd048073738df0cc6d6537e992a0d8828f39d99a469e870db126dbd" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.5.4", "arbitrary", "derive_more", "serde", @@ -3094,12 +3264,12 @@ dependencies = [ [[package]] name = "op-alloy-genesis" -version = "0.2.12" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1b8a9b70da0e027242ec1762f0f3a386278b6291d00d12ff5a64929dc19f68" +checksum = "d631e8113cf88d30e621022677209caa148a9ca3ccb590fd34bbd1c731e3aff3" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-sol-types", "serde", @@ -3108,16 +3278,16 @@ dependencies = [ [[package]] name = "op-alloy-protocol" -version = "0.2.12" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf300a82ae2d30e2255bfea87a2259da49f63a25a44db561ae64cc9e3084139f" +checksum = "9b39574acb1873315e6bd89df174f6223e897188fb87eeea2ad1eda04f7d28eb" dependencies = [ - "alloy-consensus", - "alloy-eips", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", - "alloy-serde", - "hashbrown 0.14.5", + "alloy-serde 0.5.4", + "derive_more", "op-alloy-consensus", "op-alloy-genesis", "serde", @@ -3125,17 +3295,16 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.2.12" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e281fbfc2198b7c0c16457d6524f83d192662bc9f3df70f24c3038d4521616df" +checksum = "919e9b69212d61f3c8932bfb717c7ad458ea3fc52072b3433d99994f8223d555" dependencies = [ - "alloy-eips", - "alloy-network-primitives", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", + "alloy-network-primitives 0.5.4", "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "cfg-if", - "hashbrown 0.14.5", + "alloy-rpc-types-eth 0.5.4", + "alloy-serde 0.5.4", "op-alloy-consensus", "serde", "serde_json", @@ -3143,26 +3312,25 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" -version = "0.2.12" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2947272a81ebf988f4804b6f0f6a7c0b2f6f89a908cb410e36f8f3828f81c778" +checksum = "0e3a47ea24cee189b4351be247fd138c68571704ee57060cf5a722502f44412c" dependencies = [ - "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", - "alloy-serde", + "alloy-serde 0.5.4", "derive_more", - "op-alloy-consensus", - "op-alloy-genesis", + "ethereum_ssz", "op-alloy-protocol", "serde", + "snap", ] [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags 2.6.0", "cfg-if", @@ -3192,9 +3360,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -3202,6 +3370,89 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "opentelemetry" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "570074cc999d1a58184080966e5bd3bf3a9a4af650c3b05047c2621e7405cd17" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "once_cell", + "pin-project-lite", + "thiserror", +] + +[[package]] +name = "opentelemetry-http" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6351496aeaa49d7c267fb480678d85d1cd30c5edb20b497c48c56f62a8c14b99" +dependencies = [ + "async-trait", + "bytes", + "http 1.1.0", + "opentelemetry", + "reqwest", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29e1f9c8b032d4f635c730c0efcf731d5e2530ea13fa8bef7939ddc8420696bd" +dependencies = [ + "async-trait", + "futures-core", + "http 1.1.0", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-proto", + "opentelemetry_sdk", + "prost", + "reqwest", + "serde_json", + "thiserror", + "tokio", + "tonic", +] + +[[package]] +name = "opentelemetry-proto" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d3968ce3aefdcca5c27e3c4ea4391b37547726a70893aab52d3de95d5f8b34" +dependencies = [ + "hex", + "opentelemetry", + "opentelemetry_sdk", + "prost", + "serde", + "tonic", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c627d9f4c9cdc1f21a29ee4bfbd6028fcb8bcf2a857b43f3abdf72c9c862f3" +dependencies = [ + "async-trait", + "futures-channel", + "futures-executor", + "futures-util", + "glob", + "once_cell", + "opentelemetry", + "percent-encoding", + "rand", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", +] + [[package]] name = "ordered-float" version = "4.4.0" @@ -3313,9 +3564,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.7.13" +version = "2.7.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9" +checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", "thiserror", @@ -3324,18 +3575,18 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", @@ -3344,9 +3595,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -3366,15 +3617,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "portable-atomic" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d30538d42559de6b034bc76fd6dd4c38961b1ee5c6c56e3808c50128fdbc22ce" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" [[package]] name = "powerfmt" @@ -3460,30 +3711,6 @@ dependencies = [ "toml_edit", ] -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - [[package]] name = "proc-macro-error-attr2" version = "2.0.0" @@ -3508,9 +3735,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -3551,7 +3778,7 @@ dependencies = [ "rand", "rand_chacha", "rand_xorshift", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -3568,6 +3795,29 @@ dependencies = [ "syn 2.0.85", ] +[[package]] +name = "prost" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" +dependencies = [ + "anyhow", + "itertools 0.13.0", + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "quanta" version = "0.12.3" @@ -3623,6 +3873,7 @@ dependencies = [ "libc", "rand_chacha", "rand_core", + "serde", ] [[package]] @@ -3684,23 +3935,23 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ "bitflags 2.6.0", ] [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.7", - "regex-syntax 0.8.4", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", ] [[package]] @@ -3714,13 +3965,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.4", + "regex-syntax 0.8.5", ] [[package]] @@ -3731,19 +3982,20 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.7" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", @@ -3780,21 +4032,23 @@ dependencies = [ [[package]] name = "reth-basic-payload-builder" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", "futures-core", "futures-util", - "metrics 0.23.0", + "metrics", "reth-chainspec", + "reth-evm", "reth-metrics", "reth-payload-builder", "reth-payload-primitives", "reth-primitives", "reth-provider", - "reth-revm", "reth-tasks", "reth-transaction-pool", "revm", @@ -3804,8 +4058,8 @@ dependencies = [ [[package]] name = "reth-blockchain-tree-api" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-primitives", "reth-consensus", @@ -3817,14 +4071,14 @@ dependencies = [ [[package]] name = "reth-chain-state" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-eips", + "alloy-eips 0.5.4", "alloy-primitives", "auto_impl", "derive_more", - "metrics 0.23.0", + "metrics", "parking_lot", "pin-project", "reth-chainspec", @@ -3841,34 +4095,32 @@ dependencies = [ [[package]] name = "reth-chainspec" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-chains", - "alloy-eips", - "alloy-genesis", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", + "alloy-genesis 0.5.4", "alloy-primitives", - "alloy-trie", "auto_impl", "derive_more", "once_cell", - "op-alloy-rpc-types", "reth-ethereum-forks", "reth-network-peers", "reth-primitives-traits", "reth-trie-common", - "serde", "serde_json", ] [[package]] name = "reth-codecs" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-genesis", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", + "alloy-genesis 0.5.4", "alloy-primitives", "alloy-trie", "bytes", @@ -3880,8 +4132,8 @@ dependencies = [ [[package]] name = "reth-codecs-derive" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "convert_case", "proc-macro2", @@ -3891,9 +4143,10 @@ dependencies = [ [[package]] name = "reth-consensus" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-eips 0.5.4", "alloy-primitives", "auto_impl", "derive_more", @@ -3902,25 +4155,28 @@ dependencies = [ [[package]] name = "reth-consensus-common" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "reth-chainspec", "reth-consensus", "reth-primitives", + "revm-primitives", ] [[package]] name = "reth-db" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-primitives", "bytes", "derive_more", "eyre", - "metrics 0.23.0", + "metrics", "page_size", "paste", "reth-db-api", @@ -3944,13 +4200,14 @@ dependencies = [ [[package]] name = "reth-db-api" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-genesis 0.5.4", "alloy-primitives", "bytes", "derive_more", - "metrics 0.23.0", + "metrics", "modular-bitfield", "parity-scale-codec", "reth-codecs", @@ -3966,8 +4223,8 @@ dependencies = [ [[package]] name = "reth-db-models" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-primitives", "bytes", @@ -3979,10 +4236,10 @@ dependencies = [ [[package]] name = "reth-engine-primitives" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "reth-chainspec", + "alloy-primitives", "reth-execution-types", "reth-payload-primitives", "reth-primitives", @@ -3992,8 +4249,8 @@ dependencies = [ [[package]] name = "reth-errors" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "reth-blockchain-tree-api", "reth-consensus", @@ -4005,11 +4262,11 @@ dependencies = [ [[package]] name = "reth-eth-wire-types" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-chains", - "alloy-genesis", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", "bytes", @@ -4017,21 +4274,24 @@ dependencies = [ "reth-chainspec", "reth-codecs-derive", "reth-primitives", + "serde", "thiserror", ] [[package]] name = "reth-ethereum-engine-primitives" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-eips 0.5.4", + "alloy-primitives", "alloy-rlp", + "alloy-rpc-types-engine", "reth-chain-state", "reth-chainspec", "reth-engine-primitives", "reth-payload-primitives", "reth-primitives", - "reth-rpc-types", "reth-rpc-types-compat", "serde", "sha2", @@ -4039,8 +4299,8 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-chains", "alloy-primitives", @@ -4056,53 +4316,35 @@ dependencies = [ [[package]] name = "reth-evm" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-eips", + "alloy-eips 0.5.4", + "alloy-primitives", "auto_impl", "futures-util", - "metrics 0.23.0", + "metrics", "reth-chainspec", + "reth-consensus", + "reth-consensus-common", "reth-execution-errors", "reth-execution-types", "reth-metrics", "reth-primitives", "reth-primitives-traits", "reth-prune-types", - "reth-storage-errors", - "revm", - "revm-primitives", -] - -[[package]] -name = "reth-evm-optimism" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" -dependencies = [ - "alloy-primitives", - "reth-chainspec", - "reth-ethereum-forks", - "reth-evm", - "reth-execution-errors", - "reth-execution-types", - "reth-optimism-chainspec", - "reth-optimism-consensus", - "reth-primitives", - "reth-prune-types", "reth-revm", + "reth-storage-errors", "revm", "revm-primitives", - "thiserror", - "tracing", ] [[package]] name = "reth-execution-errors" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-eips", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", "derive_more", @@ -4115,20 +4357,22 @@ dependencies = [ [[package]] name = "reth-execution-types" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "reth-chainspec", + "alloy-eips 0.5.4", + "alloy-primitives", "reth-execution-errors", "reth-primitives", "reth-trie", "revm", + "serde", ] [[package]] name = "reth-fs-util" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "serde", "serde_json", @@ -4137,8 +4381,8 @@ dependencies = [ [[package]] name = "reth-libmdbx" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "bitflags 2.6.0", "byteorder", @@ -4147,52 +4391,43 @@ dependencies = [ "indexmap 2.6.0", "parking_lot", "reth-mdbx-sys", + "smallvec", "thiserror", "tracing", ] [[package]] name = "reth-mdbx-sys" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" -dependencies = [ - "bindgen 0.69.4", - "cc", -] - -[[package]] -name = "reth-metrics" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "metrics 0.23.0", - "reth-metrics-derive", + "bindgen 0.70.1", + "cc", ] [[package]] -name = "reth-metrics-derive" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +name = "reth-metrics" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "proc-macro2", - "quote", - "regex", - "syn 2.0.85", + "metrics", + "metrics-derive", ] [[package]] name = "reth-net-banlist" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-primitives", ] [[package]] name = "reth-network-p2p" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-eips 0.5.4", "alloy-primitives", "auto_impl", "derive_more", @@ -4209,8 +4444,8 @@ dependencies = [ [[package]] name = "reth-network-peers" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -4222,8 +4457,8 @@ dependencies = [ [[package]] name = "reth-network-types" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "reth-ethereum-forks", "reth-net-banlist", @@ -4234,8 +4469,8 @@ dependencies = [ [[package]] name = "reth-nippy-jar" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "anyhow", "bincode", @@ -4251,62 +4486,118 @@ dependencies = [ [[package]] name = "reth-node-types" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "reth-chainspec", "reth-db-api", "reth-engine-primitives", + "reth-primitives", + "reth-primitives-traits", ] [[package]] name = "reth-optimism-chainspec" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-chains", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", + "alloy-genesis 0.5.4", "alloy-primitives", "derive_more", "once_cell", + "op-alloy-rpc-types", "reth-chainspec", "reth-ethereum-forks", + "reth-network-peers", + "reth-optimism-forks", "reth-primitives-traits", "serde_json", ] [[package]] name = "reth-optimism-consensus" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-consensus 0.5.4", "alloy-primitives", "reth-chainspec", "reth-consensus", "reth-consensus-common", + "reth-optimism-chainspec", + "reth-optimism-forks", "reth-primitives", "reth-trie-common", "tracing", ] +[[package]] +name = "reth-optimism-evm" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +dependencies = [ + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", + "alloy-primitives", + "derive_more", + "op-alloy-consensus", + "reth-chainspec", + "reth-consensus", + "reth-ethereum-forks", + "reth-evm", + "reth-execution-errors", + "reth-execution-types", + "reth-optimism-chainspec", + "reth-optimism-consensus", + "reth-optimism-forks", + "reth-primitives", + "reth-prune-types", + "reth-revm", + "revm", + "revm-primitives", + "tracing", +] + +[[package]] +name = "reth-optimism-forks" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +dependencies = [ + "alloy-chains", + "alloy-primitives", + "once_cell", + "reth-ethereum-forks", + "serde", +] + [[package]] name = "reth-optimism-payload-builder" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", + "alloy-rpc-types-engine", + "op-alloy-rpc-types-engine", "reth-basic-payload-builder", "reth-chain-state", "reth-chainspec", "reth-evm", - "reth-evm-optimism", "reth-execution-types", + "reth-optimism-chainspec", + "reth-optimism-consensus", + "reth-optimism-evm", + "reth-optimism-forks", "reth-payload-builder", "reth-payload-primitives", "reth-primitives", "reth-provider", "reth-revm", - "reth-rpc-types", "reth-rpc-types-compat", "reth-transaction-pool", "reth-trie", @@ -4316,24 +4607,31 @@ dependencies = [ "tracing", ] +[[package]] +name = "reth-optimism-primitives" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +dependencies = [ + "alloy-consensus 0.5.4", + "alloy-primitives", + "reth-primitives", +] + [[package]] name = "reth-payload-builder" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-primitives", + "alloy-rpc-types 0.5.4", + "async-trait", "futures-util", - "metrics 0.23.0", - "pin-project", - "reth-errors", + "metrics", "reth-ethereum-engine-primitives", "reth-metrics", "reth-payload-primitives", "reth-primitives", "reth-provider", - "reth-rpc-types", - "reth-transaction-pool", - "thiserror", "tokio", "tokio-stream", "tracing", @@ -4341,31 +4639,37 @@ dependencies = [ [[package]] name = "reth-payload-primitives" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-eips 0.5.4", "alloy-primitives", + "alloy-rpc-types 0.5.4", + "async-trait", + "op-alloy-rpc-types-engine", + "pin-project", "reth-chain-state", "reth-chainspec", "reth-errors", "reth-primitives", - "reth-rpc-types", "reth-transaction-pool", "serde", "thiserror", "tokio", + "tokio-stream", + "tracing", ] [[package]] name = "reth-primitives" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-genesis", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", + "alloy-serde 0.5.4", "bytes", "c-kzg", "derive_more", @@ -4374,10 +4678,8 @@ dependencies = [ "once_cell", "op-alloy-consensus", "rayon", - "reth-chainspec", "reth-codecs", "reth-ethereum-forks", - "reth-optimism-chainspec", "reth-primitives-traits", "reth-static-file-types", "reth-trie-common", @@ -4389,12 +4691,12 @@ dependencies = [ [[package]] name = "reth-primitives-traits" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-genesis", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", + "alloy-genesis 0.5.4", "alloy-primitives", "alloy-rlp", "byteorder", @@ -4409,16 +4711,16 @@ dependencies = [ [[package]] name = "reth-provider" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-eips", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rpc-types-engine", "auto_impl", "dashmap", "itertools 0.13.0", - "metrics 0.23.0", + "metrics", "notify", "parking_lot", "rayon", @@ -4436,6 +4738,7 @@ dependencies = [ "reth-network-p2p", "reth-nippy-jar", "reth-node-types", + "reth-optimism-primitives", "reth-primitives", "reth-prune-types", "reth-stages-types", @@ -4451,8 +4754,8 @@ dependencies = [ [[package]] name = "reth-prune-types" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-primitives", "bytes", @@ -4465,11 +4768,11 @@ dependencies = [ [[package]] name = "reth-revm" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "reth-chainspec", - "reth-consensus-common", + "alloy-eips 0.5.4", + "alloy-primitives", "reth-execution-errors", "reth-primitives", "reth-prune-types", @@ -4480,8 +4783,8 @@ dependencies = [ [[package]] name = "reth-rpc-layer" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-rpc-types-engine", "http 1.1.0", @@ -4491,44 +4794,28 @@ dependencies = [ "tracing", ] -[[package]] -name = "reth-rpc-types" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types", - "alloy-rpc-types-admin", - "alloy-rpc-types-anvil", - "alloy-rpc-types-beacon", - "alloy-rpc-types-debug", - "alloy-rpc-types-engine", - "alloy-rpc-types-mev", - "alloy-rpc-types-trace", - "alloy-rpc-types-txpool", - "alloy-serde", - "jsonrpsee-types", - "op-alloy-rpc-types", - "op-alloy-rpc-types-engine", -] - [[package]] name = "reth-rpc-types-compat" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", - "alloy-rpc-types", + "alloy-rpc-types 0.5.4", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth 0.5.4", + "alloy-serde 0.5.4", "reth-primitives", - "reth-rpc-types", "reth-trie-common", + "serde", ] [[package]] name = "reth-stages-types" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-primitives", "bytes", @@ -4540,8 +4827,8 @@ dependencies = [ [[package]] name = "reth-static-file-types" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-primitives", "derive_more", @@ -4551,10 +4838,11 @@ dependencies = [ [[package]] name = "reth-storage-api" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-eips", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "auto_impl", "reth-chainspec", @@ -4570,10 +4858,10 @@ dependencies = [ [[package]] name = "reth-storage-errors" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-eips", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", "derive_more", @@ -4583,13 +4871,13 @@ dependencies = [ [[package]] name = "reth-tasks" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "auto_impl", "dyn-clone", "futures-util", - "metrics 0.23.0", + "metrics", "reth-metrics", "thiserror", "tokio", @@ -4599,8 +4887,8 @@ dependencies = [ [[package]] name = "reth-tracing" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "clap", "eyre", @@ -4614,18 +4902,20 @@ dependencies = [ [[package]] name = "reth-transaction-pool" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-eips", + "alloy-consensus 0.5.4", + "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", "aquamarine", "auto_impl", "bitflags 2.6.0", "futures-util", - "metrics 0.23.0", + "metrics", "parking_lot", + "rand", "reth-chain-state", "reth-chainspec", "reth-eth-wire-types", @@ -4648,15 +4938,15 @@ dependencies = [ [[package]] name = "reth-trie" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ + "alloy-consensus 0.5.4", "alloy-primitives", "alloy-rlp", "auto_impl", - "derive_more", "itertools 0.13.0", - "metrics 0.23.0", + "metrics", "rayon", "reth-execution-errors", "reth-metrics", @@ -4665,16 +4955,17 @@ dependencies = [ "reth-storage-errors", "reth-trie-common", "revm", + "serde", "tracing", ] [[package]] name = "reth-trie-common" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ - "alloy-consensus", - "alloy-genesis", + "alloy-consensus 0.5.4", + "alloy-genesis 0.5.4", "alloy-primitives", "alloy-rlp", "alloy-trie", @@ -4690,22 +4981,18 @@ dependencies = [ [[package]] name = "reth-trie-db" -version = "1.0.7" -source = "git+https://github.com/paradigmxyz/reth.git?tag=v1.0.7#75b7172cf77eb4fd65fe1a6924f75066fb09fcd1" +version = "1.1.0" +source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" dependencies = [ "alloy-primitives", "alloy-rlp", - "auto_impl", "derive_more", - "itertools 0.13.0", - "metrics 0.23.0", - "rayon", + "metrics", "reth-db", "reth-db-api", "reth-execution-errors", "reth-metrics", "reth-primitives", - "reth-stages-types", "reth-storage-errors", "reth-trie", "reth-trie-common", @@ -4715,9 +5002,9 @@ dependencies = [ [[package]] name = "revm" -version = "14.0.2" +version = "17.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9f3f55d0414c3d73902d876ba3d55a654f05fe937089fbf5f34b1ced26d78d5" +checksum = "055bee6a81aaeee8c2389ae31f0d4de87f44df24f4444a1116f9755fd87a76ad" dependencies = [ "auto_impl", "cfg-if", @@ -4730,9 +5017,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "10.0.2" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713dbb271acd13afb06dcd460c1dc43da211e7ac9bc73cdf13528f615f55f96b" +checksum = "fac2034454f8bc69dc7d3c94cdb1b57559e27f5ef0518771f1787de543d7d6a1" dependencies = [ "revm-primitives", "serde", @@ -4740,12 +5027,11 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "11.0.2" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73010c271d53fa7904e9845338e95f3955eb1200a0355e0abfdb89c41aaa9cd" +checksum = "7a88c8c7c5f9b988a9e65fc0990c6ce859cdb74114db705bd118a96d22d08027" dependencies = [ "aurora-engine-modexp", - "blst", "c-kzg", "cfg-if", "k256", @@ -4760,11 +5046,12 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "9.0.2" +version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a6bff9dbde3370a5ac9555104117f7e6039b3cc76e8d5d9d01899088beca2a" +checksum = "0d11fa1e195b0bebaf3fb18596f314a13ba3a4cb1fdd16d3465934d812fd921e" dependencies = [ - "alloy-eips", + "alloy-eip2930", + "alloy-eip7702 0.3.2", "alloy-primitives", "auto_impl", "bitflags 2.6.0", @@ -4773,7 +5060,6 @@ dependencies = [ "cfg-if", "dyn-clone", "enumn", - "hashbrown 0.14.5", "hex", "serde", ] @@ -4857,7 +5143,7 @@ dependencies = [ "alloy", "alloy-primitives", "alloy-rpc-types-engine", - "alloy-rpc-types-eth", + "alloy-rpc-types-eth 0.5.4", "anyhow", "assert_cmd", "clap", @@ -4869,16 +5155,22 @@ dependencies = [ "hyper", "hyper-util", "jsonrpsee", - "metrics 0.24.0", + "lru 0.10.1", + "metrics", "metrics-derive", "metrics-exporter-prometheus", "metrics-process", "metrics-util", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", + "opentelemetry", + "opentelemetry-http", + "opentelemetry-otlp", + "opentelemetry_sdk", "predicates", "reqwest", "reth-optimism-payload-builder", + "reth-payload-primitives", "reth-rpc-layer", "serde", "serde_json", @@ -4886,6 +5178,7 @@ dependencies = [ "tokio", "tower 0.4.13", "tracing", + "tracing-opentelemetry", "tracing-subscriber", ] @@ -4943,6 +5236,9 @@ name = "rustc-hash" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +dependencies = [ + "rand", +] [[package]] name = "rustc-hex" @@ -4970,9 +5266,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.37" +version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ "bitflags 2.6.0", "errno", @@ -4983,9 +5279,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.13" +version = "0.23.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ "aws-lc-rs", "log", @@ -5025,19 +5321,18 @@ dependencies = [ [[package]] name = "rustls-pemfile" -version = "2.1.3" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64 0.22.1", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-platform-verifier" @@ -5080,9 +5375,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty-fork" @@ -5113,9 +5408,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.24" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ "windows-sys 0.59.0", ] @@ -5186,9 +5481,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.11.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -5220,18 +5515,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -5240,9 +5535,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "indexmap 2.6.0", "itoa", @@ -5276,9 +5571,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.9.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817" dependencies = [ "base64 0.22.1", "chrono", @@ -5294,9 +5589,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.9.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d" dependencies = [ "darling", "proc-macro2", @@ -5416,6 +5711,12 @@ dependencies = [ "serde", ] +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + [[package]] name = "socket2" version = "0.5.7" @@ -5538,9 +5839,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" +checksum = "16320d4a2021ba1a32470b3759676114a918885e9800e68ad60f2c67969fba62" dependencies = [ "paste", "proc-macro2", @@ -5565,16 +5866,15 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.30.13" +version = "0.31.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3" +checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" dependencies = [ - "cfg-if", "core-foundation-sys", "libc", + "memchr", "ntapi", - "once_cell", - "windows 0.52.0", + "windows 0.57.0", ] [[package]] @@ -5606,9 +5906,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", @@ -5625,18 +5925,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", @@ -5739,9 +6039,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", @@ -5821,15 +6121,45 @@ checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" -version = "0.22.21" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.6.0", "toml_datetime", "winnow", ] +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64 0.22.1", + "bytes", + "h2", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2", + "tokio", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.4.13" @@ -5838,9 +6168,13 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", + "indexmap 1.9.3", "pin-project", "pin-project-lite", + "rand", + "slab", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -5961,6 +6295,24 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tracing-opentelemetry" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc58af5d3f6c5811462cabb3289aec0093f7338e367e5a33d28c0433b3c7360b" +dependencies = [ + "js-sys", + "once_cell", + "opentelemetry", + "opentelemetry_sdk", + "smallvec", + "tracing", + "tracing-core", + "tracing-log", + "tracing-subscriber", + "web-time", +] + [[package]] name = "tracing-serde" version = "0.1.3" @@ -6006,9 +6358,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "ucd-trie" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "uint" @@ -6030,9 +6382,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" @@ -6138,9 +6490,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", "once_cell", @@ -6149,9 +6501,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -6164,9 +6516,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.43" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -6176,9 +6528,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6186,9 +6538,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -6199,15 +6551,25 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.93" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" -version = "0.3.70" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ "js-sys", "wasm-bindgen", @@ -6267,11 +6629,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.52.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" dependencies = [ - "windows-core 0.52.0", + "windows-core 0.57.0", "windows-targets 0.52.6", ] @@ -6294,19 +6656,42 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", "windows-strings", "windows-targets 0.52.6", ] +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -6318,6 +6703,17 @@ dependencies = [ "syn 2.0.85", ] +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.85", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -6335,11 +6731,20 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-strings", "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -6355,7 +6760,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] @@ -6509,9 +6914,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] diff --git a/Cargo.toml b/Cargo.toml index 85faad4efb997..b254645958d63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,12 @@ version = "0.1.0" edition = "2021" [dependencies] -alloy-primitives = "0.8.3" -alloy = { version = "0.3.6", features = ["eips", "rpc-types"] } -op-alloy-rpc-types-engine = "0.2.12" -op-alloy-rpc-types = "0.2.12" -alloy-rpc-types-engine = "0.3.6" -alloy-rpc-types-eth = "0.3.6" +alloy-primitives = "0.8.10" +alloy = { version = "0.4.2", features = ["eips", "rpc-types"] } +op-alloy-rpc-types-engine = "0.5.1" +op-alloy-rpc-types = "0.5.1" +alloy-rpc-types-engine = "0.5.4" +alloy-rpc-types-eth = "0.5.4" tokio = { version = "1", features = ["full"] } tracing = "0.1.4" tracing-subscriber = { version = "0.3.11", features = ["env-filter"] } @@ -17,6 +17,7 @@ serde = { version = "1", features = ["derive"] } thiserror = "1.0" clap = { version = "4", features = ["derive", "env"] } jsonrpsee = {version = "0.24.4", features = ["server", "http-client", "macros"]} +lru = "0.10.0" reqwest = "0.12.5" http = "1.1.0" dotenv = "0.15.0" @@ -26,8 +27,14 @@ http-body-util = "0.1.2" hyper = { version = "1.4.1", features = ["full"] } hyper-util = { version = "0.1", features = ["full"] } serde_json = "1.0.96" -reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.7" } -reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.0.7", features = ["optimism"] } +reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", branch = "main" } +reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", branch = "main", features = ["optimism"] } +reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth.git", branch = "main" } +opentelemetry = { version = "0.26.0", features = ["trace"] } +opentelemetry-http = "0.26.0" +opentelemetry-otlp = { version = "0.26.0", features = ["http-proto", "http-json", "reqwest-client", "trace"] } +opentelemetry_sdk = { version = "0.26.0", features = ["rt-tokio"] } +tracing-opentelemetry = "0.27.0" futures = "0.3.31" metrics-derive = "0.1" metrics = "0.24.0" diff --git a/Dockerfile b/Dockerfile index d7636168d6e73..f59d9fa2cddac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,13 +1,16 @@ FROM lukemathwalker/cargo-chef:latest AS chef WORKDIR /app +# Install system dependencies RUN apt-get update && apt-get -y upgrade && apt-get install -y libclang-dev pkg-config +# Prepare build plan FROM chef AS planner COPY ./Cargo.toml ./Cargo.lock ./ COPY ./src ./src RUN cargo chef prepare +# Build application FROM chef AS builder COPY --from=planner /app/recipe.json . RUN cargo chef cook --release @@ -15,6 +18,9 @@ COPY . . RUN cargo build --release FROM debian:stable-slim AS runtime + WORKDIR /app + COPY --from=builder /app/target/release/rollup-boost /usr/local/bin/ + ENTRYPOINT ["/usr/local/bin/rollup-boost"] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index aa2910706eb75..1b43ead9ba338 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,11 +10,17 @@ use jsonrpsee::RpcModule; use metrics::ServerMetrics; use metrics_exporter_prometheus::PrometheusBuilder; use metrics_util::layers::{PrefixLayer, Stack}; +use opentelemetry::global; +use opentelemetry_otlp::WithExportConfig; +use opentelemetry_sdk::propagation::TraceContextPropagator; +use opentelemetry_sdk::trace::Config; +use opentelemetry_sdk::Resource; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService}; use server::{EngineApiServer, EthEngineApi}; use std::sync::Arc; use std::{net::SocketAddr, path::PathBuf}; +use tracing::error; use tracing::{info, Level}; use tracing_subscriber::EnvFilter; @@ -71,6 +77,10 @@ struct Args { #[arg(long, env, default_value = "false")] metrics: bool, + /// OTLP endpoint + #[arg(long, env, default_value = "http://localhost:4317")] + otlp_endpoint: String, + /// Log level #[arg(long, env, default_value = "info")] log_level: Level, @@ -104,6 +114,11 @@ async fn main() -> Result<()> { (None, None) }; + // telemetry setup + if args.tracing { + init_tracing(&args.otlp_endpoint); + } + // Handle JWT secret let jwt_secret = match (args.jwt_path, args.jwt_token) { (Some(file), None) => { @@ -186,6 +201,29 @@ fn create_client( .map_err(|e| Error::InitRPCClient(e.to_string())) } +fn init_tracing(endpoint: &str) { + global::set_text_map_propagator(TraceContextPropagator::new()); + let provider = opentelemetry_otlp::new_pipeline() + .tracing() + .with_exporter( + opentelemetry_otlp::new_exporter() + .tonic() + .with_endpoint(endpoint), + ) + .with_trace_config(Config::default().with_resource(Resource::new(vec![ + opentelemetry::KeyValue::new("service.name", "rollup-boost"), + ]))) + .install_batch(opentelemetry_sdk::runtime::Tokio); + match provider { + Ok(provider) => { + let _ = global::set_tracer_provider(provider); + } + Err(e) => { + error!(message = "failed to initiate tracing provider", "error" = %e); + } + } +} + #[cfg(test)] mod tests { use assert_cmd::Command; @@ -202,7 +240,7 @@ mod tests { use super::*; - const AUTH_PORT: u32 = 8551; + const AUTH_PORT: u32 = 8550; const AUTH_ADDR: &str = "0.0.0.0"; const SECRET: &str = "f79ae8046bc11c9927afe911db7143c51a806c4a537cc08e0d37140b0192f430"; diff --git a/src/server.rs b/src/server.rs index 4445e6f5f7a1d..a194c7ce58a45 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,4 @@ +use crate::metrics::ServerMetrics; use alloy::primitives::B256; use alloy_primitives::Bytes; use alloy_rpc_types_engine::{ @@ -10,14 +11,81 @@ use jsonrpsee::http_client::HttpClient; use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::error::INVALID_REQUEST_CODE; use jsonrpsee::types::{ErrorCode, ErrorObject}; -use op_alloy_rpc_types_engine::{ - AsInnerPayload, OptimismExecutionPayloadEnvelopeV3, OptimismPayloadAttributes, -}; +use lru::LruCache; +use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; +use opentelemetry::global::{self, BoxedSpan, BoxedTracer}; +use opentelemetry::trace::{Span, TraceContextExt, Tracer}; +use opentelemetry::{Context, KeyValue}; +use reth_optimism_payload_builder::{OpPayloadAttributes, OptimismPayloadBuilderAttributes}; +use reth_payload_primitives::PayloadBuilderAttributes; use reth_rpc_layer::AuthClientService; +use std::num::NonZero; use std::sync::Arc; +use tokio::sync::Mutex; use tracing::{debug, error, info}; -use crate::metrics::ServerMetrics; +const CACHE_SIZE: usize = 100; + +struct PayloadTraceContext { + tracer: Arc, + block_hash_to_payload_ids: Arc>>>, + payload_id_to_span: Arc>>>, +} + +impl PayloadTraceContext { + fn new() -> Self { + PayloadTraceContext { + tracer: Arc::new(global::tracer("rollup-boost")), + block_hash_to_payload_ids: Arc::new(Mutex::new(LruCache::new( + NonZero::new(CACHE_SIZE).unwrap(), + ))), + payload_id_to_span: Arc::new(Mutex::new(LruCache::new( + NonZero::new(CACHE_SIZE).unwrap(), + ))), + } + } + + async fn store(&self, payload_id: PayloadId, parent_hash: B256, parent_span: BoxedSpan) { + let mut store = self.payload_id_to_span.lock().await; + store.put(payload_id, Arc::new(parent_span)); + let mut store = self.block_hash_to_payload_ids.lock().await; + if let Some(payload_ids) = store.get_mut(&parent_hash) { + payload_ids.push(payload_id); + } else { + store.put(parent_hash, vec![payload_id]); + } + } + + async fn retrieve_by_parent_hash(&self, parent_hash: &B256) -> Option>> { + let mut block_hash_to_payload_ids = self.block_hash_to_payload_ids.lock().await; + let mut payload_id_to_span = self.payload_id_to_span.lock().await; + block_hash_to_payload_ids + .get(parent_hash) + .map(move |payload_ids| { + payload_ids + .clone() + .iter() + .filter_map(move |payload_id| payload_id_to_span.get(payload_id).cloned()) + .collect() + }) + } + + async fn retrieve_by_payload_id(&self, payload_id: &PayloadId) -> Option> { + let mut store = self.payload_id_to_span.lock().await; + store.get(payload_id).cloned() + } + + async fn remove_by_parent_hash(&self, block_hash: &B256) { + let mut block_hash_to_payload_ids = self.block_hash_to_payload_ids.lock().await; + let mut payload_id_to_span = self.payload_id_to_span.lock().await; + if let Some(payload_ids) = block_hash_to_payload_ids.get_mut(block_hash) { + for payload_id in payload_ids { + payload_id_to_span.pop(payload_id); + } + } + block_hash_to_payload_ids.pop(block_hash); + } +} #[rpc(server, client, namespace = "engine")] pub trait EngineApi { @@ -25,14 +93,14 @@ pub trait EngineApi { async fn fork_choice_updated_v3( &self, fork_choice_state: ForkchoiceState, - payload_attributes: Option, + payload_attributes: Option, ) -> RpcResult; #[method(name = "getPayloadV3")] async fn get_payload_v3( &self, payload_id: PayloadId, - ) -> RpcResult; + ) -> RpcResult; #[method(name = "newPayloadV3")] async fn new_payload_v3( @@ -49,17 +117,18 @@ pub trait EthApi { async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult; } -pub struct EthEngineApi> { - l2_client: Arc>, - builder_client: Arc>, +pub struct EthEngineApi>> { + l2_client: Arc, + builder_client: Arc, boost_sync: bool, metrics: Option>, + payload_trace_context: Arc, } -impl EthEngineApi { +impl EthEngineApi { pub fn new( - l2_client: Arc>, - builder_client: Arc>, + l2_client: Arc, + builder_client: Arc, boost_sync: bool, metrics: Option>, ) -> Self { @@ -68,12 +137,16 @@ impl EthEngineApi { builder_client, boost_sync, metrics, + payload_trace_context: Arc::new(PayloadTraceContext::new()), } } } #[async_trait] -impl EthApiServer for EthEngineApi { +impl EthApiServer for EthEngineApi +where + C: EthApiClient + Send + Sync + 'static, +{ async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult { debug!( message = "received send_raw_transaction", @@ -109,11 +182,14 @@ impl EthApiServer for EthEngineApi { } #[async_trait] -impl EngineApiServer for EthEngineApi { +impl EngineApiServer for EthEngineApi +where + C: EngineApiClient + Send + Sync + 'static, +{ async fn fork_choice_updated_v3( &self, fork_choice_state: ForkchoiceState, - payload_attributes: Option, + payload_attributes: Option, ) -> RpcResult { info!( message = "received fork_choice_updated_v3", @@ -133,6 +209,41 @@ impl EngineApiServer for EthEngineApi { }; if should_send_to_builder { + let span: Option = if let Some(payload_attributes) = + payload_attributes.clone() + { + let mut parent_span = self + .payload_trace_context + .tracer + .start_with_context("build-block", &Context::current()); + let builder_attrs = OptimismPayloadBuilderAttributes::try_new( + fork_choice_state.head_block_hash, + payload_attributes, + 3, + ) + .unwrap(); + let payload_id = builder_attrs.payload_id(); + parent_span.set_attribute(KeyValue::new( + "parent_hash", + fork_choice_state.head_block_hash.to_string(), + )); + parent_span + .set_attribute(KeyValue::new("timestamp", builder_attrs.timestamp() as i64)); + parent_span.set_attribute(KeyValue::new("payload_id", payload_id.to_string())); + let ctx = + Context::current().with_remote_span_context(parent_span.span_context().clone()); + self.payload_trace_context + .store(payload_id, fork_choice_state.head_block_hash, parent_span) + .await; + Some( + self.payload_trace_context + .tracer + .start_with_context("fcu", &ctx), + ) + } else { + None + }; + // async call to builder to trigger payload building and sync if let Some(metrics) = &self.metrics { metrics.fcu_count.increment(1); @@ -140,16 +251,24 @@ impl EngineApiServer for EthEngineApi { let builder = self.builder_client.clone(); let attr = payload_attributes.clone(); tokio::spawn(async move { - builder.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { + let _ = builder.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { let payload_id_str = response.payload_id.map(|id| id.to_string()).unwrap_or_default(); if response.is_invalid() { error!(message = "builder rejected fork_choice_updated_v3 with attributes", "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); } else { info!(message = "called fork_choice_updated_v3 to builder with payload attributes", "payload_status" = %response.payload_status.status, "payload_id" = payload_id_str); - } - }).map_err(|e| { - error!(message = "error calling fork_choice_updated_v3 to builder", "error" = %e, "head_block_hash" = %fork_choice_state.head_block_hash); - }) + } + }) + .map_err(|e| { + error!( + message = "error calling fork_choice_updated_v3 to builder", + "error" = %e, + "head_block_hash" = %fork_choice_state.head_block_hash + ); + }); + if let Some(mut s) = span { + s.end() + }; }); } else { info!(message = "no payload attributes provided or no_tx_pool is set", "head_block_hash" = %fork_choice_state.head_block_hash); @@ -174,20 +293,32 @@ impl EngineApiServer for EthEngineApi { async fn get_payload_v3( &self, payload_id: PayloadId, - ) -> RpcResult { + ) -> RpcResult { info!(message = "received get_payload_v3", "payload_id" = %payload_id); let l2_client_future = self.l2_client.get_payload_v3(payload_id); let builder_client_future = Box::pin(async move { if let Some(metrics) = &self.metrics { metrics.get_payload_count.increment(1); } + let parent_span = self + .payload_trace_context + .retrieve_by_payload_id(&payload_id) + .await; + let span = parent_span.clone().map(|span| { + self.payload_trace_context.tracer.start_with_context( + "get_payload", + &Context::current().with_remote_span_context(span.span_context().clone()), + ) + }); + let builder = self.builder_client.clone(); let payload = builder.get_payload_v3(payload_id).await.map_err(|e| { error!(message = "error calling get_payload_v3 from builder", "error" = %e, "payload_id" = %payload_id); e })?; - info!(message = "received payload from builder", "payload_id" = %payload_id, "block_hash" = %payload.as_v1_payload().block_hash); + let block_hash = ExecutionPayload::from(payload.clone().execution_payload).block_hash(); + info!(message = "received payload from builder", "payload_id" = %payload_id, "block_hash" = %block_hash); // Send the payload to the local execution engine with engine_newPayload to validate the block from the builder. // Otherwise, we do not want to risk the network to a halt since op-node will not be able to propose the block. @@ -199,6 +330,15 @@ impl EngineApiServer for EthEngineApi { error!(message = "error calling new_payload_v3 to validate builder payload", "error" = %e, "payload_id" = %payload_id); e })?; + if let Some(mut s) = span { + s.end(); + }; + if let Some(mut parent) = parent_span { + let parent = Arc::get_mut(&mut parent); + if let Some(parent) = parent { + parent.end(); + } + }; if payload_status.is_invalid() { error!(message = "builder payload was not valid", "payload_status" = %payload_status.status, "payload_id" = %payload_id); Err(ClientError::Call(ErrorObject::owned( @@ -213,7 +353,6 @@ impl EngineApiServer for EthEngineApi { }); let (l2_payload, builder_payload) = tokio::join!(l2_client_future, builder_client_future); - builder_payload.or(l2_payload).map_err(|e| match e { ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it other_error => { @@ -233,20 +372,40 @@ impl EngineApiServer for EthEngineApi { versioned_hashes: Vec, parent_beacon_block_root: B256, ) -> RpcResult { - let block_hash = ExecutionPayload::from(payload.clone()).block_hash(); + let execution_payload = ExecutionPayload::from(payload.clone()); + let block_hash = execution_payload.block_hash(); + let parent_hash = execution_payload.parent_hash(); info!(message = "received new_payload_v3", "block_hash" = %block_hash); - // async call to builder to sync the builder node if self.boost_sync { if let Some(metrics) = &self.metrics { metrics.new_payload_count.increment(1); } + let parent_spans = self + .payload_trace_context + .retrieve_by_parent_hash(&parent_hash) + .await; + let spans: Option> = parent_spans.as_ref().map(|spans| { + spans + .iter() + .map(|span| { + self.payload_trace_context.tracer.start_with_context( + "new_payload", + &Context::current() + .with_remote_span_context(span.span_context().clone()), + ) + }) + .collect() + }); + self.payload_trace_context + .remove_by_parent_hash(&parent_hash) + .await; let builder = self.builder_client.clone(); let builder_payload = payload.clone(); let builder_versioned_hashes = versioned_hashes.clone(); tokio::spawn(async move { - builder.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await + let _ = builder.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await .map(|response: PayloadStatus| { if response.is_invalid() { error!(message = "builder rejected new_payload_v3", "block_hash" = %block_hash); @@ -256,10 +415,12 @@ impl EngineApiServer for EthEngineApi { }).map_err(|e| { error!(message = "error calling new_payload_v3 to builder", "error" = %e, "block_hash" = %block_hash); e - }) + }); + if let Some(mut spans) = spans { + spans.iter_mut().for_each(|s| s.end()); + }; }); } - self.l2_client .new_payload_v3(payload, versioned_hashes, parent_beacon_block_root) .await @@ -276,3 +437,331 @@ impl EngineApiServer for EthEngineApi { }) } } + +#[cfg(test)] +mod tests { + use std::net::SocketAddr; + use std::sync::Mutex; + + use super::*; + + use alloy::hex; + use alloy_primitives::{FixedBytes, U256}; + use alloy_rpc_types_engine::{ + BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, PayloadStatusEnum, + }; + use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; + use jsonrpsee::server::{ServerBuilder, ServerHandle}; + use jsonrpsee::RpcModule; + + const L2_ADDR: &str = "0.0.0.0:8554"; + const BUILDER_ADDR: &str = "0.0.0.0:8555"; + const SERVER_ADDR: &str = "0.0.0.0:8556"; + + #[derive(Debug, Clone)] + pub struct MockEngineServer { + fcu_requests: Arc)>>>, + get_payload_requests: Arc>>, + new_payload_requests: Arc, B256)>>>, + fcu_response: RpcResult, + get_payload_response: RpcResult, + new_payload_response: RpcResult, + } + + impl MockEngineServer { + pub fn new() -> Self { + Self { + fcu_requests: Arc::new(Mutex::new(vec![])), + get_payload_requests: Arc::new(Mutex::new(vec![])), + new_payload_requests: Arc::new(Mutex::new(vec![])), + fcu_response: Ok(ForkchoiceUpdated::new(PayloadStatus::from_status(PayloadStatusEnum::Valid))), + get_payload_response: Ok(OpExecutionPayloadEnvelopeV3{ + execution_payload: ExecutionPayloadV3 { + payload_inner: ExecutionPayloadV2 { + payload_inner: ExecutionPayloadV1 { + base_fee_per_gas: U256::from(7u64), + block_number: 0xa946u64, + block_hash: hex!("a5ddd3f286f429458a39cafc13ffe89295a7efa8eb363cf89a1a4887dbcf272b").into(), + logs_bloom: hex!("00200004000000000000000080000000000200000000000000000000000000000000200000000000000000000000000000000000800000000200000000000000000000000000000000000008000000200000000000000000000001000000000000000000000000000000800000000000000000000100000000000030000000000000000040000000000000000000000000000000000800080080404000000000000008000000000008200000000000200000000000000000000000000000000000000002000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000100000000000000000000").into(), + extra_data: hex!("d883010d03846765746888676f312e32312e31856c696e7578").into(), + gas_limit: 0x1c9c380, + gas_used: 0x1f4a9, + timestamp: 0x651f35b8, + fee_recipient: hex!("f97e180c050e5ab072211ad2c213eb5aee4df134").into(), + parent_hash: hex!("d829192799c73ef28a7332313b3c03af1f2d5da2c36f8ecfafe7a83a3bfb8d1e").into(), + prev_randao: hex!("753888cc4adfbeb9e24e01c84233f9d204f4a9e1273f0e29b43c4c148b2b8b7e").into(), + receipts_root: hex!("4cbc48e87389399a0ea0b382b1c46962c4b8e398014bf0cc610f9c672bee3155").into(), + state_root: hex!("017d7fa2b5adb480f5e05b2c95cb4186e12062eed893fc8822798eed134329d1").into(), + transactions: vec![], + }, + withdrawals: vec![], + }, + blob_gas_used: 0xc0000, + excess_blob_gas: 0x580000, + }, + block_value: U256::from(0), + blobs_bundle: BlobsBundleV1{ + commitments: vec![], + proofs: vec![], + blobs: vec![], + }, + should_override_builder: false, + parent_beacon_block_root: B256::ZERO, + }), + new_payload_response: Ok(PayloadStatus::from_status(PayloadStatusEnum::Valid)), + } + } + } + + struct TestHarness { + l2_server: ServerHandle, + l2_mock: MockEngineServer, + builder_server: ServerHandle, + builder_mock: MockEngineServer, + proxy_server: ServerHandle, + client: HttpClient, + } + + impl TestHarness { + async fn new( + boost_sync: bool, + l2_mock: Option, + builder_mock: Option, + ) -> Self { + let l2_client = HttpClientBuilder::new() + .build(format!("http://{L2_ADDR}")) + .unwrap(); + let builder_client = HttpClientBuilder::new() + .build(format!("http://{BUILDER_ADDR}")) + .unwrap(); + + let eth_engine_api = EthEngineApi::new( + Arc::new(l2_client), + Arc::new(builder_client), + boost_sync, + None, + ); + let mut module: RpcModule<()> = RpcModule::new(()); + module + .merge(EngineApiServer::into_rpc(eth_engine_api)) + .unwrap(); + + let proxy_server = ServerBuilder::default() + .build("0.0.0.0:8556".parse::().unwrap()) + .await + .unwrap() + .start(module); + let l2_mock = l2_mock.unwrap_or(MockEngineServer::new()); + let builder_mock = builder_mock.unwrap_or(MockEngineServer::new()); + let l2_server = spawn_server(l2_mock.clone(), L2_ADDR).await; + let builder_server = spawn_server(builder_mock.clone(), BUILDER_ADDR).await; + TestHarness { + l2_server, + l2_mock, + builder_server, + builder_mock, + proxy_server, + client: HttpClient::builder() + .build(format!("http://{SERVER_ADDR}")) + .unwrap(), + } + } + + async fn cleanup(self) { + self.l2_server.stop().unwrap(); + self.l2_server.stopped().await; + self.builder_server.stop().unwrap(); + self.builder_server.stopped().await; + self.proxy_server.stop().unwrap(); + self.proxy_server.stopped().await; + } + } + + #[tokio::test] + async fn test_server() { + engine_success().await; + boost_sync_enabled().await; + builder_payload_err().await; + } + + async fn engine_success() { + let test_harness = TestHarness::new(false, None, None).await; + + // test fork_choice_updated_v3 success + let fcu = ForkchoiceState { + head_block_hash: FixedBytes::random(), + safe_block_hash: FixedBytes::random(), + finalized_block_hash: FixedBytes::random(), + }; + let fcu_response = test_harness.client.fork_choice_updated_v3(fcu, None).await; + assert!(fcu_response.is_ok()); + let fcu_requests = test_harness.l2_mock.fcu_requests.clone(); + let fcu_requests_mu = fcu_requests.lock().unwrap(); + let fcu_requests_builder = test_harness.builder_mock.fcu_requests.clone(); + let fcu_requests_builder_mu = fcu_requests_builder.lock().unwrap(); + assert_eq!(fcu_requests_mu.len(), 1); + assert_eq!(fcu_requests_builder_mu.len(), 0); + let req: &(ForkchoiceState, Option) = fcu_requests_mu.get(0).unwrap(); + assert_eq!(req.0, fcu); + assert_eq!(req.1, None); + + // test new_payload_v3 success + let new_payload_response = test_harness + .client + .new_payload_v3( + test_harness + .l2_mock + .get_payload_response + .clone() + .unwrap() + .execution_payload + .clone(), + vec![], + B256::ZERO, + ) + .await; + assert!(new_payload_response.is_ok()); + let new_payload_requests = test_harness.l2_mock.new_payload_requests.clone(); + let new_payload_requests_mu = new_payload_requests.lock().unwrap(); + let new_payload_requests_builder = test_harness.builder_mock.new_payload_requests.clone(); + let new_payload_requests_builder_mu = new_payload_requests_builder.lock().unwrap(); + assert_eq!(new_payload_requests_mu.len(), 1); + assert_eq!(new_payload_requests_builder_mu.len(), 0); + let req: &(ExecutionPayloadV3, Vec>, B256) = + new_payload_requests_mu.get(0).unwrap(); + assert_eq!( + req.0, + test_harness + .l2_mock + .get_payload_response + .clone() + .unwrap() + .execution_payload + .clone() + ); + assert_eq!(req.1, Vec::>::new()); + assert_eq!(req.2, B256::ZERO); + drop(new_payload_requests_mu); + + // test get_payload_v3 success + let get_payload_response = test_harness + .client + .get_payload_v3(PayloadId::new([0, 0, 0, 0, 0, 0, 0, 1])) + .await; + assert!(get_payload_response.is_ok()); + let get_payload_requests = test_harness.l2_mock.get_payload_requests.clone(); + let get_payload_requests_mu = get_payload_requests.lock().unwrap(); + let get_payload_requests_builder = test_harness.builder_mock.get_payload_requests.clone(); + let get_payload_requests_builder_mu = get_payload_requests_builder.lock().unwrap(); + let new_payload_requests = test_harness.l2_mock.new_payload_requests.clone(); + let new_payload_requests_mu = new_payload_requests.lock().unwrap(); + assert_eq!(get_payload_requests_builder_mu.len(), 1); + assert_eq!(get_payload_requests_mu.len(), 1); + assert_eq!(new_payload_requests_mu.len(), 2); + let req: &PayloadId = get_payload_requests_mu.get(0).unwrap(); + assert_eq!(*req, PayloadId::new([0, 0, 0, 0, 0, 0, 0, 1])); + + test_harness.cleanup().await; + } + + async fn boost_sync_enabled() { + let test_harness = TestHarness::new(true, None, None).await; + + // test fork_choice_updated_v3 success + let fcu = ForkchoiceState { + head_block_hash: FixedBytes::random(), + safe_block_hash: FixedBytes::random(), + finalized_block_hash: FixedBytes::random(), + }; + let fcu_response = test_harness.client.fork_choice_updated_v3(fcu, None).await; + assert!(fcu_response.is_ok()); + let fcu_requests = test_harness.l2_mock.fcu_requests.clone(); + let fcu_requests_mu = fcu_requests.lock().unwrap(); + let fcu_requests_builder = test_harness.builder_mock.fcu_requests.clone(); + let fcu_requests_builder_mu = fcu_requests_builder.lock().unwrap(); + assert_eq!(fcu_requests_mu.len(), 1); + assert_eq!(fcu_requests_builder_mu.len(), 1); + + // test new_payload_v3 success + let new_payload_response = test_harness + .client + .new_payload_v3( + test_harness + .l2_mock + .get_payload_response + .clone() + .unwrap() + .execution_payload + .clone(), + vec![], + B256::ZERO, + ) + .await; + assert!(new_payload_response.is_ok()); + let new_payload_requests = test_harness.l2_mock.new_payload_requests.clone(); + let new_payload_requests_mu = new_payload_requests.lock().unwrap(); + let new_payload_requests_builder = test_harness.builder_mock.new_payload_requests.clone(); + let new_payload_requests_builder_mu = new_payload_requests_builder.lock().unwrap(); + assert_eq!(new_payload_requests_mu.len(), 1); + assert_eq!(new_payload_requests_builder_mu.len(), 1); + + test_harness.cleanup().await; + } + + async fn builder_payload_err() { + let mut l2_mock = MockEngineServer::new(); + l2_mock.new_payload_response = l2_mock.new_payload_response.clone().map(|mut status| { + status.status = PayloadStatusEnum::Invalid { + validation_error: "test".to_string(), + }; + status + }); + l2_mock.get_payload_response = l2_mock.get_payload_response.clone().map(|mut payload| { + payload.block_value = U256::from(10); + payload + }); + let test_harness = TestHarness::new(true, Some(l2_mock), None).await; + + // test get_payload_v3 return l2 payload if builder payload is invalid + let get_payload_response = test_harness + .client + .get_payload_v3(PayloadId::new([0, 0, 0, 0, 0, 0, 0, 0])) + .await; + assert!(get_payload_response.is_ok()); + assert_eq!(get_payload_response.unwrap().block_value, U256::from(10)); + + test_harness.cleanup().await; + } + + async fn spawn_server(mock_engine_server: MockEngineServer, addr: &str) -> ServerHandle { + let server = ServerBuilder::default().build(addr).await.unwrap(); + let mut module: RpcModule<()> = RpcModule::new(()); + module + .register_method("engine_forkchoiceUpdatedV3", move |params, _, _| { + let params: (ForkchoiceState, Option) = params.parse()?; + let mut fcu_requests = mock_engine_server.fcu_requests.lock().unwrap(); + fcu_requests.push(params); + mock_engine_server.fcu_response.clone() + }) + .unwrap(); + module + .register_method("engine_getPayloadV3", move |params, _, _| { + let params: (PayloadId,) = params.parse()?; + let mut get_payload_requests = + mock_engine_server.get_payload_requests.lock().unwrap(); + get_payload_requests.push(params.0); + mock_engine_server.get_payload_response.clone() + }) + .unwrap(); + module + .register_method("engine_newPayloadV3", move |params, _, _| { + let params: (ExecutionPayloadV3, Vec, B256) = params.parse()?; + let mut new_payload_requests = + mock_engine_server.new_payload_requests.lock().unwrap(); + new_payload_requests.push(params); + mock_engine_server.new_payload_response.clone() + }) + .unwrap(); + server.start(module) + } +} From 5dc5a41c596485395c405b7f32a93e4facb0d634 Mon Sep 17 00:00:00 2001 From: avalonche Date: Wed, 30 Oct 2024 16:49:25 +1100 Subject: [PATCH 021/429] use latest reth commit (flashbots/rollup-boost) --- Cargo.lock | 109 +++++++++++++++++++++++++++-------------------------- Cargo.toml | 6 +-- 2 files changed, 58 insertions(+), 57 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22f8df035e5cc..9ebaca9c64535 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4033,7 +4033,7 @@ dependencies = [ [[package]] name = "reth-basic-payload-builder" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-eips 0.5.4", @@ -4059,7 +4059,7 @@ dependencies = [ [[package]] name = "reth-blockchain-tree-api" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", "reth-consensus", @@ -4072,7 +4072,7 @@ dependencies = [ [[package]] name = "reth-chain-state" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4096,7 +4096,7 @@ dependencies = [ [[package]] name = "reth-chainspec" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-chains", "alloy-consensus 0.5.4", @@ -4116,7 +4116,7 @@ dependencies = [ [[package]] name = "reth-codecs" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-eips 0.5.4", @@ -4133,7 +4133,7 @@ dependencies = [ [[package]] name = "reth-codecs-derive" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "convert_case", "proc-macro2", @@ -4144,7 +4144,7 @@ dependencies = [ [[package]] name = "reth-consensus" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4156,7 +4156,7 @@ dependencies = [ [[package]] name = "reth-consensus-common" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-eips 0.5.4", @@ -4170,7 +4170,7 @@ dependencies = [ [[package]] name = "reth-db" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", "bytes", @@ -4201,7 +4201,7 @@ dependencies = [ [[package]] name = "reth-db-api" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-genesis 0.5.4", "alloy-primitives", @@ -4224,7 +4224,7 @@ dependencies = [ [[package]] name = "reth-db-models" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", "bytes", @@ -4237,7 +4237,7 @@ dependencies = [ [[package]] name = "reth-engine-primitives" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", "reth-execution-types", @@ -4250,7 +4250,7 @@ dependencies = [ [[package]] name = "reth-errors" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "reth-blockchain-tree-api", "reth-consensus", @@ -4263,7 +4263,7 @@ dependencies = [ [[package]] name = "reth-eth-wire-types" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-chains", "alloy-eips 0.5.4", @@ -4281,7 +4281,7 @@ dependencies = [ [[package]] name = "reth-ethereum-engine-primitives" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4300,7 +4300,7 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-chains", "alloy-primitives", @@ -4317,7 +4317,7 @@ dependencies = [ [[package]] name = "reth-evm" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4342,7 +4342,7 @@ dependencies = [ [[package]] name = "reth-execution-errors" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4358,7 +4358,7 @@ dependencies = [ [[package]] name = "reth-execution-types" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4372,7 +4372,7 @@ dependencies = [ [[package]] name = "reth-fs-util" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "serde", "serde_json", @@ -4382,7 +4382,7 @@ dependencies = [ [[package]] name = "reth-libmdbx" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "bitflags 2.6.0", "byteorder", @@ -4399,7 +4399,7 @@ dependencies = [ [[package]] name = "reth-mdbx-sys" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "bindgen 0.70.1", "cc", @@ -4408,7 +4408,7 @@ dependencies = [ [[package]] name = "reth-metrics" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "metrics", "metrics-derive", @@ -4417,7 +4417,7 @@ dependencies = [ [[package]] name = "reth-net-banlist" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", ] @@ -4425,7 +4425,7 @@ dependencies = [ [[package]] name = "reth-network-p2p" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4445,7 +4445,7 @@ dependencies = [ [[package]] name = "reth-network-peers" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -4458,7 +4458,7 @@ dependencies = [ [[package]] name = "reth-network-types" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "reth-ethereum-forks", "reth-net-banlist", @@ -4470,7 +4470,7 @@ dependencies = [ [[package]] name = "reth-nippy-jar" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "anyhow", "bincode", @@ -4487,7 +4487,7 @@ dependencies = [ [[package]] name = "reth-node-types" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "reth-chainspec", "reth-db-api", @@ -4499,7 +4499,7 @@ dependencies = [ [[package]] name = "reth-optimism-chainspec" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-chains", "alloy-consensus 0.5.4", @@ -4520,7 +4520,7 @@ dependencies = [ [[package]] name = "reth-optimism-consensus" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-primitives", @@ -4537,7 +4537,7 @@ dependencies = [ [[package]] name = "reth-optimism-evm" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-eips 0.5.4", @@ -4564,7 +4564,7 @@ dependencies = [ [[package]] name = "reth-optimism-forks" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-chains", "alloy-primitives", @@ -4576,13 +4576,14 @@ dependencies = [ [[package]] name = "reth-optimism-payload-builder" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-eips 0.5.4", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-engine", + "op-alloy-consensus", "op-alloy-rpc-types-engine", "reth-basic-payload-builder", "reth-chain-state", @@ -4610,7 +4611,7 @@ dependencies = [ [[package]] name = "reth-optimism-primitives" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-primitives", @@ -4620,7 +4621,7 @@ dependencies = [ [[package]] name = "reth-payload-builder" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", "alloy-rpc-types 0.5.4", @@ -4640,7 +4641,7 @@ dependencies = [ [[package]] name = "reth-payload-primitives" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4663,7 +4664,7 @@ dependencies = [ [[package]] name = "reth-primitives" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-eips 0.5.4", @@ -4692,7 +4693,7 @@ dependencies = [ [[package]] name = "reth-primitives-traits" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-eips 0.5.4", @@ -4712,7 +4713,7 @@ dependencies = [ [[package]] name = "reth-provider" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4755,7 +4756,7 @@ dependencies = [ [[package]] name = "reth-prune-types" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", "bytes", @@ -4769,7 +4770,7 @@ dependencies = [ [[package]] name = "reth-revm" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4784,7 +4785,7 @@ dependencies = [ [[package]] name = "reth-rpc-layer" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-rpc-types-engine", "http 1.1.0", @@ -4797,7 +4798,7 @@ dependencies = [ [[package]] name = "reth-rpc-types-compat" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-eips 0.5.4", @@ -4815,7 +4816,7 @@ dependencies = [ [[package]] name = "reth-stages-types" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", "bytes", @@ -4828,7 +4829,7 @@ dependencies = [ [[package]] name = "reth-static-file-types" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", "derive_more", @@ -4839,7 +4840,7 @@ dependencies = [ [[package]] name = "reth-storage-api" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-eips 0.5.4", @@ -4859,7 +4860,7 @@ dependencies = [ [[package]] name = "reth-storage-errors" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-eips 0.5.4", "alloy-primitives", @@ -4872,7 +4873,7 @@ dependencies = [ [[package]] name = "reth-tasks" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "auto_impl", "dyn-clone", @@ -4888,7 +4889,7 @@ dependencies = [ [[package]] name = "reth-tracing" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "clap", "eyre", @@ -4903,7 +4904,7 @@ dependencies = [ [[package]] name = "reth-transaction-pool" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-eips 0.5.4", @@ -4939,7 +4940,7 @@ dependencies = [ [[package]] name = "reth-trie" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-primitives", @@ -4962,7 +4963,7 @@ dependencies = [ [[package]] name = "reth-trie-common" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-consensus 0.5.4", "alloy-genesis 0.5.4", @@ -4982,7 +4983,7 @@ dependencies = [ [[package]] name = "reth-trie-db" version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?branch=main#dd18af1f1617ed855a889fe208ea4ba772409fb5" +source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" dependencies = [ "alloy-primitives", "alloy-rlp", diff --git a/Cargo.toml b/Cargo.toml index b254645958d63..0dbfebfcd6598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,9 +27,9 @@ http-body-util = "0.1.2" hyper = { version = "1.4.1", features = ["full"] } hyper-util = { version = "0.1", features = ["full"] } serde_json = "1.0.96" -reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", branch = "main" } -reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", branch = "main", features = ["optimism"] } -reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth.git", branch = "main" } +reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "734c78fdfb46cc5a97971450ed74c6cbdf62d5af" } +reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", rev = "734c78fdfb46cc5a97971450ed74c6cbdf62d5af", features = ["optimism"] } +reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth.git", rev = "734c78fdfb46cc5a97971450ed74c6cbdf62d5af" } opentelemetry = { version = "0.26.0", features = ["trace"] } opentelemetry-http = "0.26.0" opentelemetry-otlp = { version = "0.26.0", features = ["http-proto", "http-json", "reqwest-client", "trace"] } From beaa7ba92cc5705e86aab5969fc9253d48ce70e9 Mon Sep 17 00:00:00 2001 From: avalonche Date: Thu, 31 Oct 2024 13:29:54 +1100 Subject: [PATCH 022/429] Disable coloured logging by default (flashbots/rollup-boost) --- src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.rs b/src/main.rs index 1b43ead9ba338..26006e42bc67d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,6 +97,7 @@ async fn main() -> Result<()> { // Initialize logging tracing_subscriber::fmt() .with_env_filter(EnvFilter::new(args.log_level.to_string())) // Set the log level + .with_ansi(false) // Disable colored logging .init(); let (metrics, handler) = if args.metrics { From 0f341d6c0dd26f48f5125de07d9869382a701723 Mon Sep 17 00:00:00 2001 From: avalonche Date: Tue, 5 Nov 2024 19:02:59 +1100 Subject: [PATCH 023/429] Add builder url to logs (flashbots/rollup-boost) --- .dockerignore | 5 ++++ Dockerfile | 15 ++++++----- src/main.rs | 15 ++++++----- src/server.rs | 72 +++++++++++++++++++++++++++++++++++---------------- 4 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000000..debbdd2c2e66d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +# Ignore build artifacts +target + +# Ignore version control directories +.git diff --git a/Dockerfile b/Dockerfile index f59d9fa2cddac..df0e582651550 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,6 @@ FROM lukemathwalker/cargo-chef:latest AS chef WORKDIR /app -# Install system dependencies -RUN apt-get update && apt-get -y upgrade && apt-get install -y libclang-dev pkg-config - # Prepare build plan FROM chef AS planner COPY ./Cargo.toml ./Cargo.lock ./ @@ -13,14 +10,18 @@ RUN cargo chef prepare # Build application FROM chef AS builder COPY --from=planner /app/recipe.json . + +# Install system dependencies +RUN apt-get update && \ + apt-get install -y openssl libclang-dev libssl3 && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + RUN cargo chef cook --release COPY . . RUN cargo build --release -FROM debian:stable-slim AS runtime - -WORKDIR /app - +FROM chef AS final COPY --from=builder /app/target/release/rollup-boost /usr/local/bin/ ENTRYPOINT ["/usr/local/bin/rollup-boost"] \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 26006e42bc67d..b90eba66dac57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,8 +17,9 @@ use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService}; -use server::{EngineApiServer, EthEngineApi}; +use server::{EngineApiServer, EthEngineApi, HttpClientWrapper}; use std::sync::Arc; +use std::time::Duration; use std::{net::SocketAddr, path::PathBuf}; use tracing::error; use tracing::{info, Level}; @@ -191,15 +192,17 @@ async fn main() -> Result<()> { fn create_client( url: &str, jwt_secret: JwtSecret, -) -> Result>> { +) -> Result>>> { // Create a middleware that adds a new JWT token to every request. let auth_layer = AuthClientLayer::new(jwt_secret); let client_middleware = tower::ServiceBuilder::new().layer(auth_layer); - HttpClientBuilder::new() + let client = HttpClientBuilder::new() .set_http_middleware(client_middleware) + .request_timeout(Duration::from_secs(10)) .build(url) - .map_err(|e| Error::InitRPCClient(e.to_string())) + .map_err(|e| Error::InitRPCClient(e.to_string()))?; + Ok(HttpClientWrapper::new(client, url.to_string())) } fn init_tracing(endpoint: &str) { @@ -265,7 +268,7 @@ mod tests { let secret = JwtSecret::from_hex(SECRET).unwrap(); let url = format!("http://{}:{}", AUTH_ADDR, AUTH_PORT); let client = create_client(url.as_str(), secret); - let response = send_request(client.unwrap()).await; + let response = send_request(client.unwrap().client).await; assert!(response.is_ok()); assert_eq!(response.unwrap(), "You are the dark lord"); } @@ -274,7 +277,7 @@ mod tests { let secret = JwtSecret::random(); let url = format!("http://{}:{}", AUTH_ADDR, AUTH_PORT); let client = create_client(url.as_str(), secret); - let response = send_request(client.unwrap()).await; + let response = send_request(client.unwrap().client).await; assert!(response.is_err()); assert!(matches!( response.unwrap_err(), diff --git a/src/server.rs b/src/server.rs index a194c7ce58a45..bb704198697f6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -117,7 +117,18 @@ pub trait EthApi { async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult; } -pub struct EthEngineApi>> { +pub struct HttpClientWrapper>> { + pub client: C, + pub url: String, +} + +impl HttpClientWrapper { + pub fn new(client: C, url: String) -> Self { + Self { client, url } + } +} + +pub struct EthEngineApi { l2_client: Arc, builder_client: Arc, boost_sync: bool, @@ -143,9 +154,9 @@ impl EthEngineApi { } #[async_trait] -impl EthApiServer for EthEngineApi +impl EthApiServer for EthEngineApi> where - C: EthApiClient + Send + Sync + 'static, + C: EthApiClient + Send + Sync + Clone + 'static, { async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult { debug!( @@ -157,15 +168,17 @@ where metrics.send_raw_tx_count.increment(1); } - let builder = self.builder_client.clone(); + let builder_client = self.builder_client.client.clone(); + let url = self.builder_client.url.clone(); let tx_bytes = bytes.clone(); tokio::spawn(async move { - builder.send_raw_transaction(tx_bytes).await.map_err(|e| { - error!(message = "error calling send_raw_transaction for builder", "error" = %e); + builder_client.send_raw_transaction(tx_bytes).await.map_err(|e| { + error!(message = "error calling send_raw_transaction for builder", "url" = url, "error" = %e); }) }); self.l2_client + .client .send_raw_transaction(bytes) .await .map_err(|e| match e { @@ -173,6 +186,7 @@ where other_error => { error!( message = "error calling send_raw_transaction for l2 client", + "url" = self.l2_client.url, "error" = %other_error, ); ErrorCode::InternalError.into() @@ -182,9 +196,9 @@ where } #[async_trait] -impl EngineApiServer for EthEngineApi +impl EngineApiServer for EthEngineApi> where - C: EngineApiClient + Send + Sync + 'static, + C: EngineApiClient + Send + Sync + Clone + 'static, { async fn fork_choice_updated_v3( &self, @@ -251,17 +265,18 @@ where let builder = self.builder_client.clone(); let attr = payload_attributes.clone(); tokio::spawn(async move { - let _ = builder.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { + let _ = builder.client.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { let payload_id_str = response.payload_id.map(|id| id.to_string()).unwrap_or_default(); if response.is_invalid() { - error!(message = "builder rejected fork_choice_updated_v3 with attributes", "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); + error!(message = "builder rejected fork_choice_updated_v3 with attributes", "url" = builder.url, "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); } else { - info!(message = "called fork_choice_updated_v3 to builder with payload attributes", "payload_status" = %response.payload_status.status, "payload_id" = payload_id_str); + info!(message = "called fork_choice_updated_v3 to builder with payload attributes", "url" = builder.url, "payload_status" = %response.payload_status.status, "payload_id" = payload_id_str); } }) .map_err(|e| { error!( message = "error calling fork_choice_updated_v3 to builder", + "url" = builder.url, "error" = %e, "head_block_hash" = %fork_choice_state.head_block_hash ); @@ -275,6 +290,7 @@ where } self.l2_client + .client .fork_choice_updated_v3(fork_choice_state, payload_attributes) .await .map_err(|e| match e { @@ -282,6 +298,7 @@ where other_error => { error!( message = "error calling fork_choice_updated_v3 for l2 client", + "url" = self.l2_client.url, "error" = %other_error, "head_block_hash" = %fork_choice_state.head_block_hash, ); @@ -295,7 +312,7 @@ where payload_id: PayloadId, ) -> RpcResult { info!(message = "received get_payload_v3", "payload_id" = %payload_id); - let l2_client_future = self.l2_client.get_payload_v3(payload_id); + let l2_client_future = self.l2_client.client.get_payload_v3(payload_id); let builder_client_future = Box::pin(async move { if let Some(metrics) = &self.metrics { metrics.get_payload_count.increment(1); @@ -312,8 +329,8 @@ where }); let builder = self.builder_client.clone(); - let payload = builder.get_payload_v3(payload_id).await.map_err(|e| { - error!(message = "error calling get_payload_v3 from builder", "error" = %e, "payload_id" = %payload_id); + let payload = builder.client.get_payload_v3(payload_id).await.map_err(|e| { + error!(message = "error calling get_payload_v3 from builder", "url" = builder.url, "error" = %e, "payload_id" = %payload_id); e })?; @@ -326,8 +343,8 @@ where if let Some(metrics) = &self.metrics { metrics.new_payload_count.increment(1); } - let payload_status = self.l2_client.new_payload_v3(payload.execution_payload.clone(), vec![], payload.parent_beacon_block_root).await.map_err(|e| { - error!(message = "error calling new_payload_v3 to validate builder payload", "error" = %e, "payload_id" = %payload_id); + let payload_status = self.l2_client.client.new_payload_v3(payload.execution_payload.clone(), vec![], payload.parent_beacon_block_root).await.map_err(|e| { + error!(message = "error calling new_payload_v3 to validate builder payload", "url" = self.l2_client.url, "error" = %e, "payload_id" = %payload_id); e })?; if let Some(mut s) = span { @@ -340,7 +357,7 @@ where } }; if payload_status.is_invalid() { - error!(message = "builder payload was not valid", "payload_status" = %payload_status.status, "payload_id" = %payload_id); + error!(message = "builder payload was not valid", "url" = builder.url, "payload_status" = %payload_status.status, "payload_id" = %payload_id); Err(ClientError::Call(ErrorObject::owned( INVALID_REQUEST_CODE, "Builder payload was not valid", @@ -358,6 +375,7 @@ where other_error => { error!( message = "error calling get_payload_v3", + "url" = self.builder_client.url, "error" = %other_error, "payload_id" = %payload_id ); @@ -401,19 +419,20 @@ where .remove_by_parent_hash(&parent_hash) .await; - let builder = self.builder_client.clone(); + let builder = self.builder_client.client.clone(); + let builder_url = self.builder_client.url.clone(); let builder_payload = payload.clone(); let builder_versioned_hashes = versioned_hashes.clone(); tokio::spawn(async move { let _ = builder.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await .map(|response: PayloadStatus| { if response.is_invalid() { - error!(message = "builder rejected new_payload_v3", "block_hash" = %block_hash); + error!(message = "builder rejected new_payload_v3", "url" = builder_url, "block_hash" = %block_hash); } else { - info!(message = "called new_payload_v3 to builder", "payload_status" = %response.status, "block_hash" = %block_hash); + info!(message = "called new_payload_v3 to builder", "url" = builder_url, "payload_status" = %response.status, "block_hash" = %block_hash); } }).map_err(|e| { - error!(message = "error calling new_payload_v3 to builder", "error" = %e, "block_hash" = %block_hash); + error!(message = "error calling new_payload_v3 to builder", "url" = builder_url, "error" = %e, "block_hash" = %block_hash); e }); if let Some(mut spans) = spans { @@ -422,6 +441,7 @@ where }); } self.l2_client + .client .new_payload_v3(payload, versioned_hashes, parent_beacon_block_root) .await .map_err(|e| match e { @@ -536,8 +556,14 @@ mod tests { .unwrap(); let eth_engine_api = EthEngineApi::new( - Arc::new(l2_client), - Arc::new(builder_client), + Arc::new(HttpClientWrapper::new( + l2_client, + format!("http://{L2_ADDR}"), + )), + Arc::new(HttpClientWrapper::new( + builder_client, + format!("http://{BUILDER_ADDR}"), + )), boost_sync, None, ); From 924843c1c8295c98f40e8ca902ecdb6b76e0dbe9 Mon Sep 17 00:00:00 2001 From: avalonche Date: Wed, 6 Nov 2024 14:01:15 +1100 Subject: [PATCH 024/429] Add configurable client config (flashbots/rollup-boost) --- src/main.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index b90eba66dac57..5fc082c8ae8d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,6 +85,14 @@ struct Args { /// Log level #[arg(long, env, default_value = "info")] log_level: Level, + + /// Timeout for the builder client calls in milliseconds + #[arg(long, env, default_value = "200")] + builder_timeout: u64, + + /// Timeout for the l2 client calls in milliseconds + #[arg(long, env, default_value = "2000")] + l2_timeout: u64, } type Result = core::result::Result; @@ -150,10 +158,11 @@ async fn main() -> Result<()> { }; // Initialize the l2 client - let l2_client = create_client(&args.l2_url, jwt_secret)?; + let l2_client = create_client(&args.l2_url, jwt_secret, args.l2_timeout)?; // Initialize the builder client - let builder_client = create_client(&args.builder_url, builder_jwt_secret)?; + let builder_client = + create_client(&args.builder_url, builder_jwt_secret, args.builder_timeout)?; let eth_engine_api = EthEngineApi::new( Arc::new(l2_client), @@ -192,6 +201,7 @@ async fn main() -> Result<()> { fn create_client( url: &str, jwt_secret: JwtSecret, + timeout: u64, ) -> Result>>> { // Create a middleware that adds a new JWT token to every request. let auth_layer = AuthClientLayer::new(jwt_secret); @@ -199,7 +209,7 @@ fn create_client( let client = HttpClientBuilder::new() .set_http_middleware(client_middleware) - .request_timeout(Duration::from_secs(10)) + .request_timeout(Duration::from_millis(timeout)) .build(url) .map_err(|e| Error::InitRPCClient(e.to_string()))?; Ok(HttpClientWrapper::new(client, url.to_string())) @@ -267,7 +277,7 @@ mod tests { async fn valid_jwt() { let secret = JwtSecret::from_hex(SECRET).unwrap(); let url = format!("http://{}:{}", AUTH_ADDR, AUTH_PORT); - let client = create_client(url.as_str(), secret); + let client = create_client(url.as_str(), secret, 2000); let response = send_request(client.unwrap().client).await; assert!(response.is_ok()); assert_eq!(response.unwrap(), "You are the dark lord"); @@ -276,7 +286,7 @@ mod tests { async fn invalid_jwt() { let secret = JwtSecret::random(); let url = format!("http://{}:{}", AUTH_ADDR, AUTH_PORT); - let client = create_client(url.as_str(), secret); + let client = create_client(url.as_str(), secret, 2000); let response = send_request(client.unwrap().client).await; assert!(response.is_err()); assert!(matches!( From 0fe1ce773903316091b421d875989f6799dc588f Mon Sep 17 00:00:00 2001 From: "odysseas.eth" Date: Tue, 12 Nov 2024 07:26:27 +0100 Subject: [PATCH 025/429] Sidecar disambiguation (flashbots/rollup-boost) Also the diagram should change from `sidecar` to `rollup-boost` to be more explicit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a066ea2fc00b4..d64fb655dc721 100644 --- a/README.md +++ b/README.md @@ -50,8 +50,8 @@ cargo run --jwt-token your_jwt_token --l2-url http://localhost:8545 --builder-ur - `rollup-boost` validates the block with `proposer-op-geth` using `engine_newPayload`. - This validation ensures the block will be valid for `proposer-op-geth`, preventing network stalls due to invalid blocks. - If the external block is valid, it is returned to the `proposer-op-node`. -5. As per its normal workflow, the `proposer-op-node` sends another `newPayload` request to the sidecar and another FCU(without) to update the state of its op-geth node. - - In this case, the sidecar just relays the data and does not introspect anything. +5. As per its normal workflow, the `proposer-op-node` sends another `newPayload` request to the `rollup-boost` and another FCU(without) to update the state of its op-geth node. + - In this case, the `rollup-boost` just relays the data and does not introspect anything. - Note that since we already tested `newPayload` on the proposer-op-geth in the previous step, this process should be cached. ![Workflow Diagram](/assets/workflow.svg) From 2b8117377a56c397ea72316ee16ddaa497d44916 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Thu, 5 Dec 2024 11:31:53 +0000 Subject: [PATCH 026/429] Update alloy to fix op-geth extra payload fields (flashbots/rollup-boost) --- Cargo.lock | 1322 ++++++++++++++++++++++++------------------------- Cargo.toml | 15 +- src/main.rs | 3 +- src/server.rs | 9 +- 4 files changed, 648 insertions(+), 701 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ebaca9c64535..2a5ca809bcf33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,26 +40,18 @@ dependencies = [ ] [[package]] -name = "allocator-api2" -version = "0.2.18" +name = "alloc-no-stdlib" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] -name = "alloy" -version = "0.4.2" +name = "alloc-stdlib" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "056f2c01b2aed86e15b43c47d109bfc8b82553dc34e66452875e51247ec31ab2" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" dependencies = [ - "alloy-consensus 0.4.2", - "alloy-core", - "alloy-eips 0.4.2", - "alloy-genesis 0.4.2", - "alloy-provider", - "alloy-rpc-client", - "alloy-rpc-types 0.4.2", - "alloy-serde 0.4.2", - "alloy-transport-http", + "alloc-no-stdlib", ] [[package]] @@ -77,65 +69,33 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705687d5bfd019fee57cf9e206b27b30a9a9617535d5590a02b171e813208f8e" -dependencies = [ - "alloy-eips 0.4.2", - "alloy-primitives", - "alloy-rlp", - "alloy-serde 0.4.2", - "auto_impl", - "c-kzg", - "derive_more", - "serde", -] - -[[package]] -name = "alloy-consensus" -version = "0.5.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed961a48297c732a5d97ee321aa8bb5009ecadbcb077d8bec90cb54e651629" +checksum = "a101d4d016f47f13890a74290fdd17b05dd175191d9337bc600791fb96e4dea8" dependencies = [ - "alloy-eips 0.5.4", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.5.4", + "alloy-serde", + "alloy-trie", "arbitrary", "auto_impl", "c-kzg", "derive_more", + "rand", "serde", ] [[package]] -name = "alloy-core" -version = "0.8.10" +name = "alloy-consensus-any" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72bf30967a232bec83809bea1623031f6285a013096229330c68c406192a4ca" +checksum = "fa60357dda9a3d0f738f18844bd6d0f4a5924cc5cf00bfad2ff1369897966123" dependencies = [ - "alloy-dyn-abi", - "alloy-json-abi", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-sol-types", -] - -[[package]] -name = "alloy-dyn-abi" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5228b189b18b85761340dc9eaac0141148a8503657b36f9bc3a869413d987ca" -dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-type-parser", - "alloy-sol-types", - "const-hex", - "itoa", - "serde", - "serde_json", - "winnow", ] [[package]] @@ -153,20 +113,9 @@ dependencies = [ [[package]] name = "alloy-eip7702" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea59dc42102bc9a1905dc57901edc6dd48b9f38115df86c7d252acba70d71d04" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "serde", -] - -[[package]] -name = "alloy-eip7702" -version = "0.3.2" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64ffc577390ce50234e02d841214b3dc0bea6aaaae8e04bbf3cb82e9a45da9eb" +checksum = "4c986539255fb839d1533c128e190e557e52ff652c9ef62939e233a81dd93f7e" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -179,33 +128,15 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ffb906284a1e1f63c4607da2068c8197458a352d0b3e9796e67353d72a9be85" -dependencies = [ - "alloy-eip2930", - "alloy-eip7702 0.1.1", - "alloy-primitives", - "alloy-rlp", - "alloy-serde 0.4.2", - "c-kzg", - "derive_more", - "once_cell", - "serde", - "sha2", -] - -[[package]] -name = "alloy-eips" -version = "0.5.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b69e06cf9c37be824b9d26d6d101114fdde6af0c87de2828b414c05c4b3daa71" +checksum = "8b6755b093afef5925f25079dd5a7c8d096398b804ba60cb5275397b06b31689" dependencies = [ "alloy-eip2930", - "alloy-eip7702 0.3.2", + "alloy-eip7702", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.5.4", + "alloy-serde", "arbitrary", "c-kzg", "derive_more", @@ -218,104 +149,34 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8429cf4554eed9b40feec7f4451113e76596086447550275e3def933faf47ce3" -dependencies = [ - "alloy-primitives", - "alloy-serde 0.4.2", - "serde", -] - -[[package]] -name = "alloy-genesis" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde15e14944a88bd6a57d325e9a49b75558746fe16aaccc79713ae50a6a9574c" -dependencies = [ - "alloy-primitives", - "alloy-serde 0.5.4", - "serde", -] - -[[package]] -name = "alloy-json-abi" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31a0f0d51db8a1a30a4d98a9f90e090a94c8f44cb4d9eafc7e03aa6d00aae984" -dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-json-rpc" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fa8a1a3c4cbd221f2b8e3693aeb328fca79a757fe556ed08e47bbbc2a70db7" -dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "serde", - "serde_json", - "thiserror", - "tracing", -] - -[[package]] -name = "alloy-network" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85fa23a6a9d612b52e402c995f2d582c25165ec03ac6edf64c861a76bc5b87cd" -dependencies = [ - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", - "alloy-json-rpc", - "alloy-network-primitives 0.4.2", - "alloy-primitives", - "alloy-rpc-types-eth 0.4.2", - "alloy-serde 0.4.2", - "alloy-signer", - "alloy-sol-types", - "async-trait", - "auto_impl", - "futures-utils-wasm", - "thiserror", -] - -[[package]] -name = "alloy-network-primitives" -version = "0.4.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "801492711d4392b2ccf5fc0bc69e299fa1aab15167d74dcaa9aab96a54f684bd" +checksum = "aeec8e6eab6e52b7c9f918748c9b811e87dbef7312a2e3a2ca1729a92966a6af" dependencies = [ - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", "alloy-primitives", - "alloy-serde 0.4.2", + "alloy-serde", + "alloy-trie", "serde", ] [[package]] name = "alloy-network-primitives" -version = "0.5.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514f70ee2a953db21631cd817b13a1571474ec77ddc03d47616d5e8203489fde" +checksum = "c20219d1ad261da7a6331c16367214ee7ded41d001fabbbd656fbf71898b2773" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", - "alloy-serde 0.5.4", + "alloy-serde", "serde", ] [[package]] name = "alloy-primitives" -version = "0.8.10" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8edae627382349b56cd6a7a2106f4fd69b243a9233e560c55c2e03cabb7e1d3c" +checksum = "9db948902dfbae96a73c2fbf1f7abec62af034ab883e4c777c3fd29702bd6e2c" dependencies = [ "alloy-rlp", "arbitrary", @@ -343,40 +204,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "alloy-provider" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcfaa4ffec0af04e3555686b8aacbcdf7d13638133a0672749209069750f78a6" -dependencies = [ - "alloy-chains", - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", - "alloy-json-rpc", - "alloy-network", - "alloy-network-primitives 0.4.2", - "alloy-primitives", - "alloy-rpc-client", - "alloy-rpc-types-eth 0.4.2", - "alloy-transport", - "alloy-transport-http", - "async-stream", - "async-trait", - "auto_impl", - "dashmap", - "futures", - "futures-utils-wasm", - "lru 0.12.5", - "pin-project", - "reqwest", - "serde", - "serde_json", - "thiserror", - "tokio", - "tracing", - "url", -] - [[package]] name = "alloy-rlp" version = "0.3.9" @@ -396,67 +223,43 @@ checksum = "2b09cae092c27b6f1bde952653a22708691802e57bfef4a2973b80bea21efd3f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", -] - -[[package]] -name = "alloy-rpc-client" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370143ed581aace6e663342d21d209c6b2e34ee6142f7d6675adb518deeaf0dc" -dependencies = [ - "alloy-json-rpc", - "alloy-primitives", - "alloy-transport", - "alloy-transport-http", - "futures", - "pin-project", - "reqwest", - "serde", - "serde_json", - "tokio", - "tokio-stream", - "tower 0.5.1", - "tracing", - "url", + "syn 2.0.90", ] [[package]] name = "alloy-rpc-types" -version = "0.4.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ffc534b7919e18f35e3aa1f507b6f3d9d92ec298463a9f6beaac112809d8d06" +checksum = "5ab686b0fa475d2a4f5916c5f07797734a691ec58e44f0f55d4746ea39cbcefb" dependencies = [ "alloy-primitives", - "alloy-rpc-types-eth 0.4.2", - "alloy-serde 0.4.2", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-serde", "serde", ] [[package]] -name = "alloy-rpc-types" -version = "0.5.4" +name = "alloy-rpc-types-debug" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eea9bf1abdd506f985a53533f5ac01296bcd6102c5e139bbc5d40bc468d2c916" +checksum = "f0294b553785eb3fa7fff2e8aec45e82817258e7e6c9365c034a90cb6baeebc9" dependencies = [ "alloy-primitives", - "alloy-rpc-types-engine", - "alloy-rpc-types-eth 0.5.4", - "alloy-serde 0.5.4", "serde", ] [[package]] name = "alloy-rpc-types-engine" -version = "0.5.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "886d22d41992287a235af2f3af4299b5ced2bcafb81eb835572ad35747476946" +checksum = "5d297268357e3eae834ddd6888b15f764cbc0f4b3be9265f5f6ec239013f3d68" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.5.4", + "alloy-serde", "derive_more", "ethereum_ssz", "ethereum_ssz_derive", @@ -468,35 +271,17 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413f4aa3ccf2c3e4234a047c5fa4727916d7daf25a89f9b765df0ba09784fd87" -dependencies = [ - "alloy-consensus 0.4.2", - "alloy-eips 0.4.2", - "alloy-network-primitives 0.4.2", - "alloy-primitives", - "alloy-rlp", - "alloy-serde 0.4.2", - "alloy-sol-types", - "derive_more", - "itertools 0.13.0", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-rpc-types-eth" -version = "0.5.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b034779a4850b4b03f5be5ea674a1cf7d746b2da762b34d1860ab45e48ca27" +checksum = "a0600b8b5e2dc0cab12cbf91b5a885c35871789fb7b3a57b434bd4fced5b7a8b" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", - "alloy-network-primitives 0.5.4", + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.5.4", + "alloy-serde", "alloy-sol-types", "derive_more", "itertools 0.13.0", @@ -506,20 +291,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dff0ab1cdd43ca001e324dc27ee0e8606bd2161d6623c63e0e0b8c4dfc13600" -dependencies = [ - "alloy-primitives", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-serde" -version = "0.5.4" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028e72eaa9703e4882344983cfe7636ce06d8cce104a78ea62fd19b46659efc4" +checksum = "9afa753a97002a33b2ccb707d9f15f31c81b8c1b786c95b73cc62bb1d1fd0c3f" dependencies = [ "alloy-primitives", "arbitrary", @@ -527,39 +301,25 @@ dependencies = [ "serde_json", ] -[[package]] -name = "alloy-signer" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd4e0ad79c81a27ca659be5d176ca12399141659fef2bcbfdc848da478f4504" -dependencies = [ - "alloy-primitives", - "async-trait", - "auto_impl", - "elliptic-curve", - "k256", - "thiserror", -] - [[package]] name = "alloy-sol-macro" -version = "0.8.10" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841eabaa4710f719fddbc24c95d386eae313f07e6da4babc25830ee37945be0c" +checksum = "3bfd7853b65a2b4f49629ec975fee274faf6dff15ab8894c620943398ef283c0" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.10" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6672337f19d837b9f7073c45853aeb528ed9f7dd6a4154ce683e9e5cb7794014" +checksum = "82ec42f342d9a9261699f8078e57a7a4fda8aaa73c1a212ed3987080e6a9cd13" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -568,88 +328,42 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.10" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dff37dd20bfb118b777c96eda83b2067f4226d2644c5cfa00187b3bc01770ba" +checksum = "ed2c50e6a62ee2b4f7ab3c6d0366e5770a21cad426e109c2f40335a1b3aff3df" dependencies = [ "const-hex", "dunce", "heck", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", "syn-solidity", ] -[[package]] -name = "alloy-sol-type-parser" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b853d42292dbb159671a3edae3b2750277ff130f32b726fe07dc2b17aa6f2b5" -dependencies = [ - "serde", - "winnow", -] - [[package]] name = "alloy-sol-types" -version = "0.8.10" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa828bb1b9a6dc52208fbb18084fb9ce2c30facc2bfda6a5d922349b4990354f" +checksum = "c9dc0fffe397aa17628160e16b89f704098bf3c9d74d5d369ebc239575936de5" dependencies = [ - "alloy-json-abi", "alloy-primitives", "alloy-sol-macro", "const-hex", - "serde", -] - -[[package]] -name = "alloy-transport" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac3e97dad3d31770db0fc89bd6a63b789fbae78963086733f960cf32c483904" -dependencies = [ - "alloy-json-rpc", - "base64 0.22.1", - "futures-util", - "futures-utils-wasm", - "serde", - "serde_json", - "thiserror", - "tokio", - "tower 0.5.1", - "tracing", - "url", -] - -[[package]] -name = "alloy-transport-http" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b367dcccada5b28987c2296717ee04b9a5637aacd78eacb1726ef211678b5212" -dependencies = [ - "alloy-json-rpc", - "alloy-transport", - "reqwest", - "serde_json", - "tower 0.5.1", - "tracing", - "url", ] [[package]] name = "alloy-trie" -version = "0.7.2" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd7f8b3a7c65ca09b3c7bdd7c7d72d7423d026f5247eda96af53d24e58315c1" +checksum = "3a5fd8fea044cc9a8c8a50bb6f28e31f0385d820f116c5b98f6f4e55d6e5590b" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -742,7 +456,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -903,6 +617,22 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "async-compression" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "zstd", + "zstd-safe", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -922,7 +652,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -933,7 +663,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -960,7 +690,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1110,7 +840,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.85", + "syn 2.0.90", "which", ] @@ -1129,7 +859,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1196,6 +926,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bstr" version = "1.10.0" @@ -1344,7 +1095,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1380,9 +1131,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0121754e84117e65f9d90648ee6aa4882a6e63110307ab73967a4c5e7e69e586" +checksum = "4b0485bab839b018a8f1723fc5391819fea5f8f0f32288ef8a735fd096b6160c" dependencies = [ "cfg-if", "cpufeatures", @@ -1446,6 +1197,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -1535,7 +1295,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1546,7 +1306,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1602,7 +1362,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1623,7 +1383,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", "unicode-xid", ] @@ -1756,7 +1516,7 @@ checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1813,7 +1573,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -1877,6 +1637,16 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "float-cmp" version = "0.9.0" @@ -1999,7 +1769,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -2032,12 +1802,6 @@ dependencies = [ "slab", ] -[[package]] -name = "futures-utils-wasm" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" - [[package]] name = "generic-array" version = "0.14.7" @@ -2131,8 +1895,6 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ - "allocator-api2", - "equivalent", "foldhash", "serde", ] @@ -2238,6 +2000,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" + [[package]] name = "httparse" version = "1.9.5" @@ -2471,6 +2239,16 @@ version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" +[[package]] +name = "iri-string" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -2520,7 +2298,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.65", "walkdir", ] @@ -2581,7 +2359,7 @@ dependencies = [ "rustc-hash 2.0.0", "serde", "serde_json", - "thiserror", + "thiserror 1.0.65", "tokio", "tracing", ] @@ -2604,7 +2382,7 @@ dependencies = [ "rustls-platform-verifier", "serde", "serde_json", - "thiserror", + "thiserror 1.0.65", "tokio", "tower 0.4.13", "tracing", @@ -2621,7 +2399,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -2643,7 +2421,7 @@ dependencies = [ "serde", "serde_json", "soketto", - "thiserror", + "thiserror 1.0.65", "tokio", "tokio-stream", "tokio-util", @@ -2660,7 +2438,7 @@ dependencies = [ "http 1.1.0", "serde", "serde_json", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -2821,15 +2599,6 @@ dependencies = [ "hashbrown 0.13.2", ] -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.0", -] - [[package]] name = "lz4_flex" version = "0.11.3" @@ -2894,7 +2663,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -2913,7 +2682,7 @@ dependencies = [ "metrics", "metrics-util", "quanta", - "thiserror", + "thiserror 1.0.65", "tokio", "tracing", ] @@ -2958,6 +2727,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3210,7 +2989,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -3247,64 +3026,73 @@ dependencies = [ [[package]] name = "op-alloy-consensus" -version = "0.5.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7c98055fd048073738df0cc6d6537e992a0d8828f39d99a469e870db126dbd" +checksum = "78f0daa0d0936d436a21b57571b1e27c5663aa2ab62f6edae5ba5be999f9f93e" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.5.4", + "alloy-serde", "arbitrary", "derive_more", "serde", - "spin", + "thiserror 2.0.4", ] [[package]] name = "op-alloy-genesis" -version = "0.5.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d631e8113cf88d30e621022677209caa148a9ca3ccb590fd34bbd1c731e3aff3" +checksum = "3eb0964932faa7050b74689f017aca66ffa3e52501080278a81bb0a43836c8dd" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-sol-types", "serde", "serde_repr", + "thiserror 2.0.4", ] [[package]] name = "op-alloy-protocol" -version = "0.5.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b39574acb1873315e6bd89df174f6223e897188fb87eeea2ad1eda04f7d28eb" +checksum = "6d8c057c1a5bdf72d1f86c470a4d90f2d2ad1b273caa547c04cd6affe45b466d" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloc-no-stdlib", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.5.4", - "derive_more", + "alloy-serde", + "async-trait", + "brotli", + "cfg-if", + "miniz_oxide", "op-alloy-consensus", "op-alloy-genesis", "serde", + "thiserror 2.0.4", + "tracing", + "unsigned-varint", ] [[package]] name = "op-alloy-rpc-types" -version = "0.5.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919e9b69212d61f3c8932bfb717c7ad458ea3fc52072b3433d99994f8223d555" +checksum = "73741855ffaa2041b33cb616d7db7180c1149b648c68c23bee9e15501073fb32" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", - "alloy-network-primitives 0.5.4", + "alloy-consensus", + "alloy-eips", + "alloy-network-primitives", "alloy-primitives", - "alloy-rpc-types-eth 0.5.4", - "alloy-serde 0.5.4", + "alloy-rpc-types-eth", + "alloy-serde", + "derive_more", "op-alloy-consensus", "serde", "serde_json", @@ -3312,18 +3100,22 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types-engine" -version = "0.5.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3a47ea24cee189b4351be247fd138c68571704ee57060cf5a722502f44412c" +checksum = "ebedc32e24013c8b3faea62d091bccbb90f871286fe2238c6f7e2ff29974df8e" dependencies = [ + "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", - "alloy-serde 0.5.4", + "alloy-serde", "derive_more", "ethereum_ssz", + "op-alloy-consensus", + "op-alloy-genesis", "op-alloy-protocol", "serde", "snap", + "thiserror 2.0.4", ] [[package]] @@ -3349,7 +3141,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -3381,7 +3173,7 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror", + "thiserror 1.0.65", ] [[package]] @@ -3413,7 +3205,7 @@ dependencies = [ "prost", "reqwest", "serde_json", - "thiserror", + "thiserror 1.0.65", "tokio", "tonic", ] @@ -3448,7 +3240,7 @@ dependencies = [ "percent-encoding", "rand", "serde_json", - "thiserror", + "thiserror 1.0.65", "tokio", "tokio-stream", ] @@ -3569,7 +3361,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442" dependencies = [ "memchr", - "thiserror", + "thiserror 1.0.65", "ucd-trie", ] @@ -3590,7 +3382,7 @@ checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -3679,7 +3471,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64d1ec885c64d0457d564db4ec299b2dae3f9c02808b8ad9c3a089c591b18033" dependencies = [ "proc-macro2", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -3730,14 +3522,14 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] @@ -3792,7 +3584,7 @@ checksum = "6ff7ff745a347b87471d859a377a9a404361e7efc2a971d73424a6d183c0fc77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -3815,7 +3607,7 @@ dependencies = [ "itertools 0.13.0", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -4032,11 +3824,11 @@ dependencies = [ [[package]] name = "reth-basic-payload-builder" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", "futures-core", @@ -4046,9 +3838,12 @@ dependencies = [ "reth-evm", "reth-metrics", "reth-payload-builder", + "reth-payload-builder-primitives", "reth-payload-primitives", "reth-primitives", + "reth-primitives-traits", "reth-provider", + "reth-revm", "reth-tasks", "reth-transaction-pool", "revm", @@ -4058,25 +3853,26 @@ dependencies = [ [[package]] name = "reth-blockchain-tree-api" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "alloy-eips", "alloy-primitives", "reth-consensus", "reth-execution-errors", "reth-primitives", "reth-storage-errors", - "thiserror", + "thiserror 2.0.4", ] [[package]] name = "reth-chain-state" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", - "auto_impl", "derive_more", "metrics", "parking_lot", @@ -4086,8 +3882,10 @@ dependencies = [ "reth-execution-types", "reth-metrics", "reth-primitives", + "reth-primitives-traits", "reth-storage-api", "reth-trie", + "revm", "tokio", "tokio-stream", "tracing", @@ -4095,13 +3893,13 @@ dependencies = [ [[package]] name = "reth-chainspec" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-chains", - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", - "alloy-genesis 0.5.4", + "alloy-consensus", + "alloy-eips", + "alloy-genesis", "alloy-primitives", "auto_impl", "derive_more", @@ -4115,12 +3913,12 @@ dependencies = [ [[package]] name = "reth-codecs" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", - "alloy-genesis 0.5.4", + "alloy-consensus", + "alloy-eips", + "alloy-genesis", "alloy-primitives", "alloy-trie", "bytes", @@ -4132,53 +3930,56 @@ dependencies = [ [[package]] name = "reth-codecs-derive" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] name = "reth-consensus" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "auto_impl", "derive_more", "reth-primitives", + "reth-primitives-traits", ] [[package]] name = "reth-consensus-common" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "reth-chainspec", "reth-consensus", "reth-primitives", + "reth-primitives-traits", "revm-primitives", ] [[package]] name = "reth-db" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "alloy-consensus", "alloy-primitives", "bytes", "derive_more", "eyre", "metrics", "page_size", - "paste", "reth-db-api", "reth-fs-util", "reth-libmdbx", @@ -4195,15 +3996,16 @@ dependencies = [ "serde", "strum", "sysinfo", - "thiserror", + "thiserror 2.0.4", ] [[package]] name = "reth-db-api" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-genesis 0.5.4", + "alloy-consensus", + "alloy-genesis", "alloy-primitives", "bytes", "derive_more", @@ -4218,72 +4020,85 @@ dependencies = [ "reth-stages-types", "reth-storage-errors", "reth-trie-common", + "roaring", "serde", ] [[package]] name = "reth-db-models" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "alloy-eips", "alloy-primitives", "bytes", "modular-bitfield", "reth-codecs", - "reth-primitives", + "reth-primitives-traits", "serde", ] [[package]] name = "reth-engine-primitives" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "alloy-consensus", "alloy-primitives", + "alloy-rpc-types-engine", + "futures", + "reth-errors", "reth-execution-types", + "reth-payload-builder-primitives", "reth-payload-primitives", "reth-primitives", + "reth-primitives-traits", "reth-trie", "serde", + "thiserror 2.0.4", + "tokio", ] [[package]] name = "reth-errors" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "reth-blockchain-tree-api", "reth-consensus", "reth-execution-errors", "reth-fs-util", "reth-storage-errors", - "thiserror", + "thiserror 2.0.4", ] [[package]] name = "reth-eth-wire-types" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-chains", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", "bytes", "derive_more", "reth-chainspec", "reth-codecs-derive", + "reth-ethereum-forks", "reth-primitives", + "reth-primitives-traits", "serde", - "thiserror", + "thiserror 2.0.4", ] [[package]] name = "reth-ethereum-engine-primitives" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-eips", "alloy-primitives", "alloy-rlp", "alloy-rpc-types-engine", @@ -4291,6 +4106,7 @@ dependencies = [ "reth-chainspec", "reth-engine-primitives", "reth-payload-primitives", + "reth-payload-validator", "reth-primitives", "reth-rpc-types-compat", "serde", @@ -4299,8 +4115,8 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-chains", "alloy-primitives", @@ -4311,15 +4127,16 @@ dependencies = [ "once_cell", "rustc-hash 2.0.0", "serde", - "thiserror-no-std", + "thiserror 2.0.4", ] [[package]] name = "reth-evm" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "auto_impl", "futures-util", @@ -4341,48 +4158,52 @@ dependencies = [ [[package]] name = "reth-execution-errors" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "derive_more", "nybbles", "reth-consensus", "reth-prune-types", "reth-storage-errors", "revm-primitives", + "thiserror 2.0.4", ] [[package]] name = "reth-execution-types" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "reth-execution-errors", "reth-primitives", + "reth-primitives-traits", "reth-trie", + "reth-trie-common", "revm", "serde", + "serde_with", ] [[package]] name = "reth-fs-util" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "serde", "serde_json", - "thiserror", + "thiserror 2.0.4", ] [[package]] name = "reth-libmdbx" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "bitflags 2.6.0", "byteorder", @@ -4392,14 +4213,14 @@ dependencies = [ "parking_lot", "reth-mdbx-sys", "smallvec", - "thiserror", + "thiserror 2.0.4", "tracing", ] [[package]] name = "reth-mdbx-sys" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "bindgen 0.70.1", "cc", @@ -4407,8 +4228,8 @@ dependencies = [ [[package]] name = "reth-metrics" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "metrics", "metrics-derive", @@ -4416,18 +4237,19 @@ dependencies = [ [[package]] name = "reth-net-banlist" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-primitives", ] [[package]] name = "reth-network-p2p" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "auto_impl", "derive_more", @@ -4437,6 +4259,7 @@ dependencies = [ "reth-network-peers", "reth-network-types", "reth-primitives", + "reth-primitives-traits", "reth-storage-errors", "tokio", "tracing", @@ -4444,21 +4267,22 @@ dependencies = [ [[package]] name = "reth-network-peers" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-primitives", "alloy-rlp", "enr", + "secp256k1", "serde_with", - "thiserror", + "thiserror 2.0.4", "url", ] [[package]] name = "reth-network-types" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "reth-ethereum-forks", "reth-net-banlist", @@ -4469,8 +4293,8 @@ dependencies = [ [[package]] name = "reth-nippy-jar" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "anyhow", "bincode", @@ -4479,32 +4303,32 @@ dependencies = [ "memmap2", "reth-fs-util", "serde", - "thiserror", + "thiserror 2.0.4", "tracing", "zstd", ] [[package]] name = "reth-node-types" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "reth-chainspec", "reth-db-api", "reth-engine-primitives", - "reth-primitives", "reth-primitives-traits", + "reth-trie-db", ] [[package]] name = "reth-optimism-chainspec" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-chains", - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", - "alloy-genesis 0.5.4", + "alloy-consensus", + "alloy-eips", + "alloy-genesis", "alloy-primitives", "derive_more", "once_cell", @@ -4519,16 +4343,18 @@ dependencies = [ [[package]] name = "reth-optimism-consensus" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", + "alloy-consensus", "alloy-primitives", + "alloy-trie", "reth-chainspec", "reth-consensus", "reth-consensus-common", "reth-optimism-chainspec", "reth-optimism-forks", + "reth-optimism-primitives", "reth-primitives", "reth-trie-common", "tracing", @@ -4536,16 +4362,17 @@ dependencies = [ [[package]] name = "reth-optimism-evm" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "derive_more", "op-alloy-consensus", "reth-chainspec", "reth-consensus", + "reth-consensus-common", "reth-ethereum-forks", "reth-evm", "reth-execution-errors", @@ -4553,6 +4380,7 @@ dependencies = [ "reth-optimism-chainspec", "reth-optimism-consensus", "reth-optimism-forks", + "reth-optimism-primitives", "reth-primitives", "reth-prune-types", "reth-revm", @@ -4563,8 +4391,8 @@ dependencies = [ [[package]] name = "reth-optimism-forks" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-chains", "alloy-primitives", @@ -4575,13 +4403,14 @@ dependencies = [ [[package]] name = "reth-optimism-payload-builder" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", + "alloy-rpc-types-debug", "alloy-rpc-types-engine", "op-alloy-consensus", "op-alloy-rpc-types-engine", @@ -4595,44 +4424,69 @@ dependencies = [ "reth-optimism-evm", "reth-optimism-forks", "reth-payload-builder", + "reth-payload-builder-primitives", "reth-payload-primitives", + "reth-payload-util", "reth-primitives", "reth-provider", "reth-revm", "reth-rpc-types-compat", "reth-transaction-pool", - "reth-trie", "revm", "sha2", - "thiserror", + "thiserror 2.0.4", "tracing", ] [[package]] name = "reth-optimism-primitives" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", + "alloy-rlp", + "bytes", + "derive_more", + "op-alloy-consensus", + "rand", + "reth-codecs", "reth-primitives", + "reth-primitives-traits", + "revm-primitives", + "secp256k1", + "serde", ] [[package]] name = "reth-payload-builder" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-primitives", - "alloy-rpc-types 0.5.4", + "alloy-rpc-types", "async-trait", "futures-util", "metrics", + "reth-chain-state", "reth-ethereum-engine-primitives", "reth-metrics", + "reth-payload-builder-primitives", + "reth-payload-primitives", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-payload-builder-primitives" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-rpc-types-engine", + "async-trait", + "pin-project", "reth-payload-primitives", - "reth-primitives", - "reth-provider", "tokio", "tokio-stream", "tracing", @@ -4640,37 +4494,55 @@ dependencies = [ [[package]] name = "reth-payload-primitives" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-eips", "alloy-primitives", - "alloy-rpc-types 0.5.4", - "async-trait", + "alloy-rpc-types-engine", "op-alloy-rpc-types-engine", - "pin-project", "reth-chain-state", "reth-chainspec", "reth-errors", "reth-primitives", - "reth-transaction-pool", + "revm-primitives", "serde", - "thiserror", + "thiserror 2.0.4", "tokio", - "tokio-stream", - "tracing", +] + +[[package]] +name = "reth-payload-util" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "reth-primitives", +] + +[[package]] +name = "reth-payload-validator" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-rpc-types", + "reth-chainspec", + "reth-primitives", + "reth-rpc-types-compat", ] [[package]] name = "reth-primitives" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde 0.5.4", + "alloy-serde", + "alloy-trie", "bytes", "c-kzg", "derive_more", @@ -4683,39 +4555,42 @@ dependencies = [ "reth-ethereum-forks", "reth-primitives-traits", "reth-static-file-types", - "reth-trie-common", "revm-primitives", "secp256k1", "serde", + "serde_with", "zstd", ] [[package]] name = "reth-primitives-traits" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", - "alloy-genesis 0.5.4", + "alloy-consensus", + "alloy-eips", + "alloy-genesis", "alloy-primitives", "alloy-rlp", + "auto_impl", "byteorder", "bytes", "derive_more", "modular-bitfield", + "op-alloy-consensus", "reth-codecs", "revm-primitives", - "roaring", "serde", + "serde_with", ] [[package]] name = "reth-provider" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", "auto_impl", @@ -4741,6 +4616,7 @@ dependencies = [ "reth-node-types", "reth-optimism-primitives", "reth-primitives", + "reth-primitives-traits", "reth-prune-types", "reth-stages-types", "reth-storage-api", @@ -4755,8 +4631,8 @@ dependencies = [ [[package]] name = "reth-prune-types" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-primitives", "bytes", @@ -4764,59 +4640,60 @@ dependencies = [ "modular-bitfield", "reth-codecs", "serde", - "thiserror", + "thiserror 2.0.4", ] [[package]] name = "reth-revm" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-eips", "alloy-primitives", "reth-execution-errors", "reth-primitives", + "reth-primitives-traits", "reth-prune-types", "reth-storage-api", "reth-storage-errors", + "reth-trie", "revm", ] [[package]] name = "reth-rpc-layer" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-rpc-types-engine", "http 1.1.0", "jsonrpsee-http-client", "pin-project", "tower 0.4.13", + "tower-http", "tracing", ] [[package]] name = "reth-rpc-types-compat" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-rpc-types 0.5.4", "alloy-rpc-types-engine", - "alloy-rpc-types-eth 0.5.4", - "alloy-serde 0.5.4", + "alloy-rpc-types-eth", + "jsonrpsee-types", "reth-primitives", - "reth-trie-common", "serde", ] [[package]] name = "reth-stages-types" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-primitives", "bytes", @@ -4828,8 +4705,8 @@ dependencies = [ [[package]] name = "reth-static-file-types" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-primitives", "derive_more", @@ -4839,48 +4716,54 @@ dependencies = [ [[package]] name = "reth-storage-api" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", + "alloy-rpc-types-engine", "auto_impl", "reth-chainspec", + "reth-db", "reth-db-api", "reth-db-models", "reth-execution-types", "reth-primitives", + "reth-primitives-traits", "reth-prune-types", "reth-stages-types", "reth-storage-errors", "reth-trie", + "reth-trie-db", + "revm", ] [[package]] name = "reth-storage-errors" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-eips 0.5.4", + "alloy-eips", "alloy-primitives", "alloy-rlp", "derive_more", "reth-fs-util", - "reth-primitives", + "reth-primitives-traits", + "reth-static-file-types", ] [[package]] name = "reth-tasks" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "auto_impl", "dyn-clone", "futures-util", "metrics", "reth-metrics", - "thiserror", + "thiserror 2.0.4", "tokio", "tracing", "tracing-futures", @@ -4888,8 +4771,8 @@ dependencies = [ [[package]] name = "reth-tracing" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "clap", "eyre", @@ -4903,11 +4786,11 @@ dependencies = [ [[package]] name = "reth-transaction-pool" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-eips 0.5.4", + "alloy-consensus", + "alloy-eips", "alloy-primitives", "alloy-rlp", "aquamarine", @@ -4923,7 +4806,9 @@ dependencies = [ "reth-execution-types", "reth-fs-util", "reth-metrics", + "reth-payload-util", "reth-primitives", + "reth-primitives-traits", "reth-storage-api", "reth-tasks", "revm", @@ -4931,7 +4816,7 @@ dependencies = [ "schnellru", "serde", "smallvec", - "thiserror", + "thiserror 2.0.4", "tokio", "tokio-stream", "tracing", @@ -4939,12 +4824,13 @@ dependencies = [ [[package]] name = "reth-trie" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", + "alloy-consensus", "alloy-primitives", "alloy-rlp", + "alloy-trie", "auto_impl", "itertools 0.13.0", "metrics", @@ -4955,20 +4841,21 @@ dependencies = [ "reth-stages-types", "reth-storage-errors", "reth-trie-common", + "reth-trie-sparse", "revm", - "serde", "tracing", ] [[package]] name = "reth-trie-common" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus 0.5.4", - "alloy-genesis 0.5.4", + "alloy-consensus", + "alloy-genesis", "alloy-primitives", "alloy-rlp", + "alloy-rpc-types-eth", "alloy-trie", "bytes", "derive_more", @@ -4982,8 +4869,8 @@ dependencies = [ [[package]] name = "reth-trie-db" -version = "1.1.0" -source = "git+https://github.com/paradigmxyz/reth.git?rev=734c78fdfb46cc5a97971450ed74c6cbdf62d5af#734c78fdfb46cc5a97971450ed74c6cbdf62d5af" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -4996,16 +4883,30 @@ dependencies = [ "reth-primitives", "reth-storage-errors", "reth-trie", - "reth-trie-common", "revm", "tracing", ] +[[package]] +name = "reth-trie-sparse" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "reth-execution-errors", + "reth-primitives-traits", + "reth-tracing", + "reth-trie-common", + "smallvec", + "thiserror 2.0.4", +] + [[package]] name = "revm" -version = "17.1.0" +version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "055bee6a81aaeee8c2389ae31f0d4de87f44df24f4444a1116f9755fd87a76ad" +checksum = "15689a3c6a8d14b647b4666f2e236ef47b5a5133cdfd423f545947986fff7013" dependencies = [ "auto_impl", "cfg-if", @@ -5018,9 +4919,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "13.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac2034454f8bc69dc7d3c94cdb1b57559e27f5ef0518771f1787de543d7d6a1" +checksum = "74e3f11d0fed049a4a10f79820c59113a79b38aed4ebec786a79d5c667bfeb51" dependencies = [ "revm-primitives", "serde", @@ -5028,9 +4929,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "14.0.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a88c8c7c5f9b988a9e65fc0990c6ce859cdb74114db705bd118a96d22d08027" +checksum = "e381060af24b750069a2b2d2c54bba273d84e8f5f9e8026fc9262298e26cc336" dependencies = [ "aurora-engine-modexp", "c-kzg", @@ -5047,12 +4948,12 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "13.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d11fa1e195b0bebaf3fb18596f314a13ba3a4cb1fdd16d3465934d812fd921e" +checksum = "3702f132bb484f4f0d0ca4f6fbde3c82cfd745041abbedd6eda67730e1868ef0" dependencies = [ "alloy-eip2930", - "alloy-eip7702 0.3.2", + "alloy-eip7702", "alloy-primitives", "auto_impl", "bitflags 2.6.0", @@ -5141,10 +5042,9 @@ dependencies = [ name = "rollup-boost" version = "0.1.0" dependencies = [ - "alloy", "alloy-primitives", "alloy-rpc-types-engine", - "alloy-rpc-types-eth 0.5.4", + "alloy-rpc-types-eth", "anyhow", "assert_cmd", "clap", @@ -5156,7 +5056,7 @@ dependencies = [ "hyper", "hyper-util", "jsonrpsee", - "lru 0.10.1", + "lru", "metrics", "metrics-derive", "metrics-exporter-prometheus", @@ -5175,7 +5075,7 @@ dependencies = [ "reth-rpc-layer", "serde", "serde_json", - "thiserror", + "thiserror 1.0.65", "tokio", "tower 0.4.13", "tracing", @@ -5455,6 +5355,7 @@ checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ "rand", "secp256k1-sys", + "serde", ] [[package]] @@ -5531,7 +5432,7 @@ checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -5555,7 +5456,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -5597,7 +5498,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -5684,7 +5585,7 @@ checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" dependencies = [ "num-bigint", "num-traits", - "thiserror", + "thiserror 1.0.65", "time", ] @@ -5749,9 +5650,6 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] [[package]] name = "spki" @@ -5794,7 +5692,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -5829,9 +5727,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.85" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -5840,14 +5738,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.10" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16320d4a2021ba1a32470b3759676114a918885e9800e68ad60f2c67969fba62" +checksum = "da0523f59468a2696391f2a772edc089342aacd53c3caa2ac3264e598edf119b" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -5867,9 +5765,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.31.4" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" +checksum = "e3b5ae3f4f7d64646c46c4cae4e3f01d1c5d255c7406fdd7c7f999a94e488791" dependencies = [ "core-foundation-sys", "libc", @@ -5930,38 +5828,38 @@ version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.65", ] [[package]] -name = "thiserror-impl" -version = "1.0.65" +name = "thiserror" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +checksum = "2f49a1853cf82743e3b7950f77e0f4d622ca36cf4317cba00c767838bac8d490" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.85", + "thiserror-impl 2.0.4", ] [[package]] -name = "thiserror-impl-no-std" -version = "2.0.2" +name = "thiserror-impl" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58e6318948b519ba6dc2b442a6d0b904ebfb8d411a3ad3e07843615a72249758" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.90", ] [[package]] -name = "thiserror-no-std" -version = "2.0.2" +name = "thiserror-impl" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3ad459d94dd517257cc96add8a43190ee620011bb6e6cdc82dafd97dfafafea" +checksum = "8381894bb3efe0c4acac3ded651301ceee58a15d47c2e34885ed1908ad667061" dependencies = [ - "thiserror-impl-no-std", + "proc-macro2", + "quote", + "syn 2.0.90", ] [[package]] @@ -6064,7 +5962,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -6195,6 +6093,37 @@ dependencies = [ "tower-service", ] +[[package]] +name = "tower-http" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +dependencies = [ + "async-compression", + "base64 0.22.1", + "bitflags 2.6.0", + "bytes", + "futures-core", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "http-range-header", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower 0.5.1", + "tower-layer", + "tower-service", + "tracing", + "uuid", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -6226,7 +6155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.65", "time", "tracing-subscriber", ] @@ -6239,7 +6168,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -6381,6 +6310,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" +[[package]] +name = "unicase" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" + [[package]] name = "unicode-bidi" version = "0.3.17" @@ -6414,6 +6349,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unsigned-varint" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb066959b24b5196ae73cb057f45598450d2c5f71460e98c49b738086eff9c06" + [[package]] name = "untrusted" version = "0.9.0" @@ -6437,6 +6378,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +dependencies = [ + "getrandom", +] + [[package]] name = "valuable" version = "0.1.0" @@ -6511,7 +6461,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", "wasm-bindgen-shared", ] @@ -6545,7 +6495,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -6690,7 +6640,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -6701,7 +6651,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -6712,7 +6662,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -6723,7 +6673,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -6949,7 +6899,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] @@ -6969,7 +6919,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.85", + "syn 2.0.90", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0dbfebfcd6598..3b051a474275e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,11 @@ version = "0.1.0" edition = "2021" [dependencies] +op-alloy-rpc-types-engine = "0.7.3" +op-alloy-rpc-types = "0.7.3" +alloy-rpc-types-engine = "0.7.3" +alloy-rpc-types-eth = "0.7.3" alloy-primitives = "0.8.10" -alloy = { version = "0.4.2", features = ["eips", "rpc-types"] } -op-alloy-rpc-types-engine = "0.5.1" -op-alloy-rpc-types = "0.5.1" -alloy-rpc-types-engine = "0.5.4" -alloy-rpc-types-eth = "0.5.4" tokio = { version = "1", features = ["full"] } tracing = "0.1.4" tracing-subscriber = { version = "0.3.11", features = ["env-filter"] } @@ -27,9 +26,9 @@ http-body-util = "0.1.2" hyper = { version = "1.4.1", features = ["full"] } hyper-util = { version = "0.1", features = ["full"] } serde_json = "1.0.96" -reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "734c78fdfb46cc5a97971450ed74c6cbdf62d5af" } -reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", rev = "734c78fdfb46cc5a97971450ed74c6cbdf62d5af", features = ["optimism"] } -reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth.git", rev = "734c78fdfb46cc5a97971450ed74c6cbdf62d5af" } +reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" } +reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222", features = ["optimism"] } +reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" } opentelemetry = { version = "0.26.0", features = ["trace"] } opentelemetry-http = "0.26.0" opentelemetry-otlp = { version = "0.26.0", features = ["http-proto", "http-json", "reqwest-client", "trace"] } diff --git a/src/main.rs b/src/main.rs index 5fc082c8ae8d3..ffe0e4eb4b48c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -use alloy_rpc_types_engine::JwtSecret; use clap::{arg, ArgGroup, Parser}; use dotenv::dotenv; use error::Error; @@ -16,7 +15,7 @@ use opentelemetry_sdk::propagation::TraceContextPropagator; use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; -use reth_rpc_layer::{AuthClientLayer, AuthClientService}; +use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use server::{EngineApiServer, EthEngineApi, HttpClientWrapper}; use std::sync::Arc; use std::time::Duration; diff --git a/src/server.rs b/src/server.rs index bb704198697f6..a7af7ae8b8831 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,5 @@ use crate::metrics::ServerMetrics; -use alloy::primitives::B256; -use alloy_primitives::Bytes; +use alloy_primitives::{Bytes, B256}; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, @@ -16,7 +15,7 @@ use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; use opentelemetry::global::{self, BoxedSpan, BoxedTracer}; use opentelemetry::trace::{Span, TraceContextExt, Tracer}; use opentelemetry::{Context, KeyValue}; -use reth_optimism_payload_builder::{OpPayloadAttributes, OptimismPayloadBuilderAttributes}; +use reth_optimism_payload_builder::{OpPayloadAttributes, OpPayloadBuilderAttributes}; use reth_payload_primitives::PayloadBuilderAttributes; use reth_rpc_layer::AuthClientService; use std::num::NonZero; @@ -230,7 +229,7 @@ where .payload_trace_context .tracer .start_with_context("build-block", &Context::current()); - let builder_attrs = OptimismPayloadBuilderAttributes::try_new( + let builder_attrs = OpPayloadBuilderAttributes::try_new( fork_choice_state.head_block_hash, payload_attributes, 3, @@ -465,7 +464,7 @@ mod tests { use super::*; - use alloy::hex; + use alloy_primitives::hex; use alloy_primitives::{FixedBytes, U256}; use alloy_rpc_types_engine::{ BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, PayloadStatusEnum, From ef551abbd7bc6ecb524cafed1dfa7e588f75ed26 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Thu, 5 Dec 2024 11:36:46 +0000 Subject: [PATCH 027/429] Add defaults for jwt token and l2 url (flashbots/rollup-boost) --- src/main.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index ffe0e4eb4b48c..acc0dd2781d3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,10 +31,14 @@ mod server; #[derive(Parser, Debug)] #[clap(author, version, about)] -#[clap(group(ArgGroup::new("jwt").required(true).multiple(false).args(&["jwt_token", "jwt_path"])))] +#[clap(group(ArgGroup::new("jwt").multiple(false).args(&["jwt_token", "jwt_path"])))] struct Args { /// JWT token for authentication - #[arg(long, env)] + #[arg( + long, + env, + default_value = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a" + )] jwt_token: Option, /// Path to the JWT secret file @@ -50,7 +54,7 @@ struct Args { builder_jwt_path: Option, /// URL of the local l2 execution engine - #[arg(long, env)] + #[arg(long, env, default_value = "http://localhost:8551")] l2_url: String, /// URL of the builder execution engine From e8333dea963ee3c8a989de34bfdb8a87834544c3 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Thu, 5 Dec 2024 12:23:01 +0000 Subject: [PATCH 028/429] Fix (flashbots/rollup-boost) --- src/main.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index acc0dd2781d3f..b305a70983ca1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,14 +31,10 @@ mod server; #[derive(Parser, Debug)] #[clap(author, version, about)] -#[clap(group(ArgGroup::new("jwt").multiple(false).args(&["jwt_token", "jwt_path"])))] +#[clap(group(ArgGroup::new("jwt").required(true).multiple(false).args(&["jwt_token", "jwt_path"])))] struct Args { /// JWT token for authentication - #[arg( - long, - env, - default_value = "688f5d737bad920bdfb2fc2f488d6b6209eebda1dae949a8de91398d932c517a" - )] + #[arg(long, env)] jwt_token: Option, /// Path to the JWT secret file From d9285a09b5141c68b7703fb2d0df5295ced9cce7 Mon Sep 17 00:00:00 2001 From: avalonche Date: Tue, 10 Dec 2024 08:02:14 +1100 Subject: [PATCH 029/429] Add caching to docker build (flashbots/rollup-boost) --- .github/workflows/release.yml | 15 +++++++++++++++ Dockerfile | 6 +++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 98977e728cf6e..15dda34ddd9c2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,21 @@ jobs: type=pep440,pattern={{major}}.{{minor}} type=raw,value=latest,enable=${{ !contains(env.RELEASE_VERSION, '-') }} + # https://github.com/WarpBuilds/rust-cache + - name: Run WarpBuilds/rust-cache + uses: WarpBuilds/rust-cache@v2 + with: + cache-on-failure: true + + # https://github.com/Mozilla-Actions/sccache-action + - name: Setup sccache-action + uses: mozilla-actions/sccache-action@v0.0.5 + + - name: Set env vars + run: | + echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV + echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV + - name: Set up QEMU uses: docker/setup-qemu-action@v2 diff --git a/Dockerfile b/Dockerfile index df0e582651550..c2b4446c1bcca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,19 +9,19 @@ RUN cargo chef prepare # Build application FROM chef AS builder -COPY --from=planner /app/recipe.json . # Install system dependencies RUN apt-get update && \ apt-get install -y openssl libclang-dev libssl3 && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* - + +COPY --from=planner /app/recipe.json . RUN cargo chef cook --release COPY . . RUN cargo build --release -FROM chef AS final +FROM debian:bullseye-slim AS final COPY --from=builder /app/target/release/rollup-boost /usr/local/bin/ ENTRYPOINT ["/usr/local/bin/rollup-boost"] \ No newline at end of file From eb5d9159680db767f70d5499aa26459f8fc25e9a Mon Sep 17 00:00:00 2001 From: avalonche Date: Wed, 11 Dec 2024 11:20:49 +1100 Subject: [PATCH 030/429] Docker build time in CI (flashbots/rollup-boost) --- .github/workflows/release.yml | 33 ++++++---------- Dockerfile | 73 ++++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 39 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 15dda34ddd9c2..3ebd930e8eefd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,7 +12,7 @@ permissions: jobs: docker-image: name: Publish Docker Image - runs-on: warp-ubuntu-latest-x64-16x + runs-on: ubuntu-latest-xlarge steps: - name: Checkout sources @@ -37,36 +37,27 @@ jobs: type=pep440,pattern={{major}}.{{minor}} type=raw,value=latest,enable=${{ !contains(env.RELEASE_VERSION, '-') }} - # https://github.com/WarpBuilds/rust-cache - - name: Run WarpBuilds/rust-cache - uses: WarpBuilds/rust-cache@v2 - with: - cache-on-failure: true - - # https://github.com/Mozilla-Actions/sccache-action - - name: Setup sccache-action - uses: mozilla-actions/sccache-action@v0.0.5 - - - name: Set env vars - run: | - echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV - echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to DockerHub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.FLASHBOTS_DOCKERHUB_USERNAME }} password: ${{ secrets.FLASHBOTS_DOCKERHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: + cache-from: | + type=gha,scope=amd64 + type=gha,scope=arm64 + cache-to: | + type=gha,mode=max,scope=amd64 + type=gha,mode=max,scope=arm64 context: . push: true build-args: | @@ -79,7 +70,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Create release id: create_release diff --git a/Dockerfile b/Dockerfile index c2b4446c1bcca..10b34d29a8ec7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,27 +1,64 @@ -FROM lukemathwalker/cargo-chef:latest AS chef +# +# Base container (with sccache and cargo-chef) +# +# - https://github.com/mozilla/sccache +# - https://github.com/LukeMathWalker/cargo-chef +# +# Based on https://depot.dev/blog/rust-dockerfile-best-practices +# +FROM rust:1.82 as base + +ARG FEATURES + +RUN cargo install sccache --version ^0.8 +RUN cargo install cargo-chef --version ^0.1 + +RUN apt-get update \ + && apt-get install -y clang libclang-dev + +ENV CARGO_HOME=/usr/local/cargo +ENV RUSTC_WRAPPER=sccache +ENV SCCACHE_DIR=/sccache + +# +# Planner container (running "cargo chef prepare") +# +FROM base AS planner WORKDIR /app -# Prepare build plan -FROM chef AS planner -COPY ./Cargo.toml ./Cargo.lock ./ -COPY ./src ./src -RUN cargo chef prepare +COPY . . -# Build application -FROM chef AS builder +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git \ + --mount=type=cache,target=$SCCACHE_DIR,sharing=locked \ + cargo chef prepare --recipe-path recipe.json -# Install system dependencies -RUN apt-get update && \ - apt-get install -y openssl libclang-dev libssl3 && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* +# +# Builder container (running "cargo chef cook" and "cargo build --release") +# +FROM base as builder +WORKDIR /app +# Default binary filename +ARG ROLLUP_BOOST_BIN="rollup-boost" +COPY --from=planner /app/recipe.json recipe.json + +RUN --mount=type=cache,target=$SCCACHE_DIR,sharing=locked \ + cargo chef cook --release --recipe-path recipe.json -COPY --from=planner /app/recipe.json . -RUN cargo chef cook --release COPY . . -RUN cargo build --release -FROM debian:bullseye-slim AS final -COPY --from=builder /app/target/release/rollup-boost /usr/local/bin/ +RUN --mount=type=cache,target=/usr/local/cargo/registry \ + --mount=type=cache,target=/usr/local/cargo/git \ + --mount=type=cache,target=$SCCACHE_DIR,sharing=locked \ + cargo build --release --features="$FEATURES" --package=${ROLLUP_BOOST_BIN} + +# +# Runtime container +# +FROM gcr.io/distroless/cc-debian12 +WORKDIR /app + +ARG ROLLUP_BOOST_BIN="rollup-boost" +COPY --from=builder /app/target/release/${ROLLUP_BOOST_BIN} /usr/local/bin/ ENTRYPOINT ["/usr/local/bin/rollup-boost"] \ No newline at end of file From 5cd7bd07db991374598edf5916f0aa49533c2773 Mon Sep 17 00:00:00 2001 From: avalonche Date: Wed, 11 Dec 2024 16:37:22 +1100 Subject: [PATCH 031/429] testing (flashbots/rollup-boost) --- .github/workflows/release.yml | 39 +++++++++++++++++++++++++---------- Dockerfile | 2 +- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3ebd930e8eefd..5058dc6b20910 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,8 +12,16 @@ permissions: jobs: docker-image: name: Publish Docker Image - runs-on: ubuntu-latest-xlarge - + strategy: + matrix: + config: + - target: x86_64-unknown-linux-gnu + runner: warp-ubuntu-latest-x64-16x + platform: linux/amd64 + - target: aarch64-unknown-linux-gnu + runner: warp-ubuntu-latest-arm64-16x + platform: linux/arm64 + runs-on: ${{ matrix.config.runner }} steps: - name: Checkout sources uses: actions/checkout@v2 @@ -50,21 +58,30 @@ jobs: password: ${{ secrets.FLASHBOTS_DOCKERHUB_TOKEN }} - name: Build and push - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: - cache-from: | - type=gha,scope=amd64 - type=gha,scope=arm64 - cache-to: | - type=gha,mode=max,scope=amd64 - type=gha,mode=max,scope=arm64 + cache-from: type=gha + cache-to: type=gha,mode=max context: . - push: true build-args: | VERSION=${{ env.RELEASE_VERSION }} - platforms: linux/amd64,linux/arm64 + platforms: ${{ matrix.config.platform }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} + outputs: type=image,name=${{ github.repository }},push-by-digest=true,name-canonical=true,push=true + + - name: Export Digest + run: | + digest="${{ steps.build.outputs.digest }}" + [ "$digest" ] || exit 1 + mkdir -p /tmp/digests + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload Digest + uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.config.platform }} + path: /tmp/digests/* github-release: runs-on: ubuntu-latest diff --git a/Dockerfile b/Dockerfile index 10b34d29a8ec7..1da923bd19c43 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ FROM rust:1.82 as base ARG FEATURES -RUN cargo install sccache --version ^0.8 +RUN cargo install sccache --version ^0.9 RUN cargo install cargo-chef --version ^0.1 RUN apt-get update \ From 377ded25c159ba7eaa246368557ad2c1d75a31fe Mon Sep 17 00:00:00 2001 From: avalonche Date: Wed, 11 Dec 2024 20:08:05 +1100 Subject: [PATCH 032/429] merge tags (flashbots/rollup-boost) --- .github/workflows/release.yml | 81 ++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 20 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5058dc6b20910..529a66010de0d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,25 +9,29 @@ on: permissions: contents: write +env: + REGISTRY_IMAGE: flashbots/rollup-boost + jobs: - docker-image: + build: name: Publish Docker Image strategy: matrix: config: - - target: x86_64-unknown-linux-gnu + - platform: linux/amd64 runner: warp-ubuntu-latest-x64-16x - platform: linux/amd64 - - target: aarch64-unknown-linux-gnu + - platform: linux/arm64 runner: warp-ubuntu-latest-arm64-16x - platform: linux/arm64 runs-on: ${{ matrix.config.runner }} steps: - name: Checkout sources uses: actions/checkout@v2 - - name: Get tag version - run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + - name: Set env + run: | + platform=${{ matrix.config.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV - name: Print version run: | @@ -38,12 +42,7 @@ jobs: id: meta uses: docker/metadata-action@v4 with: - images: flashbots/rollup-boost - tags: | - type=sha - type=pep440,pattern={{version}} - type=pep440,pattern={{major}}.{{minor}} - type=raw,value=latest,enable=${{ !contains(env.RELEASE_VERSION, '-') }} + images: ${{ env.REGISTRY_IMAGE }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -66,22 +65,64 @@ jobs: build-args: | VERSION=${{ env.RELEASE_VERSION }} platforms: ${{ matrix.config.platform }} - tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - outputs: type=image,name=${{ github.repository }},push-by-digest=true,name-canonical=true,push=true + outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true - - name: Export Digest + - name: Export digest run: | - digest="${{ steps.build.outputs.digest }}" - [ "$digest" ] || exit 1 mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - - name: Upload Digest + - name: Upload digest uses: actions/upload-artifact@v4 with: - name: digests-${{ matrix.config.platform }} + name: digests-${{ env.PLATFORM_PAIR }} path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + runs-on: ubuntu-latest + needs: + - build + steps: + - name: Download digests + uses: actions/download-artifact@v4 + with: + path: /tmp/digests + pattern: digests-* + merge-multiple: true + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.FLASHBOTS_DOCKERHUB_USERNAME }} + password: ${{ secrets.FLASHBOTS_DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + tags: | + type=sha + type=pep440,pattern={{version}} + type=pep440,pattern={{major}}.{{minor}} + type=raw,value=latest,enable=${{ !contains(env.RELEASE_VERSION, '-') }} + + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} github-release: runs-on: ubuntu-latest From 2b092c0ee7010389deed34fc18f0be886cffb4b3 Mon Sep 17 00:00:00 2001 From: avalonche Date: Wed, 11 Dec 2024 21:30:03 +1100 Subject: [PATCH 033/429] debug (flashbots/rollup-boost) --- .github/workflows/release.yml | 1 + Dockerfile | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 529a66010de0d..28b1d647b9af7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,6 +57,7 @@ jobs: password: ${{ secrets.FLASHBOTS_DOCKERHUB_TOKEN }} - name: Build and push + id: build uses: docker/build-push-action@v6 with: cache-from: type=gha diff --git a/Dockerfile b/Dockerfile index 1da923bd19c43..cf63059a602e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,7 @@ # # Based on https://depot.dev/blog/rust-dockerfile-best-practices # -FROM rust:1.82 as base +FROM rust:1.82 AS base ARG FEATURES @@ -36,7 +36,7 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \ # # Builder container (running "cargo chef cook" and "cargo build --release") # -FROM base as builder +FROM base AS builder WORKDIR /app # Default binary filename ARG ROLLUP_BOOST_BIN="rollup-boost" From aff5557c3eef310a6dc5b3bb83f2ab4edb5c9fb1 Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Wed, 15 Jan 2025 00:40:33 +0530 Subject: [PATCH 034/429] chore: mapping builder (flashbots/rollup-boost) --- src/server.rs | 128 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 38 deletions(-) diff --git a/src/server.rs b/src/server.rs index a7af7ae8b8831..4d10aeb55e9e1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,4 +1,6 @@ -use crate::metrics::ServerMetrics; +use std::num::NonZero; +use std::sync::Arc; + use alloy_primitives::{Bytes, B256}; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, @@ -18,17 +20,18 @@ use opentelemetry::{Context, KeyValue}; use reth_optimism_payload_builder::{OpPayloadAttributes, OpPayloadBuilderAttributes}; use reth_payload_primitives::PayloadBuilderAttributes; use reth_rpc_layer::AuthClientService; -use std::num::NonZero; -use std::sync::Arc; use tokio::sync::Mutex; use tracing::{debug, error, info}; +use crate::metrics::ServerMetrics; + const CACHE_SIZE: usize = 100; struct PayloadTraceContext { tracer: Arc, block_hash_to_payload_ids: Arc>>>, payload_id_to_span: Arc>>>, + local_to_external_payload_ids: Arc>>, } impl PayloadTraceContext { @@ -41,6 +44,9 @@ impl PayloadTraceContext { payload_id_to_span: Arc::new(Mutex::new(LruCache::new( NonZero::new(CACHE_SIZE).unwrap(), ))), + local_to_external_payload_ids: Arc::new(Mutex::new(LruCache::new( + NonZero::new(CACHE_SIZE).unwrap(), + ))), } } @@ -84,6 +90,16 @@ impl PayloadTraceContext { } block_hash_to_payload_ids.pop(block_hash); } + + async fn store_payload_id_mapping(&self, local_id: PayloadId, external_id: PayloadId) { + let mut local_to_external = self.local_to_external_payload_ids.lock().await; + local_to_external.put(local_id, external_id); + } + + async fn get_external_payload_id(&self, local_id: &PayloadId) -> Option { + let mut store = self.local_to_external_payload_ids.lock().await; + store.get(local_id).copied() + } } #[rpc(server, client, namespace = "engine")] @@ -210,6 +226,25 @@ where "has_attributes" = payload_attributes.is_some(), ); + // First get the local payload ID from L2 client + let l2_response = self + .l2_client + .client + .fork_choice_updated_v3(fork_choice_state.clone(), payload_attributes.clone()) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, + other_error => { + error!( + message = "error calling fork_choice_updated_v3 for l2 client", + "url" = self.l2_client.url, + "error" = %other_error, + "head_block_hash" = %fork_choice_state.head_block_hash, + ); + ErrorCode::InternalError.into() + } + })?; + let use_tx_pool = payload_attributes .as_ref() .map(|attr| !attr.no_tx_pool.unwrap_or_default()); @@ -235,18 +270,23 @@ where 3, ) .unwrap(); - let payload_id = builder_attrs.payload_id(); + let local_payload_id = builder_attrs.payload_id(); parent_span.set_attribute(KeyValue::new( "parent_hash", fork_choice_state.head_block_hash.to_string(), )); parent_span .set_attribute(KeyValue::new("timestamp", builder_attrs.timestamp() as i64)); - parent_span.set_attribute(KeyValue::new("payload_id", payload_id.to_string())); + parent_span + .set_attribute(KeyValue::new("payload_id", local_payload_id.to_string())); let ctx = Context::current().with_remote_span_context(parent_span.span_context().clone()); self.payload_trace_context - .store(payload_id, fork_choice_state.head_block_hash, parent_span) + .store( + local_payload_id, + fork_choice_state.head_block_hash, + parent_span, + ) .await; Some( self.payload_trace_context @@ -263,23 +303,41 @@ where } let builder = self.builder_client.clone(); let attr = payload_attributes.clone(); + let payload_trace_context = self.payload_trace_context.clone(); + let local_payload_id = l2_response.payload_id; tokio::spawn(async move { - let _ = builder.client.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { - let payload_id_str = response.payload_id.map(|id| id.to_string()).unwrap_or_default(); - if response.is_invalid() { - error!(message = "builder rejected fork_choice_updated_v3 with attributes", "url" = builder.url, "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); - } else { - info!(message = "called fork_choice_updated_v3 to builder with payload attributes", "url" = builder.url, "payload_status" = %response.payload_status.status, "payload_id" = payload_id_str); + match builder + .client + .fork_choice_updated_v3(fork_choice_state, attr) + .await + { + Ok(response) => { + let external_payload_id = response.payload_id; + if let (Some(local_id), Some(external_id)) = + (local_payload_id, external_payload_id) + { + payload_trace_context + .store_payload_id_mapping(local_id, external_id) + .await; } - }) - .map_err(|e| { + let payload_id_str = external_payload_id + .map(|id| id.to_string()) + .unwrap_or_default(); + if response.is_invalid() { + error!(message = "builder rejected fork_choice_updated_v3 with attributes", "url" = builder.url, "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); + } else { + info!(message = "called fork_choice_updated_v3 to builder with payload attributes", "url" = builder.url, "payload_status" = %response.payload_status.status, "payload_id" = payload_id_str); + } + } + Err(e) => { error!( message = "error calling fork_choice_updated_v3 to builder", - "url" = builder.url, + "url" = builder.url, "error" = %e, "head_block_hash" = %fork_choice_state.head_block_hash ); - }); + } + } if let Some(mut s) = span { s.end() }; @@ -288,22 +346,7 @@ where info!(message = "no payload attributes provided or no_tx_pool is set", "head_block_hash" = %fork_choice_state.head_block_hash); } - self.l2_client - .client - .fork_choice_updated_v3(fork_choice_state, payload_attributes) - .await - .map_err(|e| match e { - ClientError::Call(err) => err, // Already an ErrorObjectOwned, so just return it - other_error => { - error!( - message = "error calling fork_choice_updated_v3 for l2 client", - "url" = self.l2_client.url, - "error" = %other_error, - "head_block_hash" = %fork_choice_state.head_block_hash, - ); - ErrorCode::InternalError.into() - } - }) + Ok(l2_response) } async fn get_payload_v3( @@ -327,14 +370,21 @@ where ) }); + // Get the external builder's payload ID that corresponds to our local payload ID + let external_payload_id = self + .payload_trace_context + .get_external_payload_id(&payload_id) + .await + .unwrap_or(payload_id); // Fallback to local ID if no mapping exists + let builder = self.builder_client.clone(); - let payload = builder.client.get_payload_v3(payload_id).await.map_err(|e| { - error!(message = "error calling get_payload_v3 from builder", "url" = builder.url, "error" = %e, "payload_id" = %payload_id); + let payload = builder.client.get_payload_v3(external_payload_id).await.map_err(|e| { + error!(message = "error calling get_payload_v3 from builder", "url" = builder.url, "error" = %e, "local_payload_id" = %payload_id, "external_payload_id" = %external_payload_id); e })?; let block_hash = ExecutionPayload::from(payload.clone().execution_payload).block_hash(); - info!(message = "received payload from builder", "payload_id" = %payload_id, "block_hash" = %block_hash); + info!(message = "received payload from builder", "local_payload_id" = %payload_id, "external_payload_id" = %external_payload_id, "block_hash" = %block_hash); // Send the payload to the local execution engine with engine_newPayload to validate the block from the builder. // Otherwise, we do not want to risk the network to a halt since op-node will not be able to propose the block. @@ -343,7 +393,7 @@ where metrics.new_payload_count.increment(1); } let payload_status = self.l2_client.client.new_payload_v3(payload.execution_payload.clone(), vec![], payload.parent_beacon_block_root).await.map_err(|e| { - error!(message = "error calling new_payload_v3 to validate builder payload", "url" = self.l2_client.url, "error" = %e, "payload_id" = %payload_id); + error!(message = "error calling new_payload_v3 to validate builder payload", "url" = self.l2_client.url, "error" = %e, "local_payload_id" = %payload_id, "external_payload_id" = %external_payload_id); e })?; if let Some(mut s) = span { @@ -356,14 +406,14 @@ where } }; if payload_status.is_invalid() { - error!(message = "builder payload was not valid", "url" = builder.url, "payload_status" = %payload_status.status, "payload_id" = %payload_id); + error!(message = "builder payload was not valid", "url" = builder.url, "payload_status" = %payload_status.status, "local_payload_id" = %payload_id, "external_payload_id" = %external_payload_id); Err(ClientError::Call(ErrorObject::owned( INVALID_REQUEST_CODE, "Builder payload was not valid", None::, ))) } else { - info!(message = "received payload status from local execution engine validating builder payload", "payload_id" = %payload_id); + info!(message = "received payload status from local execution engine validating builder payload", "local_payload_id" = %payload_id, "external_payload_id" = %external_payload_id); Ok(payload) } }); @@ -604,6 +654,8 @@ mod tests { #[tokio::test] async fn test_server() { + let _ = tracing_subscriber::fmt::try_init(); + engine_success().await; boost_sync_enabled().await; builder_payload_err().await; From c0b5aeed1d1c99e354de3195c3891d6d30263dcf Mon Sep 17 00:00:00 2001 From: avalonche Date: Wed, 22 Jan 2025 06:14:04 +1100 Subject: [PATCH 035/429] Add log format json in args (flashbots/rollup-boost) --- .env.example | 3 ++- src/main.rs | 24 ++++++++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index dd8b0d6bc4cd1..5aa9d3de31c98 100644 --- a/.env.example +++ b/.env.example @@ -7,4 +7,5 @@ RPC_PORT=8081 TRACING=false LOG_LEVEL=info METRICS=false -BOOST_SYNC=false \ No newline at end of file +BOOST_SYNC=false +LOG_FORMAT=text \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index b305a70983ca1..4d59e3c80bfb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -85,6 +85,10 @@ struct Args { #[arg(long, env, default_value = "info")] log_level: Level, + /// Log format + #[arg(long, env, default_value = "text")] + log_format: String, + /// Timeout for the builder client calls in milliseconds #[arg(long, env, default_value = "200")] builder_timeout: u64, @@ -103,10 +107,22 @@ async fn main() -> Result<()> { let args: Args = Args::parse(); // Initialize logging - tracing_subscriber::fmt() - .with_env_filter(EnvFilter::new(args.log_level.to_string())) // Set the log level - .with_ansi(false) // Disable colored logging - .init(); + let log_format = args.log_format.to_lowercase(); + let log_level = args.log_level.to_string(); + if log_format == "json" { + // JSON log format + tracing_subscriber::fmt() + .json() // Use JSON format + .with_env_filter(EnvFilter::new(log_level)) // Set log level + .with_ansi(false) // Disable colored logging + .init(); + } else { + // Default (text) log format + tracing_subscriber::fmt() + .with_env_filter(EnvFilter::new(log_level)) // Set log level + .with_ansi(false) // Disable colored logging + .init(); + } let (metrics, handler) = if args.metrics { let recorder = PrometheusBuilder::new().build_recorder(); From d5505768498e06d1d5dda9d764f0e74602ead185 Mon Sep 17 00:00:00 2001 From: avalonche Date: Wed, 22 Jan 2025 07:09:28 +1100 Subject: [PATCH 036/429] Separate metrics server from rpc server (flashbots/rollup-boost) --- src/main.rs | 69 ++++++++++++++++++++++++++++++++++++++++++++++------ src/proxy.rs | 33 ++++++------------------- 2 files changed, 69 insertions(+), 33 deletions(-) diff --git a/src/main.rs b/src/main.rs index b305a70983ca1..a05efcd848986 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,16 @@ use clap::{arg, ArgGroup, Parser}; use dotenv::dotenv; use error::Error; -use http::Uri; +use http::{StatusCode, Uri}; +use hyper::service::service_fn; +use hyper::{server::conn::http1, Request, Response}; +use hyper_util::rt::TokioIo; use jsonrpsee::http_client::transport::HttpBackend; -use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; +use jsonrpsee::http_client::{HttpBody, HttpClient, HttpClientBuilder}; use jsonrpsee::server::Server; use jsonrpsee::RpcModule; use metrics::ServerMetrics; -use metrics_exporter_prometheus::PrometheusBuilder; +use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle}; use metrics_util::layers::{PrefixLayer, Stack}; use opentelemetry::global; use opentelemetry_otlp::WithExportConfig; @@ -17,9 +20,11 @@ use opentelemetry_sdk::Resource; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use server::{EngineApiServer, EthEngineApi, HttpClientWrapper}; +use std::net::AddrParseError; use std::sync::Arc; use std::time::Duration; use std::{net::SocketAddr, path::PathBuf}; +use tokio::net::TcpListener; use tracing::error; use tracing::{info, Level}; use tracing_subscriber::EnvFilter; @@ -77,6 +82,14 @@ struct Args { #[arg(long, env, default_value = "false")] metrics: bool, + /// Host to run the metrics server on + #[arg(long, env, default_value = "0.0.0.0")] + metrics_host: String, + + /// Port to run the metrics server on + #[arg(long, env, default_value = "9090")] + metrics_port: u16, + /// OTLP endpoint #[arg(long, env, default_value = "http://localhost:4317")] otlp_endpoint: String, @@ -108,7 +121,7 @@ async fn main() -> Result<()> { .with_ansi(false) // Disable colored logging .init(); - let (metrics, handler) = if args.metrics { + let metrics = if args.metrics { let recorder = PrometheusBuilder::new().build_recorder(); let handle = recorder.handle(); @@ -118,9 +131,16 @@ async fn main() -> Result<()> { .install() .map_err(|e| Error::InitMetrics(e.to_string()))?; - (Some(Arc::new(ServerMetrics::default())), Some(handle)) + // Start the metrics server + let metrics_addr = format!("{}:{}", args.metrics_host, args.metrics_port); + let addr: SocketAddr = metrics_addr + .parse() + .map_err(|e: AddrParseError| Error::InitMetrics(e.to_string()))?; + tokio::spawn(init_metrics_server(addr, handle)); // Run the metrics server in a separate task + + Some(Arc::new(ServerMetrics::default())) } else { - (None, None) + None }; // telemetry setup @@ -180,7 +200,6 @@ async fn main() -> Result<()> { args.l2_url .parse::() .map_err(|e| Error::InvalidArgs(e.to_string()))?, - handler, )); let server = Server::builder() .set_http_middleware(service_builder) @@ -237,6 +256,42 @@ fn init_tracing(endpoint: &str) { } } +async fn init_metrics_server(addr: SocketAddr, handle: PrometheusHandle) -> Result<()> { + let listener = TcpListener::bind(addr) + .await + .map_err(|e| Error::InitMetrics(e.to_string()))?; + info!("Metrics server running on {}", addr); + + loop { + match listener.accept().await { + Ok((stream, _)) => { + let handle = handle.clone(); // Clone the handle for each connection + tokio::task::spawn(async move { + let service = service_fn(move |_req: Request| { + let response = match _req.uri().path() { + "/metrics" => Response::new(HttpBody::from(handle.render())), + _ => Response::builder() + .status(StatusCode::NOT_FOUND) + .body(HttpBody::empty()) + .unwrap(), + }; + async { Ok::<_, hyper::Error>(response) } + }); + + let io = TokioIo::new(stream); + + if let Err(err) = http1::Builder::new().serve_connection(io, service).await { + error!(message = "Error serving metrics connection", error = %err); + } + }); + } + Err(e) => { + error!(message = "Error accepting connection", error = %e); + } + } + } +} + #[cfg(test)] mod tests { use assert_cmd::Command; diff --git a/src/proxy.rs b/src/proxy.rs index aa9fb9db15848..350e7fb265eba 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,11 +1,9 @@ -use http::header::CONTENT_TYPE; -use http::{HeaderValue, Uri}; +use http::Uri; use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client; use hyper_util::rt::TokioExecutor; use jsonrpsee::core::{http_helpers, BoxError}; use jsonrpsee::http_client::{HttpBody, HttpRequest, HttpResponse}; -use metrics_exporter_prometheus::PrometheusHandle; use std::task::{Context, Poll}; use std::{future::Future, pin::Pin}; use tower::{Layer, Service}; @@ -16,12 +14,11 @@ const MULTIPLEX_METHODS: [&str; 2] = ["engine_", "eth_sendRawTransaction"]; #[derive(Debug, Clone)] pub struct ProxyLayer { target_url: Uri, - handle: Option, } impl ProxyLayer { - pub fn new(target_url: Uri, handle: Option) -> Self { - ProxyLayer { target_url, handle } + pub fn new(target_url: Uri) -> Self { + ProxyLayer { target_url } } } @@ -33,7 +30,6 @@ impl Layer for ProxyLayer { inner, client: Client::builder(TokioExecutor::new()).build_http(), target_url: self.target_url.clone(), - handle: self.handle.clone(), } } } @@ -43,7 +39,6 @@ pub struct ProxyService { inner: S, client: Client, target_url: Uri, - handle: Option, } impl Service> for ProxyService @@ -63,22 +58,9 @@ where } fn call(&mut self, req: HttpRequest) -> Self::Future { - match req.uri().path() { - "/healthz" => return Box::pin(async { Ok(Self::Response::new(HttpBody::from("OK"))) }), - "/metrics" => { - if let Some(handle) = self.handle.as_ref() { - let metrics = handle.render(); - return Box::pin(async { - let mut response = Self::Response::new(HttpBody::from(metrics)); - response - .headers_mut() - .insert(CONTENT_TYPE, HeaderValue::from_static("text/plain")); - Ok::(response) - }); - } - } - _ => {} - }; + if req.uri().path() == "/healthz" { + return Box::pin(async { Ok(Self::Response::new(HttpBody::from("OK"))) }); + } let target_url = self.target_url.clone(); let client = self.client.clone(); @@ -254,8 +236,7 @@ mod tests { /// Spawn a new RPC server with a proxy layer. async fn spawn_proxy_server() -> ServerHandle { let addr = format!("{ADDR}:{PORT}"); - let proxy_layer = - ProxyLayer::new(format!("http://{ADDR}:{PROXY_PORT}").parse().unwrap(), None); + let proxy_layer = ProxyLayer::new(format!("http://{ADDR}:{PROXY_PORT}").parse().unwrap()); // Create a layered server let server = ServerBuilder::default() .set_http_middleware(tower::ServiceBuilder::new().layer(proxy_layer)) From 72ce65fc7901fdeebd3cc78213f7601ceef3c6c7 Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Thu, 23 Jan 2025 20:52:30 +0530 Subject: [PATCH 037/429] chore: same payload id check (flashbots/rollup-boost) --- src/server.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/server.rs b/src/server.rs index 4d10aeb55e9e1..fe648c5254857 100644 --- a/src/server.rs +++ b/src/server.rs @@ -230,7 +230,7 @@ where let l2_response = self .l2_client .client - .fork_choice_updated_v3(fork_choice_state.clone(), payload_attributes.clone()) + .fork_choice_updated_v3(fork_choice_state, payload_attributes.clone()) .await .map_err(|e| match e { ClientError::Call(err) => err, @@ -270,7 +270,7 @@ where 3, ) .unwrap(); - let local_payload_id = builder_attrs.payload_id(); + let local_payload_id = l2_response.payload_id.expect("local payload_id is None"); parent_span.set_attribute(KeyValue::new( "parent_hash", fork_choice_state.head_block_hash.to_string(), @@ -316,9 +316,12 @@ where if let (Some(local_id), Some(external_id)) = (local_payload_id, external_payload_id) { - payload_trace_context - .store_payload_id_mapping(local_id, external_id) - .await; + // Only store mapping if local and external IDs are different + if local_id != external_id { + payload_trace_context + .store_payload_id_mapping(local_id, external_id) + .await; + } } let payload_id_str = external_payload_id .map(|id| id.to_string()) @@ -371,11 +374,12 @@ where }); // Get the external builder's payload ID that corresponds to our local payload ID + // If no mapping exists, fallback to local ID let external_payload_id = self .payload_trace_context .get_external_payload_id(&payload_id) .await - .unwrap_or(payload_id); // Fallback to local ID if no mapping exists + .unwrap_or(payload_id); let builder = self.builder_client.clone(); let payload = builder.client.get_payload_v3(external_payload_id).await.map_err(|e| { From 62540dc60863f5c7122c80195102e1b9e97fb485 Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 24 Jan 2025 03:33:18 +0530 Subject: [PATCH 038/429] test: same, diff payload ids (flashbots/rollup-boost) --- src/server.rs | 221 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 185 insertions(+), 36 deletions(-) diff --git a/src/server.rs b/src/server.rs index fe648c5254857..bc0fd6511d6b9 100644 --- a/src/server.rs +++ b/src/server.rs @@ -539,6 +539,8 @@ mod tests { fcu_response: RpcResult, get_payload_response: RpcResult, new_payload_response: RpcResult, + + pub override_payload_id: Option, } impl MockEngineServer { @@ -581,8 +583,9 @@ mod tests { should_override_builder: false, parent_beacon_block_root: B256::ZERO, }), - new_payload_response: Ok(PayloadStatus::from_status(PayloadStatusEnum::Valid)), - } + override_payload_id: None, + new_payload_response: Ok(PayloadStatus::from_status(PayloadStatusEnum::Valid)), + } } } @@ -656,6 +659,22 @@ mod tests { } } + /// Waits up to `max_attempts * poll_interval_ms` for `checker` to return true. + /// Returns true if `checker` returned true, otherwise false after exhaustion. + async fn wait_for bool>( + checker: F, + max_attempts: usize, + poll_interval_ms: u64, + ) -> bool { + for _ in 0..max_attempts { + if checker() { + return true; + } + tokio::time::sleep(std::time::Duration::from_millis(poll_interval_ms)).await; + } + false + } + #[tokio::test] async fn test_server() { let _ = tracing_subscriber::fmt::try_init(); @@ -745,48 +764,38 @@ mod tests { test_harness.cleanup().await; } + // #[tokio::test] async fn boost_sync_enabled() { let test_harness = TestHarness::new(true, None, None).await; - // test fork_choice_updated_v3 success let fcu = ForkchoiceState { - head_block_hash: FixedBytes::random(), - safe_block_hash: FixedBytes::random(), - finalized_block_hash: FixedBytes::random(), + head_block_hash: B256::random(), + safe_block_hash: B256::random(), + finalized_block_hash: B256::random(), }; let fcu_response = test_harness.client.fork_choice_updated_v3(fcu, None).await; assert!(fcu_response.is_ok()); - let fcu_requests = test_harness.l2_mock.fcu_requests.clone(); - let fcu_requests_mu = fcu_requests.lock().unwrap(); - let fcu_requests_builder = test_harness.builder_mock.fcu_requests.clone(); - let fcu_requests_builder_mu = fcu_requests_builder.lock().unwrap(); - assert_eq!(fcu_requests_mu.len(), 1); - assert_eq!(fcu_requests_builder_mu.len(), 1); - // test new_payload_v3 success - let new_payload_response = test_harness - .client - .new_payload_v3( - test_harness - .l2_mock - .get_payload_response - .clone() - .unwrap() - .execution_payload - .clone(), - vec![], - B256::ZERO, - ) - .await; - assert!(new_payload_response.is_ok()); - let new_payload_requests = test_harness.l2_mock.new_payload_requests.clone(); - let new_payload_requests_mu = new_payload_requests.lock().unwrap(); - let new_payload_requests_builder = test_harness.builder_mock.new_payload_requests.clone(); - let new_payload_requests_builder_mu = new_payload_requests_builder.lock().unwrap(); - assert_eq!(new_payload_requests_mu.len(), 1); - assert_eq!(new_payload_requests_builder_mu.len(), 1); + // <--- Wait for the background task to call the builder mock + let got_builder_call = wait_for( + || test_harness.builder_mock.fcu_requests.lock().unwrap().len() == 1, + 10, // up to 10 attempts + 50, // 50ms between attempts + ) + .await; + assert!(got_builder_call, "did not observe builder's call in time"); + + // Now the mock server should have recorded the request + let fcu_requests_builder_mu = test_harness.builder_mock.fcu_requests.lock().unwrap(); + assert_eq!( + fcu_requests_builder_mu.len(), + 1, + "builder FCU requests mismatch" + ); - test_harness.cleanup().await; + // L2 engine was called synchronously in the main task, so no race needed: + let fcu_requests_l2_mu = test_harness.l2_mock.fcu_requests.lock().unwrap(); + assert_eq!(fcu_requests_l2_mu.len(), 1, "l2 FCU requests mismatch"); } async fn builder_payload_err() { @@ -817,32 +826,172 @@ mod tests { async fn spawn_server(mock_engine_server: MockEngineServer, addr: &str) -> ServerHandle { let server = ServerBuilder::default().build(addr).await.unwrap(); let mut module: RpcModule<()> = RpcModule::new(()); + module .register_method("engine_forkchoiceUpdatedV3", move |params, _, _| { let params: (ForkchoiceState, Option) = params.parse()?; let mut fcu_requests = mock_engine_server.fcu_requests.lock().unwrap(); fcu_requests.push(params); - mock_engine_server.fcu_response.clone() + + let mut response = mock_engine_server.fcu_response.clone(); + if let Ok(ref mut fcu_response) = response { + if let Some(override_id) = mock_engine_server.override_payload_id { + fcu_response.payload_id = Some(override_id); + } + } + + response }) .unwrap(); + module .register_method("engine_getPayloadV3", move |params, _, _| { let params: (PayloadId,) = params.parse()?; let mut get_payload_requests = mock_engine_server.get_payload_requests.lock().unwrap(); get_payload_requests.push(params.0); + mock_engine_server.get_payload_response.clone() }) .unwrap(); + module .register_method("engine_newPayloadV3", move |params, _, _| { let params: (ExecutionPayloadV3, Vec, B256) = params.parse()?; let mut new_payload_requests = mock_engine_server.new_payload_requests.lock().unwrap(); new_payload_requests.push(params); + mock_engine_server.new_payload_response.clone() }) .unwrap(); + server.start(module) } + + #[tokio::test] + async fn test_local_external_payload_ids_same() { + let same_id = PayloadId::new([0, 0, 0, 0, 0, 0, 0, 42]); + + let mut l2_mock = MockEngineServer::new(); + l2_mock.fcu_response = Ok(ForkchoiceUpdated::new(PayloadStatus::from_status( + PayloadStatusEnum::Valid, + )) + .with_payload_id(same_id)); + + let mut builder_mock = MockEngineServer::new(); + builder_mock.override_payload_id = Some(same_id); + + let test_harness = + TestHarness::new(true, Some(l2_mock.clone()), Some(builder_mock.clone())).await; + + // Test FCU call + let fcu_state = ForkchoiceState { + head_block_hash: FixedBytes::random(), + safe_block_hash: FixedBytes::random(), + finalized_block_hash: FixedBytes::random(), + }; + let fcu_result = test_harness + .client + .fork_choice_updated_v3(fcu_state, None) + .await; + assert!(fcu_result.is_ok()); + + let success = wait_for( + || builder_mock.fcu_requests.lock().unwrap().len() == 1, + 10, + 50, + ) + .await; + assert!(success, "builder FCU call not observed"); + + let builder_fcu_req = builder_mock.fcu_requests.lock().unwrap(); + assert_eq!(builder_fcu_req.len(), 1); + assert_eq!(l2_mock.fcu_requests.lock().unwrap().len(), 1); + + // Test getPayload call + let get_res = test_harness.client.get_payload_v3(same_id).await; + assert!(get_res.is_ok()); + + let got_builder_gp = wait_for( + || builder_mock.get_payload_requests.lock().unwrap().len() == 1, + 10, + 50, + ) + .await; + assert!(got_builder_gp, "builder getPayload call not observed"); + + let builder_gp_reqs = builder_mock.get_payload_requests.lock().unwrap(); + assert_eq!(builder_gp_reqs.len(), 1); + assert_eq!(builder_gp_reqs[0], same_id); + + let local_gp_reqs = l2_mock.get_payload_requests.lock().unwrap(); + assert_eq!(local_gp_reqs.len(), 1); + assert_eq!(local_gp_reqs[0], same_id); + + test_harness.cleanup().await; + } + + #[tokio::test] + async fn test_local_external_payload_ids_different() { + let local_id = PayloadId::new([1, 2, 3, 4, 5, 6, 7, 8]); + let external_id = PayloadId::new([9, 9, 9, 9, 9, 9, 9, 9]); + + let mut l2_mock = MockEngineServer::new(); + let mut fcu_resp = + ForkchoiceUpdated::new(PayloadStatus::from_status(PayloadStatusEnum::Valid)); + fcu_resp.payload_id = Some(local_id); + l2_mock.fcu_response = Ok(fcu_resp); + + let mut builder_mock = MockEngineServer::new(); + builder_mock.override_payload_id = Some(external_id); + + let test_harness = + TestHarness::new(true, Some(l2_mock.clone()), Some(builder_mock.clone())).await; + + // Test FCU call + let fcu_state = ForkchoiceState { + head_block_hash: B256::random(), + safe_block_hash: B256::random(), + finalized_block_hash: B256::random(), + }; + let fcu_result = test_harness + .client + .fork_choice_updated_v3(fcu_state, None) + .await; + assert!(fcu_result.is_ok()); + + let success = wait_for( + || builder_mock.fcu_requests.lock().unwrap().len() == 1, + 10, + 50, + ) + .await; + assert!(success, "builder FCU call not observed"); + + assert_eq!(l2_mock.fcu_requests.lock().unwrap().len(), 1); + assert_eq!(builder_mock.fcu_requests.lock().unwrap().len(), 1); + + // Test getPayload call with local->external mapping + let get_res = test_harness.client.get_payload_v3(local_id).await; + assert!(get_res.is_ok(), "getPayload should succeed"); + + let gp_success = wait_for( + || builder_mock.get_payload_requests.lock().unwrap().len() == 1, + 10, + 50, + ) + .await; + assert!(gp_success, "builder getPayload call not observed"); + + let builder_gp = builder_mock.get_payload_requests.lock().unwrap(); + assert_eq!(builder_gp.len(), 1); + assert_eq!(builder_gp[0], external_id); + + let l2_gp = l2_mock.get_payload_requests.lock().unwrap(); + assert_eq!(l2_gp.len(), 1); + assert_eq!(l2_gp[0], local_id); + + test_harness.cleanup().await; + } } From 6e8b72942adfcdb0935afa29017b012c9ee765b6 Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Thu, 23 Jan 2025 00:47:05 +0530 Subject: [PATCH 039/429] chore: graceful shutdown (flashbots/rollup-boost) --- src/main.rs | 53 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9741efb086f58..943f77572a841 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +use std::{net::SocketAddr, path::PathBuf, sync::Arc, time::Duration}; + use clap::{arg, ArgGroup, Parser}; use dotenv::dotenv; use error::Error; @@ -5,28 +7,24 @@ use http::{StatusCode, Uri}; use hyper::service::service_fn; use hyper::{server::conn::http1, Request, Response}; use hyper_util::rt::TokioIo; -use jsonrpsee::http_client::transport::HttpBackend; -use jsonrpsee::http_client::{HttpBody, HttpClient, HttpClientBuilder}; -use jsonrpsee::server::Server; -use jsonrpsee::RpcModule; +use jsonrpsee::{ + http_client::{transport::HttpBackend, HttpBody, HttpClient, HttpClientBuilder}, + server::Server, + RpcModule, +}; use metrics::ServerMetrics; use metrics_exporter_prometheus::{PrometheusBuilder, PrometheusHandle}; use metrics_util::layers::{PrefixLayer, Stack}; use opentelemetry::global; use opentelemetry_otlp::WithExportConfig; -use opentelemetry_sdk::propagation::TraceContextPropagator; -use opentelemetry_sdk::trace::Config; -use opentelemetry_sdk::Resource; +use opentelemetry_sdk::{propagation::TraceContextPropagator, trace::Config, Resource}; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use server::{EngineApiServer, EthEngineApi, HttpClientWrapper}; use std::net::AddrParseError; -use std::sync::Arc; -use std::time::Duration; -use std::{net::SocketAddr, path::PathBuf}; use tokio::net::TcpListener; -use tracing::error; -use tracing::{info, Level}; +use tokio::signal::unix::{signal as unix_signal, SignalKind}; +use tracing::{error, info, Level}; use tracing_subscriber::EnvFilter; mod error; @@ -159,7 +157,7 @@ async fn main() -> Result<()> { None }; - // telemetry setup + // Telemetry setup if args.tracing { init_tracing(&args.otlp_endpoint); } @@ -199,6 +197,7 @@ async fn main() -> Result<()> { let builder_client = create_client(&args.builder_url, builder_jwt_secret, args.builder_timeout)?; + // Construct the RPC module let eth_engine_api = EthEngineApi::new( Arc::new(l2_client), Arc::new(builder_client), @@ -210,13 +209,14 @@ async fn main() -> Result<()> { .merge(eth_engine_api.into_rpc()) .map_err(|e| Error::InitRPCServer(e.to_string()))?; - // server setup + // Build and start the server info!("Starting server on :{}", args.rpc_port); let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new( args.l2_url .parse::() .map_err(|e| Error::InvalidArgs(e.to_string()))?, )); + let server = Server::builder() .set_http_middleware(service_builder) .build( @@ -226,8 +226,30 @@ async fn main() -> Result<()> { ) .await .map_err(|e| Error::InitRPCServer(e.to_string()))?; + let handle = server.start(module); - handle.stopped().await; + let stop_handle = handle.clone(); + + // Capture SIGINT and SIGTERM + let mut sigint = + unix_signal(SignalKind::interrupt()).map_err(|e| Error::InitRPCServer(e.to_string()))?; + let mut sigterm = + unix_signal(SignalKind::terminate()).map_err(|e| Error::InitRPCServer(e.to_string()))?; + + tokio::select! { + _ = handle.stopped() => { + // The server has already shut down by itself + info!("Server stopped"); + } + _ = sigint.recv() => { + info!("Received SIGINT, shutting down gracefully..."); + let _ = stop_handle.stop(); + } + _ = sigterm.recv() => { + info!("Received SIGTERM, shutting down gracefully..."); + let _ = stop_handle.stop(); + } + } Ok(()) } @@ -246,6 +268,7 @@ fn create_client( .request_timeout(Duration::from_millis(timeout)) .build(url) .map_err(|e| Error::InitRPCClient(e.to_string()))?; + Ok(HttpClientWrapper::new(client, url.to_string())) } From 2bad8dd7c9e14d6c31290004aecbe2518a58dbcf Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 24 Jan 2025 22:23:49 +0530 Subject: [PATCH 040/429] test: fix and working (flashbots/rollup-boost) --- src/server.rs | 129 +++++++++++++++++++------------------------------- 1 file changed, 50 insertions(+), 79 deletions(-) diff --git a/src/server.rs b/src/server.rs index bc0fd6511d6b9..3c4d27b49d860 100644 --- a/src/server.rs +++ b/src/server.rs @@ -526,6 +526,7 @@ mod tests { use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::server::{ServerBuilder, ServerHandle}; use jsonrpsee::RpcModule; + use tokio::time::sleep; const L2_ADDR: &str = "0.0.0.0:8554"; const BUILDER_ADDR: &str = "0.0.0.0:8555"; @@ -659,22 +660,6 @@ mod tests { } } - /// Waits up to `max_attempts * poll_interval_ms` for `checker` to return true. - /// Returns true if `checker` returned true, otherwise false after exhaustion. - async fn wait_for bool>( - checker: F, - max_attempts: usize, - poll_interval_ms: u64, - ) -> bool { - for _ in 0..max_attempts { - if checker() { - return true; - } - tokio::time::sleep(std::time::Duration::from_millis(poll_interval_ms)).await; - } - false - } - #[tokio::test] async fn test_server() { let _ = tracing_subscriber::fmt::try_init(); @@ -764,38 +749,50 @@ mod tests { test_harness.cleanup().await; } - // #[tokio::test] async fn boost_sync_enabled() { let test_harness = TestHarness::new(true, None, None).await; let fcu = ForkchoiceState { - head_block_hash: B256::random(), - safe_block_hash: B256::random(), - finalized_block_hash: B256::random(), + head_block_hash: FixedBytes::random(), + safe_block_hash: FixedBytes::random(), + finalized_block_hash: FixedBytes::random(), }; let fcu_response = test_harness.client.fork_choice_updated_v3(fcu, None).await; assert!(fcu_response.is_ok()); - // <--- Wait for the background task to call the builder mock - let got_builder_call = wait_for( - || test_harness.builder_mock.fcu_requests.lock().unwrap().len() == 1, - 10, // up to 10 attempts - 50, // 50ms between attempts - ) - .await; - assert!(got_builder_call, "did not observe builder's call in time"); - - // Now the mock server should have recorded the request - let fcu_requests_builder_mu = test_harness.builder_mock.fcu_requests.lock().unwrap(); - assert_eq!( - fcu_requests_builder_mu.len(), - 1, - "builder FCU requests mismatch" - ); + sleep(std::time::Duration::from_millis(100)).await; + + let fcu_requests = test_harness.l2_mock.fcu_requests.clone(); + let fcu_requests_mu = fcu_requests.lock().unwrap(); + let fcu_requests_builder = test_harness.builder_mock.fcu_requests.clone(); + let fcu_requests_builder_mu = fcu_requests_builder.lock().unwrap(); + assert_eq!(fcu_requests_mu.len(), 1); + assert_eq!(fcu_requests_builder_mu.len(), 1); - // L2 engine was called synchronously in the main task, so no race needed: - let fcu_requests_l2_mu = test_harness.l2_mock.fcu_requests.lock().unwrap(); - assert_eq!(fcu_requests_l2_mu.len(), 1, "l2 FCU requests mismatch"); + // test new_payload_v3 success + let new_payload_response = test_harness + .client + .new_payload_v3( + test_harness + .l2_mock + .get_payload_response + .clone() + .unwrap() + .execution_payload + .clone(), + vec![], + B256::ZERO, + ) + .await; + assert!(new_payload_response.is_ok()); + let new_payload_requests = test_harness.l2_mock.new_payload_requests.clone(); + let new_payload_requests_mu = new_payload_requests.lock().unwrap(); + let new_payload_requests_builder = test_harness.builder_mock.new_payload_requests.clone(); + let new_payload_requests_builder_mu = new_payload_requests_builder.lock().unwrap(); + assert_eq!(new_payload_requests_mu.len(), 1); + assert_eq!(new_payload_requests_builder_mu.len(), 1); + + test_harness.cleanup().await; } async fn builder_payload_err() { @@ -886,24 +883,16 @@ mod tests { TestHarness::new(true, Some(l2_mock.clone()), Some(builder_mock.clone())).await; // Test FCU call - let fcu_state = ForkchoiceState { + let fcu = ForkchoiceState { head_block_hash: FixedBytes::random(), safe_block_hash: FixedBytes::random(), finalized_block_hash: FixedBytes::random(), }; - let fcu_result = test_harness - .client - .fork_choice_updated_v3(fcu_state, None) - .await; - assert!(fcu_result.is_ok()); + let fcu_response = test_harness.client.fork_choice_updated_v3(fcu, None).await; + assert!(fcu_response.is_ok()); - let success = wait_for( - || builder_mock.fcu_requests.lock().unwrap().len() == 1, - 10, - 50, - ) - .await; - assert!(success, "builder FCU call not observed"); + // wait for builder to observe the FCU call + sleep(std::time::Duration::from_millis(100)).await; let builder_fcu_req = builder_mock.fcu_requests.lock().unwrap(); assert_eq!(builder_fcu_req.len(), 1); @@ -913,13 +902,8 @@ mod tests { let get_res = test_harness.client.get_payload_v3(same_id).await; assert!(get_res.is_ok()); - let got_builder_gp = wait_for( - || builder_mock.get_payload_requests.lock().unwrap().len() == 1, - 10, - 50, - ) - .await; - assert!(got_builder_gp, "builder getPayload call not observed"); + // wait for builder to observe the getPayload call + sleep(std::time::Duration::from_millis(100)).await; let builder_gp_reqs = builder_mock.get_payload_requests.lock().unwrap(); assert_eq!(builder_gp_reqs.len(), 1); @@ -950,24 +934,16 @@ mod tests { TestHarness::new(true, Some(l2_mock.clone()), Some(builder_mock.clone())).await; // Test FCU call - let fcu_state = ForkchoiceState { + let fcu = ForkchoiceState { head_block_hash: B256::random(), safe_block_hash: B256::random(), finalized_block_hash: B256::random(), }; - let fcu_result = test_harness - .client - .fork_choice_updated_v3(fcu_state, None) - .await; - assert!(fcu_result.is_ok()); + let fcu_response = test_harness.client.fork_choice_updated_v3(fcu, None).await; + assert!(fcu_response.is_ok()); - let success = wait_for( - || builder_mock.fcu_requests.lock().unwrap().len() == 1, - 10, - 50, - ) - .await; - assert!(success, "builder FCU call not observed"); + // wait for builder to observe the FCU call + sleep(std::time::Duration::from_millis(100)).await; assert_eq!(l2_mock.fcu_requests.lock().unwrap().len(), 1); assert_eq!(builder_mock.fcu_requests.lock().unwrap().len(), 1); @@ -976,13 +952,8 @@ mod tests { let get_res = test_harness.client.get_payload_v3(local_id).await; assert!(get_res.is_ok(), "getPayload should succeed"); - let gp_success = wait_for( - || builder_mock.get_payload_requests.lock().unwrap().len() == 1, - 10, - 50, - ) - .await; - assert!(gp_success, "builder getPayload call not observed"); + // wait for builder to observe the getPayload call + sleep(std::time::Duration::from_millis(100)).await; let builder_gp = builder_mock.get_payload_requests.lock().unwrap(); assert_eq!(builder_gp.len(), 1); From d1d14d3ed3b634a8a56cfd35a29210e49f7551c3 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Sat, 25 Jan 2025 14:58:57 -0500 Subject: [PATCH 041/429] add miner_ to MULTIPLEX_METHODS (flashbots/rollup-boost) --- src/proxy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proxy.rs b/src/proxy.rs index 350e7fb265eba..5e1e586133b74 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -9,7 +9,7 @@ use std::{future::Future, pin::Pin}; use tower::{Layer, Service}; use tracing::debug; -const MULTIPLEX_METHODS: [&str; 2] = ["engine_", "eth_sendRawTransaction"]; +const MULTIPLEX_METHODS: [&str; 3] = ["engine_", "eth_sendRawTransaction", "miner_"]; #[derive(Debug, Clone)] pub struct ProxyLayer { From 204f6f3171370f972477461b1be88d82f8085c5e Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 11:09:46 -0500 Subject: [PATCH 042/429] add MinerApi trait (flashbots/rollup-boost) --- src/server.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/server.rs b/src/server.rs index a7af7ae8b8831..65751f3c9ac60 100644 --- a/src/server.rs +++ b/src/server.rs @@ -4,6 +4,7 @@ use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, }; +use http::method; use jsonrpsee::core::{async_trait, ClientError, RpcResult}; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::HttpClient; @@ -116,6 +117,21 @@ pub trait EthApi { async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult; } +#[rpc(server, client, namespace = "miner")] +pub trait MinerApi { + #[method(name = "setMaxDASize")] + async fn set_max_da_size(&self, bytes: Bytes) -> RpcResult; + + #[method(name = "setExtra")] + async fn set_extra(&self, bytes: Bytes) -> RpcResult; + + #[method(name = "setGasPrice")] + async fn set_gas_price(&self, bytes: Bytes) -> RpcResult; + + #[method(name = "setGasLimit")] + async fn set_gas_limit(&self, bytes: Bytes) -> RpcResult; +} + pub struct HttpClientWrapper>> { pub client: C, pub url: String, @@ -194,6 +210,8 @@ where } } +// TODO: set this for all the miner methods impl a trait or use one from reth + #[async_trait] impl EngineApiServer for EthEngineApi> where From 15657037fb19bd78aaf26e480c10f00962d0366e Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 11:41:53 -0500 Subject: [PATCH 043/429] forward set max da size to builder and l2 client (flashbots/rollup-boost) --- src/server.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/server.rs b/src/server.rs index 65751f3c9ac60..47d918cfa277c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -4,7 +4,6 @@ use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, }; -use http::method; use jsonrpsee::core::{async_trait, ClientError, RpcResult}; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::HttpClient; @@ -210,7 +209,55 @@ where } } -// TODO: set this for all the miner methods impl a trait or use one from reth +#[async_trait] +impl MinerApiServer for EthEngineApi> +where + C: MinerApiClient + Send + Sync + Clone + 'static, +{ + async fn set_max_da_size(&self, bytes: Bytes) -> RpcResult { + debug!( + message = "received set_max_da_size", + "bytes_len" = bytes.len() + ); + + let builder_client = self.builder_client.client.clone(); + let url = self.builder_client.url.clone(); + let tx_bytes = bytes.clone(); + tokio::spawn(async move { + builder_client.set_max_da_size(tx_bytes).await.map_err(|e| { + error!(message = "error calling set_max_da_size for builder", "url" = url, "error" = %e); + }) + }); + + self.l2_client + .client + .set_max_da_size(bytes) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, + other_error => { + error!( + message = "error calling set_max_da_size for l2 client", + "url" = self.l2_client.url, + "error" = %other_error, + ); + ErrorCode::InternalError.into() + } + }) + } + + async fn set_extra(&self, bytes: Bytes) -> RpcResult { + todo!() + } + + async fn set_gas_price(&self, bytes: Bytes) -> RpcResult { + todo!() + } + + async fn set_gas_limit(&self, bytes: Bytes) -> RpcResult { + todo!() + } +} #[async_trait] impl EngineApiServer for EthEngineApi> From 5c64fe6b187c934eb67426739ea35c4af6c66551 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 12:09:33 -0500 Subject: [PATCH 044/429] impl miner functions (flashbots/rollup-boost) --- src/server.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/src/server.rs b/src/server.rs index 47d918cfa277c..a2052ee15b591 100644 --- a/src/server.rs +++ b/src/server.rs @@ -225,7 +225,7 @@ where let tx_bytes = bytes.clone(); tokio::spawn(async move { builder_client.set_max_da_size(tx_bytes).await.map_err(|e| { - error!(message = "error calling set_max_da_size for builder", "url" = url, "error" = %e); + error!(message = "error calling set_max_da_size for builder", "url" =url, "error" = %e); }) }); @@ -247,15 +247,96 @@ where } async fn set_extra(&self, bytes: Bytes) -> RpcResult { - todo!() + debug!(message = "received set_extra", "bytes_len" = bytes.len()); + + let builder_client = self.builder_client.client.clone(); + let url = self.builder_client.url.clone(); + let tx_bytes = bytes.clone(); + tokio::spawn(async move { + builder_client.set_extra(tx_bytes).await.map_err(|e| { + error!(message = "error calling set_extra for builder", "url" =url, "error" = %e); + }) + }); + + self.l2_client + .client + .set_extra(bytes) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, + other_error => { + error!( + message = "error calling set_extra for l2 client", + "url" = self.l2_client.url, + "error" = %other_error, + ); + ErrorCode::InternalError.into() + } + }) } async fn set_gas_price(&self, bytes: Bytes) -> RpcResult { - todo!() + debug!( + message = "received set_gas_price", + "bytes_len" = bytes.len() + ); + + let builder_client = self.builder_client.client.clone(); + let url = self.builder_client.url.clone(); + let tx_bytes = bytes.clone(); + tokio::spawn(async move { + builder_client.set_gas_price(tx_bytes).await.map_err(|e| { + error!(message = "error calling set_gas_price for builder", "url" =url, "error" = %e); + }) + }); + + self.l2_client + .client + .set_gas_price(bytes) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, + other_error => { + error!( + message = "error calling set_gas_price for l2 client", + "url" = self.l2_client.url, + "error" = %other_error, + ); + ErrorCode::InternalError.into() + } + }) } async fn set_gas_limit(&self, bytes: Bytes) -> RpcResult { - todo!() + debug!( + message = "received set_gas_limit", + "bytes_len" = bytes.len() + ); + + let builder_client = self.builder_client.client.clone(); + let url = self.builder_client.url.clone(); + let tx_bytes = bytes.clone(); + tokio::spawn(async move { + builder_client.set_gas_limit(tx_bytes).await.map_err(|e| { + error!(message = "error calling set_gas_limit for builder", "url" =url, "error" = %e); + }) + }); + + self.l2_client + .client + .set_gas_limit(bytes) + .await + .map_err(|e| match e { + ClientError::Call(err) => err, + other_error => { + error!( + message = "error calling set_gas_limit for l2 client", + "url" = self.l2_client.url, + "error" = %other_error, + ); + ErrorCode::InternalError.into() + } + }) } } From f6a2ce55d5d5ece5ce6bdc224a21745f6a7b1e05 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 12:31:08 -0500 Subject: [PATCH 045/429] update logging (flashbots/rollup-boost) --- src/server.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/server.rs b/src/server.rs index a2052ee15b591..286e4996fd26c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -216,7 +216,7 @@ where { async fn set_max_da_size(&self, bytes: Bytes) -> RpcResult { debug!( - message = "received set_max_da_size", + message = "received miner_setMaxDASize", "bytes_len" = bytes.len() ); @@ -225,7 +225,7 @@ where let tx_bytes = bytes.clone(); tokio::spawn(async move { builder_client.set_max_da_size(tx_bytes).await.map_err(|e| { - error!(message = "error calling set_max_da_size for builder", "url" =url, "error" = %e); + error!(message = "error calling miner_setMaxDASize for builder", "url" =url, "error" = %e); }) }); @@ -237,7 +237,7 @@ where ClientError::Call(err) => err, other_error => { error!( - message = "error calling set_max_da_size for l2 client", + message = "error calling miner_setMaxDASize for l2 client", "url" = self.l2_client.url, "error" = %other_error, ); @@ -247,14 +247,17 @@ where } async fn set_extra(&self, bytes: Bytes) -> RpcResult { - debug!(message = "received set_extra", "bytes_len" = bytes.len()); + debug!( + message = "received miner_setExtra", + "bytes_len" = bytes.len() + ); let builder_client = self.builder_client.client.clone(); let url = self.builder_client.url.clone(); let tx_bytes = bytes.clone(); tokio::spawn(async move { builder_client.set_extra(tx_bytes).await.map_err(|e| { - error!(message = "error calling set_extra for builder", "url" =url, "error" = %e); + error!(message = "error calling miner_setExtra for builder", "url" =url, "error" = %e); }) }); @@ -266,7 +269,7 @@ where ClientError::Call(err) => err, other_error => { error!( - message = "error calling set_extra for l2 client", + message = "error calling miner_setExtra for l2 client", "url" = self.l2_client.url, "error" = %other_error, ); @@ -277,7 +280,7 @@ where async fn set_gas_price(&self, bytes: Bytes) -> RpcResult { debug!( - message = "received set_gas_price", + message = "received miner_setGasPrice", "bytes_len" = bytes.len() ); @@ -286,7 +289,7 @@ where let tx_bytes = bytes.clone(); tokio::spawn(async move { builder_client.set_gas_price(tx_bytes).await.map_err(|e| { - error!(message = "error calling set_gas_price for builder", "url" =url, "error" = %e); + error!(message = "error calling miner_setGasPrice for builder", "url" =url, "error" = %e); }) }); @@ -298,7 +301,7 @@ where ClientError::Call(err) => err, other_error => { error!( - message = "error calling set_gas_price for l2 client", + message = "error calling miner_setGasPrice for l2 client", "url" = self.l2_client.url, "error" = %other_error, ); From 674ced7a2beab6261cc15c50edc7e7eddbf4e394 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 12:37:31 -0500 Subject: [PATCH 046/429] fix log message (flashbots/rollup-boost) --- src/server.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/server.rs b/src/server.rs index 286e4996fd26c..207a96f37cf72 100644 --- a/src/server.rs +++ b/src/server.rs @@ -312,7 +312,7 @@ where async fn set_gas_limit(&self, bytes: Bytes) -> RpcResult { debug!( - message = "received set_gas_limit", + message = "received miner_setGasLimit", "bytes_len" = bytes.len() ); @@ -321,7 +321,7 @@ where let tx_bytes = bytes.clone(); tokio::spawn(async move { builder_client.set_gas_limit(tx_bytes).await.map_err(|e| { - error!(message = "error calling set_gas_limit for builder", "url" =url, "error" = %e); + error!(message = "error calling miner_setGasLimit for builder", "url" =url, "error" = %e); }) }); @@ -333,7 +333,7 @@ where ClientError::Call(err) => err, other_error => { error!( - message = "error calling set_gas_limit for l2 client", + message = "error calling miner_setGasLimit for l2 client", "url" = self.l2_client.url, "error" = %other_error, ); @@ -938,4 +938,7 @@ mod tests { .unwrap(); server.start(module) } + + #[test] + fn test_set_extra() {} } From c3d2ed746b1f58c71993c854f4ef3acd20283921 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 12:53:12 -0500 Subject: [PATCH 047/429] remove unused test (flashbots/rollup-boost) --- src/server.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/server.rs b/src/server.rs index 207a96f37cf72..d5b4763d030d9 100644 --- a/src/server.rs +++ b/src/server.rs @@ -938,7 +938,4 @@ mod tests { .unwrap(); server.start(module) } - - #[test] - fn test_set_extra() {} } From 8a4b0dde78475758c5d51f7437a4da5ae2f83bde Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 15:50:11 -0500 Subject: [PATCH 048/429] use MinerApi from reth (flashbots/rollup-boost) --- Cargo.lock | 1946 +++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 19 +- src/server.rs | 142 ++-- 3 files changed, 1961 insertions(+), 146 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a5ca809bcf33..c1e6795bdf405 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -17,6 +17,41 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.8.11" @@ -54,6 +89,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "alloy-chains" version = "0.1.43" @@ -96,6 +137,26 @@ dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rlp", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-dyn-abi" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80759b3f57b3b20fa7cd8fef6479930fc95461b58ff8adea6e87e618449c8a1d" +dependencies = [ + "alloy-json-abi", + "alloy-primitives", + "alloy-sol-type-parser", + "alloy-sol-types", + "const-hex", + "derive_more", + "itoa", + "serde", + "serde_json", + "winnow", ] [[package]] @@ -144,7 +205,7 @@ dependencies = [ "ethereum_ssz_derive", "once_cell", "serde", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -159,6 +220,57 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-json-abi" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac4b22b3e51cac09fd2adfcc73b55f447b4df669f983c13f7894ec82b607c63f" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-json-rpc" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fa077efe0b834bcd89ff4ba547f48fb081e4fdc3673dd7da1b295a2cf2bb7b7" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "serde", + "serde_json", + "thiserror 2.0.4", + "tracing", +] + +[[package]] +name = "alloy-network" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209a1882a08e21aca4aac6e2a674dc6fcf614058ef8cb02947d63782b1899552" +dependencies = [ + "alloy-consensus", + "alloy-consensus-any", + "alloy-eips", + "alloy-json-rpc", + "alloy-network-primitives", + "alloy-primitives", + "alloy-rpc-types-any", + "alloy-rpc-types-eth", + "alloy-serde", + "alloy-signer", + "alloy-sol-types", + "async-trait", + "auto_impl", + "futures-utils-wasm", + "serde", + "serde_json", + "thiserror 2.0.4", +] + [[package]] name = "alloy-network-primitives" version = "0.7.3" @@ -239,6 +351,56 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-rpc-types-admin" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f0874a976ccdf83a178ad93b64bec5b8c91a47428d714d544ca70258acfa07b" +dependencies = [ + "alloy-genesis", + "alloy-primitives", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-rpc-types-anvil" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d33bc190844626c08e21897736dbd7956ab323c09e6f141b118d1c8b7aff689e" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + +[[package]] +name = "alloy-rpc-types-any" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200661999b6e235d9840be5d60a6e8ae2f0af9eb2a256dd378786744660e36ec" +dependencies = [ + "alloy-consensus-any", + "alloy-rpc-types-eth", + "alloy-serde", +] + +[[package]] +name = "alloy-rpc-types-beacon" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc37861dc8cbf5da35d346139fbe6e03ee7823cc21138a2c4a590d3b0b4b24be" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "alloy-serde", + "serde", + "serde_with", + "thiserror 2.0.4", +] + [[package]] name = "alloy-rpc-types-debug" version = "0.7.3" @@ -289,6 +451,46 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-rpc-types-mev" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093d618d5a42808e7ae26062f415a1e816fc27d3d32662c6ed52d0871b154894" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", + "serde_json", +] + +[[package]] +name = "alloy-rpc-types-trace" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e073ab0e67429c60be281e181731132fd07d82e091c10c29ace6935101034bb" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", + "serde_json", + "thiserror 2.0.4", +] + +[[package]] +name = "alloy-rpc-types-txpool" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7435f6bfb93912f16d64bb61f4278fa698469e054784f477337ef87ec0b2527b" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", +] + [[package]] name = "alloy-serde" version = "0.7.3" @@ -301,6 +503,20 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-signer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2cbff01a673936c2efd7e00d4c0e9a4dbbd6d600e2ce298078d33efbb19cd7" +dependencies = [ + "alloy-primitives", + "async-trait", + "auto_impl", + "elliptic-curve", + "k256", + "thiserror 2.0.4", +] + [[package]] name = "alloy-sol-macro" version = "0.8.14" @@ -348,15 +564,27 @@ dependencies = [ "syn-solidity", ] +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e60b084fe1aef8acecda2743ff2d93c18ff3eb67a2d3b12f62582a1e66ef5e" +dependencies = [ + "serde", + "winnow", +] + [[package]] name = "alloy-sol-types" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9dc0fffe397aa17628160e16b89f704098bf3c9d74d5d369ebc239575936de5" dependencies = [ + "alloy-json-abi", "alloy-primitives", "alloy-sol-macro", "const-hex", + "serde", ] [[package]] @@ -592,6 +820,12 @@ dependencies = [ "rand", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -601,6 +835,12 @@ dependencies = [ "serde", ] +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + [[package]] name = "assert_cmd" version = "2.0.16" @@ -788,12 +1028,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -905,6 +1157,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -914,6 +1175,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "blst" version = "0.3.13" @@ -947,6 +1217,15 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bstr" version = "1.10.0" @@ -1006,6 +1285,38 @@ dependencies = [ "serde", ] +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.23", + "serde", + "serde_json", + "thiserror 1.0.65", +] + [[package]] name = "cc" version = "1.1.31" @@ -1053,6 +1364,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -1129,6 +1450,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "concat-kdf" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d72c1252426a83be2092dd5884a5f6e3b8e7180f6891b6263d2c21b92ec8816" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "const-hex" version = "1.14.0" @@ -1149,19 +1479,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] -name = "convert_case" -version = "0.6.0" +name = "const_format" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" dependencies = [ - "unicode-segmentation", + "const_format_proc_macros", + "konst", ] [[package]] -name = "core-foundation" -version = "0.9.4" +name = "const_format_proc_macros" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -1173,6 +1524,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.14" @@ -1271,9 +1631,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "darling" version = "0.20.10" @@ -1321,6 +1728,44 @@ dependencies = [ "lock_api", "once_cell", "parking_lot_core", + "serde", +] + +[[package]] +name = "data-encoding" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" + +[[package]] +name = "data-encoding-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b16d9d0d88a5273d830dac8b78ceb217ffc9b1d5404e5597a3542515329405b" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145d32e826a7748b69ee8fc62d3e6355ff7f1051df53141e7048162fc90481b" +dependencies = [ + "data-encoding", + "syn 2.0.90", +] + +[[package]] +name = "delay_map" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df941644b671f05f59433e481ba0d31ac10e3667de725236a4c0d587c496fba1" +dependencies = [ + "futures", + "tokio", + "tokio-util", ] [[package]] @@ -1408,12 +1853,87 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "discv5" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e6b70634e26c909d1edbb3142b3eaf3b89da0e52f284f00ca7c80d9901ad9e" +dependencies = [ + "aes", + "aes-gcm", + "alloy-rlp", + "arrayvec", + "ctr", + "delay_map", + "enr", + "fnv", + "futures", + "hashlink", + "hex", + "hkdf", + "lazy_static", + "libp2p-identity", + "lru 0.12.5", + "more-asserts", + "multiaddr", + "parking_lot", + "rand", + "smallvec", + "socket2", + "tokio", + "tracing", + "uint 0.10.0", + "zeroize", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -1452,6 +1972,31 @@ dependencies = [ "spki", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.13.0" @@ -1501,13 +2046,29 @@ dependencies = [ "alloy-rlp", "base64 0.21.7", "bytes", + "ed25519-dalek", "hex", + "k256", "log", "rand", + "secp256k1", + "serde", "sha3", "zeroize", ] +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "enumn" version = "0.1.14" @@ -1613,6 +2174,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "filetime" version = "0.2.25" @@ -1784,6 +2351,16 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +dependencies = [ + "gloo-timers", + "send_wrapper", +] + [[package]] name = "futures-util" version = "0.3.31" @@ -1802,12 +2379,19 @@ dependencies = [ "slab", ] +[[package]] +name = "futures-utils-wasm" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" + [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ + "serde", "typenum", "version_check", "zeroize", @@ -1826,6 +2410,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.31.1" @@ -1838,6 +2432,52 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http 1.1.0", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror 1.0.65", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "group" version = "0.13.0" @@ -1888,6 +2528,9 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] [[package]] name = "hashbrown" @@ -1895,10 +2538,21 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ + "allocator-api2", + "equivalent", "foldhash", "serde", ] +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + [[package]] name = "heck" version = "0.5.0" @@ -1926,6 +2580,25 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1935,6 +2608,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] + [[package]] name = "home" version = "0.5.9" @@ -1945,8 +2629,19 @@ dependencies = [ ] [[package]] -name = "http" -version = "0.2.12" +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ @@ -2018,6 +2713,22 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" version = "1.5.0" @@ -2135,6 +2846,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -2145,6 +2866,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "if-addrs" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78a89907582615b19f6f0da1af18abf6ff08be259395669b834b057a7ee92d8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -2233,6 +2964,28 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + [[package]] name = "ipnet" version = "2.10.1" @@ -2332,13 +3085,41 @@ version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" dependencies = [ + "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-http-client", "jsonrpsee-proc-macros", "jsonrpsee-server", "jsonrpsee-types", + "jsonrpsee-wasm-client", + "jsonrpsee-ws-client", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548125b159ba1314104f5bb5f38519e03a41862786aa3925cf349aae9cdd546e" +dependencies = [ + "base64 0.22.1", + "futures-channel", + "futures-util", + "gloo-net", + "http 1.1.0", + "jsonrpsee-core", + "pin-project", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "soketto", + "thiserror 1.0.65", "tokio", + "tokio-rustls", + "tokio-util", "tracing", + "url", ] [[package]] @@ -2349,19 +3130,23 @@ checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" dependencies = [ "async-trait", "bytes", + "futures-timer", "futures-util", "http 1.1.0", "http-body 1.0.1", "http-body-util", "jsonrpsee-types", "parking_lot", + "pin-project", "rand", "rustc-hash 2.0.0", "serde", "serde_json", "thiserror 1.0.65", "tokio", + "tokio-stream", "tracing", + "wasm-bindgen-futures", ] [[package]] @@ -2441,6 +3226,30 @@ dependencies = [ "thiserror 1.0.65", ] +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01cd500915d24ab28ca17527e23901ef1be6d659a2322451e1045532516c25" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe322e0896d0955a3ebdd5bf813571c53fea29edd713bc315b76620b327e86d" +dependencies = [ + "http 1.1.0", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", + "url", +] + [[package]] name = "jsonwebtoken" version = "9.3.0" @@ -2466,7 +3275,8 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2", + "sha2 0.10.8", + "signature", ] [[package]] @@ -2488,6 +3298,21 @@ dependencies = [ "sha3-asm", ] +[[package]] +name = "konst" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330f0e13e6483b8c34885f7e6c9f19b1a7bd449c673fbb948a51c99d66ef74f4" +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 = "kqueue" version = "1.0.8" @@ -2545,6 +3370,25 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +[[package]] +name = "libp2p-identity" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b5621d159b32282eac446bed6670c39c7dc68a200a992d8f056afa0066f6d" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "hkdf", + "libsecp256k1", + "multihash", + "quick-protobuf", + "sha2 0.10.8", + "thiserror 1.0.65", + "tracing", + "zeroize", +] + [[package]] name = "libproc" version = "0.14.10" @@ -2567,6 +3411,70 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linked_hash_set" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae85b5be22d9843c80e5fc80e9b64c8a3b1f98f867c709956eca3efff4e92e2" +dependencies = [ + "linked-hash-map", + "serde", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -2599,6 +3507,24 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.0", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "lz4_flex" version = "0.11.3" @@ -2614,6 +3540,12 @@ dependencies = [ "libc", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -2803,6 +3735,52 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "more-asserts" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" + +[[package]] +name = "multiaddr" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" +dependencies = [ + "core2", + "unsigned-varint", +] + [[package]] name = "native-tls" version = "0.2.12" @@ -2859,6 +3837,7 @@ dependencies = [ "libc", "log", "mio 0.8.11", + "serde", "walkdir", "windows-sys 0.48.0", ] @@ -2992,6 +3971,15 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "nybbles" version = "0.2.1" @@ -3080,6 +4068,16 @@ dependencies = [ "unsigned-varint", ] +[[package]] +name = "op-alloy-rpc-jsonrpsee" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446c38f9e3e86acac6f2972a4953ffdd8bfef032bec633b0280a95783c0babd" +dependencies = [ + "alloy-primitives", + "jsonrpsee", +] + [[package]] name = "op-alloy-rpc-types" version = "0.7.3" @@ -3118,6 +4116,12 @@ dependencies = [ "thiserror 2.0.4", ] +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "openssl" version = "0.10.68" @@ -3245,6 +4249,12 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-float" version = "4.4.0" @@ -3269,7 +4279,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -3413,6 +4423,18 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.9.0" @@ -3491,7 +4513,7 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", - "uint", + "uint 0.9.5", ] [[package]] @@ -3631,6 +4653,15 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + [[package]] name = "quote" version = "1.0.37" @@ -3734,6 +4765,17 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror 1.0.65", +] + [[package]] name = "regex" version = "1.11.1" @@ -3822,6 +4864,16 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + [[package]] name = "reth-basic-payload-builder" version = "1.1.2" @@ -3852,14 +4904,54 @@ dependencies = [ ] [[package]] -name = "reth-blockchain-tree-api" +name = "reth-beacon-consensus" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "alloy-consensus", "alloy-eips", "alloy-primitives", - "reth-consensus", - "reth-execution-errors", + "alloy-rpc-types-engine", + "futures", + "itertools 0.13.0", + "metrics", + "reth-blockchain-tree-api", + "reth-codecs", + "reth-db-api", + "reth-engine-primitives", + "reth-errors", + "reth-ethereum-consensus", + "reth-metrics", + "reth-network-p2p", + "reth-node-types", + "reth-payload-builder", + "reth-payload-builder-primitives", + "reth-payload-primitives", + "reth-payload-validator", + "reth-primitives", + "reth-primitives-traits", + "reth-provider", + "reth-prune", + "reth-stages-api", + "reth-static-file", + "reth-tasks", + "reth-tokio-util", + "schnellru", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-blockchain-tree-api" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "reth-consensus", + "reth-execution-errors", "reth-primitives", "reth-storage-errors", "thiserror 2.0.4", @@ -3911,6 +5003,23 @@ dependencies = [ "serde_json", ] +[[package]] +name = "reth-cli-util" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "cfg-if", + "eyre", + "libc", + "rand", + "reth-fs-util", + "secp256k1", + "serde", + "thiserror 2.0.4", +] + [[package]] name = "reth-codecs" version = "1.1.2" @@ -3939,6 +5048,20 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "reth-config" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "eyre", + "humantime-serde", + "reth-network-types", + "reth-prune-types", + "reth-stages-types", + "serde", + "toml", +] + [[package]] name = "reth-consensus" version = "1.1.2" @@ -4038,6 +5161,111 @@ dependencies = [ "serde", ] +[[package]] +name = "reth-discv4" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "discv5", + "enr", + "generic-array", + "itertools 0.13.0", + "parking_lot", + "rand", + "reth-ethereum-forks", + "reth-net-banlist", + "reth-net-nat", + "reth-network-peers", + "schnellru", + "secp256k1", + "serde", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-discv5" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more", + "discv5", + "enr", + "futures", + "itertools 0.13.0", + "metrics", + "rand", + "reth-chainspec", + "reth-ethereum-forks", + "reth-metrics", + "reth-network-peers", + "secp256k1", + "thiserror 2.0.4", + "tokio", + "tracing", +] + +[[package]] +name = "reth-dns-discovery" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "data-encoding", + "enr", + "linked_hash_set", + "parking_lot", + "reth-ethereum-forks", + "reth-network-peers", + "reth-tokio-util", + "schnellru", + "secp256k1", + "serde", + "serde_with", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tracing", + "trust-dns-resolver", +] + +[[package]] +name = "reth-ecies" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "aes", + "alloy-primitives", + "alloy-rlp", + "block-padding", + "byteorder", + "cipher", + "concat-kdf", + "ctr", + "digest 0.10.7", + "futures", + "generic-array", + "hmac 0.12.1", + "pin-project", + "rand", + "reth-network-peers", + "secp256k1", + "sha2 0.10.8", + "sha3", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", + "typenum", +] + [[package]] name = "reth-engine-primitives" version = "1.1.2" @@ -4072,6 +5300,34 @@ dependencies = [ "thiserror 2.0.4", ] +[[package]] +name = "reth-eth-wire" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-chains", + "alloy-primitives", + "alloy-rlp", + "bytes", + "derive_more", + "futures", + "pin-project", + "reth-codecs", + "reth-ecies", + "reth-eth-wire-types", + "reth-ethereum-forks", + "reth-metrics", + "reth-network-peers", + "reth-primitives-traits", + "serde", + "snap", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + [[package]] name = "reth-eth-wire-types" version = "1.1.2" @@ -4093,6 +5349,22 @@ dependencies = [ "thiserror 2.0.4", ] +[[package]] +name = "reth-ethereum-consensus" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "reth-chainspec", + "reth-consensus", + "reth-consensus-common", + "reth-primitives", + "reth-primitives-traits", + "tracing", +] + [[package]] name = "reth-ethereum-engine-primitives" version = "1.1.2" @@ -4110,7 +5382,7 @@ dependencies = [ "reth-primitives", "reth-rpc-types-compat", "serde", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -4190,6 +5462,18 @@ dependencies = [ "serde_with", ] +[[package]] +name = "reth-exex-types" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "reth-chain-state", + "reth-execution-types", + "reth-primitives-traits", +] + [[package]] name = "reth-fs-util" version = "1.1.2" @@ -4231,8 +5515,11 @@ name = "reth-metrics" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "futures", "metrics", "metrics-derive", + "tokio", + "tokio-util", ] [[package]] @@ -4243,6 +5530,98 @@ dependencies = [ "alloy-primitives", ] +[[package]] +name = "reth-net-nat" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "futures-util", + "if-addrs", + "reqwest", + "serde_with", + "thiserror 2.0.4", + "tokio", + "tracing", +] + +[[package]] +name = "reth-network" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "aquamarine", + "auto_impl", + "derive_more", + "discv5", + "enr", + "futures", + "itertools 0.13.0", + "metrics", + "parking_lot", + "pin-project", + "rand", + "reth-chainspec", + "reth-consensus", + "reth-discv4", + "reth-discv5", + "reth-dns-discovery", + "reth-ecies", + "reth-eth-wire", + "reth-eth-wire-types", + "reth-ethereum-forks", + "reth-fs-util", + "reth-metrics", + "reth-net-banlist", + "reth-network-api", + "reth-network-p2p", + "reth-network-peers", + "reth-network-types", + "reth-primitives", + "reth-primitives-traits", + "reth-provider", + "reth-storage-api", + "reth-tasks", + "reth-tokio-util", + "reth-transaction-pool", + "rustc-hash 2.0.0", + "schnellru", + "secp256k1", + "serde", + "smallvec", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + +[[package]] +name = "reth-network-api" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-admin", + "auto_impl", + "derive_more", + "enr", + "futures", + "reth-eth-wire-types", + "reth-ethereum-forks", + "reth-network-p2p", + "reth-network-peers", + "reth-network-types", + "reth-tokio-util", + "serde", + "thiserror 2.0.4", + "tokio", + "tokio-stream", +] + [[package]] name = "reth-network-p2p" version = "1.1.2" @@ -4276,6 +5655,7 @@ dependencies = [ "secp256k1", "serde_with", "thiserror 2.0.4", + "tokio", "url", ] @@ -4284,9 +5664,11 @@ name = "reth-network-types" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "humantime-serde", "reth-ethereum-forks", "reth-net-banlist", "reth-network-peers", + "serde", "serde_json", "tracing", ] @@ -4308,6 +5690,77 @@ dependencies = [ "zstd", ] +[[package]] +name = "reth-node-api" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-rpc-types-engine", + "eyre", + "reth-beacon-consensus", + "reth-consensus", + "reth-engine-primitives", + "reth-evm", + "reth-network-api", + "reth-node-core", + "reth-node-types", + "reth-payload-builder-primitives", + "reth-payload-primitives", + "reth-provider", + "reth-tasks", + "reth-transaction-pool", +] + +[[package]] +name = "reth-node-core" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "clap", + "const_format", + "derive_more", + "dirs-next", + "eyre", + "futures", + "humantime", + "rand", + "reth-chainspec", + "reth-cli-util", + "reth-config", + "reth-consensus", + "reth-db", + "reth-discv4", + "reth-discv5", + "reth-ethereum-forks", + "reth-net-nat", + "reth-network", + "reth-network-p2p", + "reth-network-peers", + "reth-primitives", + "reth-primitives-traits", + "reth-prune-types", + "reth-rpc-eth-types", + "reth-rpc-server-types", + "reth-rpc-types-compat", + "reth-stages-types", + "reth-storage-api", + "reth-storage-errors", + "reth-tracing", + "reth-transaction-pool", + "secp256k1", + "serde", + "shellexpand", + "strum", + "thiserror 2.0.4", + "toml", + "tracing", + "vergen", +] + [[package]] name = "reth-node-types" version = "1.1.2" @@ -4433,7 +5886,7 @@ dependencies = [ "reth-rpc-types-compat", "reth-transaction-pool", "revm", - "sha2", + "sha2 0.10.8", "thiserror 2.0.4", "tracing", ] @@ -4617,47 +6070,187 @@ dependencies = [ "reth-optimism-primitives", "reth-primitives", "reth-primitives-traits", - "reth-prune-types", - "reth-stages-types", - "reth-storage-api", - "reth-storage-errors", - "reth-trie", - "reth-trie-db", + "reth-prune-types", + "reth-stages-types", + "reth-storage-api", + "reth-storage-errors", + "reth-trie", + "reth-trie-db", + "revm", + "strum", + "tokio", + "tracing", +] + +[[package]] +name = "reth-prune" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "itertools 0.13.0", + "metrics", + "rayon", + "reth-chainspec", + "reth-config", + "reth-db", + "reth-db-api", + "reth-errors", + "reth-exex-types", + "reth-metrics", + "reth-primitives-traits", + "reth-provider", + "reth-prune-types", + "reth-static-file-types", + "reth-tokio-util", + "rustc-hash 2.0.0", + "thiserror 2.0.4", + "tokio", + "tracing", +] + +[[package]] +name = "reth-prune-types" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "bytes", + "derive_more", + "modular-bitfield", + "reth-codecs", + "serde", + "thiserror 2.0.4", +] + +[[package]] +name = "reth-revm" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "reth-execution-errors", + "reth-primitives", + "reth-primitives-traits", + "reth-prune-types", + "reth-storage-api", + "reth-storage-errors", + "reth-trie", + "revm", +] + +[[package]] +name = "reth-rpc-api" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-eips", + "alloy-json-rpc", + "alloy-primitives", + "alloy-rpc-types", + "alloy-rpc-types-admin", + "alloy-rpc-types-anvil", + "alloy-rpc-types-beacon", + "alloy-rpc-types-debug", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "alloy-rpc-types-mev", + "alloy-rpc-types-trace", + "alloy-rpc-types-txpool", + "alloy-serde", + "jsonrpsee", + "reth-engine-primitives", + "reth-network-peers", + "reth-rpc-eth-api", +] + +[[package]] +name = "reth-rpc-eth-api" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-consensus", + "alloy-dyn-abi", + "alloy-eips", + "alloy-json-rpc", + "alloy-network", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-rpc-types-mev", + "alloy-serde", + "async-trait", + "auto_impl", + "dyn-clone", + "futures", + "jsonrpsee", + "jsonrpsee-types", + "parking_lot", + "reth-chainspec", + "reth-errors", + "reth-evm", + "reth-execution-types", + "reth-network-api", + "reth-node-api", + "reth-primitives", + "reth-primitives-traits", + "reth-provider", + "reth-revm", + "reth-rpc-eth-types", + "reth-rpc-server-types", + "reth-rpc-types-compat", + "reth-tasks", + "reth-transaction-pool", + "reth-trie-common", "revm", - "strum", + "revm-inspectors", + "revm-primitives", "tokio", "tracing", ] [[package]] -name = "reth-prune-types" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", - "bytes", - "derive_more", - "modular-bitfield", - "reth-codecs", - "serde", - "thiserror 2.0.4", -] - -[[package]] -name = "reth-revm" +name = "reth-rpc-eth-types" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "alloy-consensus", "alloy-eips", "alloy-primitives", - "reth-execution-errors", + "alloy-rpc-types-eth", + "alloy-sol-types", + "derive_more", + "futures", + "itertools 0.13.0", + "jsonrpsee-core", + "jsonrpsee-types", + "metrics", + "rand", + "reth-chain-state", + "reth-chainspec", + "reth-errors", + "reth-execution-types", + "reth-metrics", "reth-primitives", "reth-primitives-traits", - "reth-prune-types", + "reth-revm", + "reth-rpc-server-types", + "reth-rpc-types-compat", "reth-storage-api", - "reth-storage-errors", + "reth-tasks", + "reth-transaction-pool", "reth-trie", "revm", + "revm-inspectors", + "revm-primitives", + "schnellru", + "serde", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tracing", ] [[package]] @@ -4674,6 +6267,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "reth-rpc-server-types" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "jsonrpsee-core", + "jsonrpsee-types", + "reth-errors", + "reth-network-api", + "serde", + "strum", +] + [[package]] name = "reth-rpc-types-compat" version = "1.1.2" @@ -4690,6 +6299,32 @@ dependencies = [ "serde", ] +[[package]] +name = "reth-stages-api" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "aquamarine", + "auto_impl", + "futures-util", + "metrics", + "reth-consensus", + "reth-errors", + "reth-metrics", + "reth-network-p2p", + "reth-primitives-traits", + "reth-provider", + "reth-prune", + "reth-stages-types", + "reth-static-file", + "reth-static-file-types", + "reth-tokio-util", + "thiserror 2.0.4", + "tokio", + "tracing", +] + [[package]] name = "reth-stages-types" version = "1.1.2" @@ -4703,6 +6338,27 @@ dependencies = [ "serde", ] +[[package]] +name = "reth-static-file" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "parking_lot", + "rayon", + "reth-codecs", + "reth-db", + "reth-db-api", + "reth-primitives-traits", + "reth-provider", + "reth-prune-types", + "reth-stages-types", + "reth-static-file-types", + "reth-storage-errors", + "reth-tokio-util", + "tracing", +] + [[package]] name = "reth-static-file-types" version = "1.1.2" @@ -4762,6 +6418,8 @@ dependencies = [ "dyn-clone", "futures-util", "metrics", + "pin-project", + "rayon", "reth-metrics", "thiserror 2.0.4", "tokio", @@ -4769,6 +6427,16 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "reth-tokio-util" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "reth-tracing" version = "1.1.2" @@ -4856,6 +6524,7 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", + "alloy-serde", "alloy-trie", "bytes", "derive_more", @@ -4884,6 +6553,7 @@ dependencies = [ "reth-storage-errors", "reth-trie", "revm", + "serde", "tracing", ] @@ -4917,6 +6587,23 @@ dependencies = [ "serde_json", ] +[[package]] +name = "revm-inspectors" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7f5f8a2deafb3c76f357bbf9e71b73bddb915c4994bbbe3208fbfbe8fc7f8e" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-rpc-types-trace", + "alloy-sol-types", + "anstyle", + "colorchoice", + "revm", + "serde_json", + "thiserror 1.0.65", +] + [[package]] name = "revm-interpreter" version = "14.0.0" @@ -4942,7 +6629,7 @@ dependencies = [ "revm-primitives", "ripemd", "secp256k1", - "sha2", + "sha2 0.10.8", "substrate-bn", ] @@ -4972,7 +6659,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac", + "hmac 0.12.1", "subtle", ] @@ -5056,12 +6743,13 @@ dependencies = [ "hyper", "hyper-util", "jsonrpsee", - "lru", + "lru 0.10.1", "metrics", "metrics-derive", "metrics-exporter-prometheus", "metrics-process", "metrics-util", + "op-alloy-rpc-jsonrpsee", "op-alloy-rpc-types", "op-alloy-rpc-types-engine", "opentelemetry", @@ -5072,6 +6760,7 @@ dependencies = [ "reqwest", "reth-optimism-payload-builder", "reth-payload-primitives", + "reth-rpc-api", "reth-rpc-layer", "serde", "serde_json", @@ -5405,6 +7094,9 @@ name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] [[package]] name = "semver-parser" @@ -5415,6 +7107,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + [[package]] name = "serde" version = "1.0.214" @@ -5459,6 +7157,15 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -5512,6 +7219,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.8" @@ -5552,6 +7272,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shellexpand" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +dependencies = [ + "dirs", +] + [[package]] name = "shlex" version = "1.3.0" @@ -5889,7 +7618,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", "serde", "time-core", @@ -6009,14 +7740,30 @@ dependencies = [ "futures-io", "futures-sink", "pin-project-lite", + "slab", "tokio", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -6025,6 +7772,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.6.0", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -6274,6 +8023,53 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trust-dns-proto" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "smallvec", + "thiserror 1.0.65", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" +dependencies = [ + "cfg-if", + "futures-util", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "serde", + "smallvec", + "thiserror 1.0.65", + "tokio", + "tracing", + "trust-dns-proto", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -6304,6 +8100,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unarray" version = "0.1.4" @@ -6349,6 +8157,16 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "unsigned-varint" version = "0.8.0" @@ -6368,7 +8186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", ] @@ -6399,6 +8217,20 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vergen" +version = "8.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" +dependencies = [ + "anyhow", + "cargo_metadata", + "cfg-if", + "regex", + "rustversion", + "time", +] + [[package]] name = "version_check" version = "0.9.5" @@ -6547,6 +8379,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -6872,6 +8710,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index 3b051a474275e..bd861c6c2cfb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] op-alloy-rpc-types-engine = "0.7.3" op-alloy-rpc-types = "0.7.3" +op-alloy-rpc-jsonrpsee = { version = "0.9.6", features = ["client"] } alloy-rpc-types-engine = "0.7.3" alloy-rpc-types-eth = "0.7.3" alloy-primitives = "0.8.10" @@ -15,7 +16,7 @@ tracing-subscriber = { version = "0.3.11", features = ["env-filter"] } serde = { version = "1", features = ["derive"] } thiserror = "1.0" clap = { version = "4", features = ["derive", "env"] } -jsonrpsee = {version = "0.24.4", features = ["server", "http-client", "macros"]} +jsonrpsee = { version = "0.24", features = ["server", "http-client", "macros"] } lru = "0.10.0" reqwest = "0.12.5" http = "1.1.0" @@ -27,11 +28,21 @@ hyper = { version = "1.4.1", features = ["full"] } hyper-util = { version = "0.1", features = ["full"] } serde_json = "1.0.96" reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" } -reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222", features = ["optimism"] } +reth-rpc-api = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222", features = [ + "client", +] } +reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222", features = [ + "optimism", +] } reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" } opentelemetry = { version = "0.26.0", features = ["trace"] } opentelemetry-http = "0.26.0" -opentelemetry-otlp = { version = "0.26.0", features = ["http-proto", "http-json", "reqwest-client", "trace"] } +opentelemetry-otlp = { version = "0.26.0", features = [ + "http-proto", + "http-json", + "reqwest-client", + "trace", +] } opentelemetry_sdk = { version = "0.26.0", features = ["rt-tokio"] } tracing-opentelemetry = "0.27.0" futures = "0.3.31" @@ -44,4 +55,4 @@ metrics-util = "0.18.0" [dev-dependencies] anyhow = "1.0" assert_cmd = "2.0.10" -predicates = "3.1.2" \ No newline at end of file +predicates = "3.1.2" diff --git a/src/server.rs b/src/server.rs index d5b4763d030d9..58a71ffd3fc07 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,5 +1,5 @@ use crate::metrics::ServerMetrics; -use alloy_primitives::{Bytes, B256}; +use alloy_primitives::{Bytes, B256, U128, U64}; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, @@ -11,12 +11,14 @@ use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::error::INVALID_REQUEST_CODE; use jsonrpsee::types::{ErrorCode, ErrorObject}; use lru::LruCache; +use op_alloy_rpc_jsonrpsee::traits::MinerApiExtServer; use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; use opentelemetry::global::{self, BoxedSpan, BoxedTracer}; use opentelemetry::trace::{Span, TraceContextExt, Tracer}; use opentelemetry::{Context, KeyValue}; use reth_optimism_payload_builder::{OpPayloadAttributes, OpPayloadBuilderAttributes}; use reth_payload_primitives::PayloadBuilderAttributes; +use reth_rpc_api::{MinerApiClient, MinerApiServer}; use reth_rpc_layer::AuthClientService; use std::num::NonZero; use std::sync::Arc; @@ -116,21 +118,6 @@ pub trait EthApi { async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult; } -#[rpc(server, client, namespace = "miner")] -pub trait MinerApi { - #[method(name = "setMaxDASize")] - async fn set_max_da_size(&self, bytes: Bytes) -> RpcResult; - - #[method(name = "setExtra")] - async fn set_extra(&self, bytes: Bytes) -> RpcResult; - - #[method(name = "setGasPrice")] - async fn set_gas_price(&self, bytes: Bytes) -> RpcResult; - - #[method(name = "setGasLimit")] - async fn set_gas_limit(&self, bytes: Bytes) -> RpcResult; -} - pub struct HttpClientWrapper>> { pub client: C, pub url: String, @@ -209,137 +196,106 @@ where } } -#[async_trait] impl MinerApiServer for EthEngineApi> where C: MinerApiClient + Send + Sync + Clone + 'static, { - async fn set_max_da_size(&self, bytes: Bytes) -> RpcResult { - debug!( - message = "received miner_setMaxDASize", - "bytes_len" = bytes.len() - ); - - let builder_client = self.builder_client.client.clone(); - let url = self.builder_client.url.clone(); - let tx_bytes = bytes.clone(); - tokio::spawn(async move { - builder_client.set_max_da_size(tx_bytes).await.map_err(|e| { - error!(message = "error calling miner_setMaxDASize for builder", "url" =url, "error" = %e); - }) - }); - - self.l2_client - .client - .set_max_da_size(bytes) - .await - .map_err(|e| match e { - ClientError::Call(err) => err, - other_error => { - error!( - message = "error calling miner_setMaxDASize for l2 client", - "url" = self.l2_client.url, - "error" = %other_error, - ); - ErrorCode::InternalError.into() - } - }) - } - - async fn set_extra(&self, bytes: Bytes) -> RpcResult { + fn set_extra(&self, record: Bytes) -> RpcResult { debug!( message = "received miner_setExtra", - "bytes_len" = bytes.len() + "record_len" = record.len() ); let builder_client = self.builder_client.client.clone(); let url = self.builder_client.url.clone(); - let tx_bytes = bytes.clone(); + let rec = record.clone(); tokio::spawn(async move { - builder_client.set_extra(tx_bytes).await.map_err(|e| { + builder_client.set_extra(rec).await.map_err(|e| { error!(message = "error calling miner_setExtra for builder", "url" =url, "error" = %e); }) }); - self.l2_client - .client - .set_extra(bytes) - .await - .map_err(|e| match e { - ClientError::Call(err) => err, + match tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on(self.l2_client.client.set_extra(record)) + }) { + Ok(result) => Ok(result), + Err(e) => match e { + ClientError::Call(err) => Err(err), other_error => { error!( message = "error calling miner_setExtra for l2 client", "url" = self.l2_client.url, "error" = %other_error, ); - ErrorCode::InternalError.into() + Err(ErrorCode::InternalError.into()) } - }) + }, + } } - async fn set_gas_price(&self, bytes: Bytes) -> RpcResult { + fn set_gas_limit(&self, gas_price: U128) -> RpcResult { debug!( - message = "received miner_setGasPrice", - "bytes_len" = bytes.len() + message = "received miner_setGasLimit", + "gas_price" = ?gas_price ); let builder_client = self.builder_client.client.clone(); let url = self.builder_client.url.clone(); - let tx_bytes = bytes.clone(); + let gas_price = gas_price.clone(); tokio::spawn(async move { - builder_client.set_gas_price(tx_bytes).await.map_err(|e| { - error!(message = "error calling miner_setGasPrice for builder", "url" =url, "error" = %e); + builder_client.set_gas_limit(gas_price).await.map_err(|e| { + error!(message = "error calling miner_setGasLimit for builder", "url" =url, "error" = %e); }) }); - self.l2_client - .client - .set_gas_price(bytes) - .await - .map_err(|e| match e { - ClientError::Call(err) => err, + match tokio::task::block_in_place(|| { + tokio::runtime::Handle::current() + .block_on(self.l2_client.client.set_gas_limit(gas_price)) + }) { + Ok(result) => Ok(result), + Err(e) => match e { + ClientError::Call(err) => Err(err), other_error => { error!( - message = "error calling miner_setGasPrice for l2 client", + message = "error calling miner_setGasLimit for l2 client", "url" = self.l2_client.url, "error" = %other_error, ); - ErrorCode::InternalError.into() + Err(ErrorCode::InternalError.into()) } - }) + }, + } } - async fn set_gas_limit(&self, bytes: Bytes) -> RpcResult { - debug!( - message = "received miner_setGasLimit", - "bytes_len" = bytes.len() - ); + fn set_gas_price(&self, gas_price: U128) -> RpcResult { + debug!(message = "received miner_setGasPrice", ?gas_price); let builder_client = self.builder_client.client.clone(); let url = self.builder_client.url.clone(); - let tx_bytes = bytes.clone(); + let gas_price = gas_price.clone(); tokio::spawn(async move { - builder_client.set_gas_limit(tx_bytes).await.map_err(|e| { - error!(message = "error calling miner_setGasLimit for builder", "url" =url, "error" = %e); + builder_client.set_gas_price(gas_price).await.map_err(|e| { + error!(message = "error calling miner_setGasPrice for builder", "url" =url, "error" = %e); }) }); - self.l2_client - .client - .set_gas_limit(bytes) - .await - .map_err(|e| match e { - ClientError::Call(err) => err, + match tokio::task::block_in_place(|| { + tokio::runtime::Handle::current() + .block_on(self.l2_client.client.set_gas_price(gas_price)) + }) { + Ok(result) => Ok(result), + Err(e) => match e { + ClientError::Call(err) => Err(err), other_error => { error!( - message = "error calling miner_setGasLimit for l2 client", + message = "error calling miner_setGasPrice for l2 client", "url" = self.l2_client.url, "error" = %other_error, ); - ErrorCode::InternalError.into() + Err(ErrorCode::InternalError.into()) } - }) + }, + } } } From f5bf8c8e5d45d0a29572c7e5c9eb7568a32c0838 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 15:59:43 -0500 Subject: [PATCH 049/429] wip (flashbots/rollup-boost) --- src/server.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/server.rs b/src/server.rs index 58a71ffd3fc07..c3a75b5f5d052 100644 --- a/src/server.rs +++ b/src/server.rs @@ -11,7 +11,7 @@ use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::error::INVALID_REQUEST_CODE; use jsonrpsee::types::{ErrorCode, ErrorObject}; use lru::LruCache; -use op_alloy_rpc_jsonrpsee::traits::MinerApiExtServer; +use op_alloy_rpc_jsonrpsee::traits::{MinerApiExtClient, MinerApiExtServer}; use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; use opentelemetry::global::{self, BoxedSpan, BoxedTracer}; use opentelemetry::trace::{Span, TraceContextExt, Tracer}; @@ -299,6 +299,15 @@ where } } +impl MinerApiExtServer for EthEngineApi> +where + C: MinerApiExtClient + Send + Sync + Clone + 'static, +{ + async fn set_max_da_size(&self, max_tx_size: U64, max_block_size: U64) -> RpcResult { + todo!() + } +} + #[async_trait] impl EngineApiServer for EthEngineApi> where From b0f32e9c9cd4407a7c6f3e509c39efaf5e394e51 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 16:10:15 -0500 Subject: [PATCH 050/429] update MinerApi trait methods to be async (flashbots/rollup-boost) --- src/server.rs | 80 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 63 insertions(+), 17 deletions(-) diff --git a/src/server.rs b/src/server.rs index c3a75b5f5d052..b5df12b382b39 100644 --- a/src/server.rs +++ b/src/server.rs @@ -18,7 +18,6 @@ use opentelemetry::trace::{Span, TraceContextExt, Tracer}; use opentelemetry::{Context, KeyValue}; use reth_optimism_payload_builder::{OpPayloadAttributes, OpPayloadBuilderAttributes}; use reth_payload_primitives::PayloadBuilderAttributes; -use reth_rpc_api::{MinerApiClient, MinerApiServer}; use reth_rpc_layer::AuthClientService; use std::num::NonZero; use std::sync::Arc; @@ -196,11 +195,33 @@ where } } +/*TODO: Remove this in favor of the `MinerApi` from Reth once the + trait methods are updated to be async +*/ +/// Miner namespace rpc interface that can control miner/builder settings +#[rpc(server, client, namespace = "miner")] +pub trait MinerApi { + /// Sets the extra data string that is included when this miner mines a block. + /// + /// Returns an error if the extra data is too long. + #[method(name = "setExtra")] + async fn set_extra(&self, record: Bytes) -> RpcResult; + + /// Sets the minimum accepted gas price for the miner. + #[method(name = "setGasPrice")] + async fn set_gas_price(&self, gas_price: U128) -> RpcResult; + + /// Sets the gaslimit to target towards during mining. + #[method(name = "setGasLimit")] + async fn set_gas_limit(&self, gas_price: U128) -> RpcResult; +} + +#[async_trait] impl MinerApiServer for EthEngineApi> where C: MinerApiClient + Send + Sync + Clone + 'static, { - fn set_extra(&self, record: Bytes) -> RpcResult { + async fn set_extra(&self, record: Bytes) -> RpcResult { debug!( message = "received miner_setExtra", "record_len" = record.len() @@ -215,9 +236,7 @@ where }) }); - match tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on(self.l2_client.client.set_extra(record)) - }) { + match self.l2_client.client.set_extra(record).await { Ok(result) => Ok(result), Err(e) => match e { ClientError::Call(err) => Err(err), @@ -233,7 +252,7 @@ where } } - fn set_gas_limit(&self, gas_price: U128) -> RpcResult { + async fn set_gas_limit(&self, gas_price: U128) -> RpcResult { debug!( message = "received miner_setGasLimit", "gas_price" = ?gas_price @@ -248,10 +267,7 @@ where }) }); - match tokio::task::block_in_place(|| { - tokio::runtime::Handle::current() - .block_on(self.l2_client.client.set_gas_limit(gas_price)) - }) { + match self.l2_client.client.set_gas_limit(gas_price).await { Ok(result) => Ok(result), Err(e) => match e { ClientError::Call(err) => Err(err), @@ -267,22 +283,18 @@ where } } - fn set_gas_price(&self, gas_price: U128) -> RpcResult { + async fn set_gas_price(&self, gas_price: U128) -> RpcResult { debug!(message = "received miner_setGasPrice", ?gas_price); let builder_client = self.builder_client.client.clone(); let url = self.builder_client.url.clone(); - let gas_price = gas_price.clone(); tokio::spawn(async move { builder_client.set_gas_price(gas_price).await.map_err(|e| { error!(message = "error calling miner_setGasPrice for builder", "url" =url, "error" = %e); }) }); - match tokio::task::block_in_place(|| { - tokio::runtime::Handle::current() - .block_on(self.l2_client.client.set_gas_price(gas_price)) - }) { + match self.l2_client.client.set_gas_price(gas_price).await { Ok(result) => Ok(result), Err(e) => match e { ClientError::Call(err) => Err(err), @@ -299,12 +311,46 @@ where } } +#[async_trait] impl MinerApiExtServer for EthEngineApi> where C: MinerApiExtClient + Send + Sync + Clone + 'static, { async fn set_max_da_size(&self, max_tx_size: U64, max_block_size: U64) -> RpcResult { - todo!() + debug!( + message = "received miner_setMaxDASize", + ?max_tx_size, + ?max_block_size + ); + + let builder_client = self.builder_client.client.clone(); + let url = self.builder_client.url.clone(); + tokio::spawn(async move { + builder_client.set_max_da_size(max_tx_size, max_block_size).await.map_err(|e| { + error!(message = "error calling miner_setMaxDASize for builder", "url" =url, "error" = %e); + }) + }); + + match tokio::task::block_in_place(|| { + tokio::runtime::Handle::current().block_on( + self.l2_client + .client + .set_max_da_size(max_tx_size, max_block_size), + ) + }) { + Ok(result) => Ok(result), + Err(e) => match e { + ClientError::Call(err) => Err(err), + other_error => { + error!( + message = "error calling miner_setMaxDASize for l2 client", + "url" = self.l2_client.url, + "error" = %other_error, + ); + Err(ErrorCode::InternalError.into()) + } + }, + } } } From 3fdc9f284d1d63c9489fc38c4e5662e7f469f7f7 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 16:12:02 -0500 Subject: [PATCH 051/429] remove unneeded clone (flashbots/rollup-boost) --- src/server.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/server.rs b/src/server.rs index b5df12b382b39..0f0105a915c61 100644 --- a/src/server.rs +++ b/src/server.rs @@ -260,7 +260,6 @@ where let builder_client = self.builder_client.client.clone(); let url = self.builder_client.url.clone(); - let gas_price = gas_price.clone(); tokio::spawn(async move { builder_client.set_gas_limit(gas_price).await.map_err(|e| { error!(message = "error calling miner_setGasLimit for builder", "url" =url, "error" = %e); From 5dfd1b690a9e3a30d3cd6130fe11c7ab7bc2731d Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 16:25:14 -0500 Subject: [PATCH 052/429] remove unused dep (flashbots/rollup-boost) --- Cargo.lock | 1851 ++-------------------------------------------------- Cargo.toml | 3 - 2 files changed, 66 insertions(+), 1788 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1e6795bdf405..5dd28b0d12fcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,41 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - [[package]] name = "ahash" version = "0.8.11" @@ -89,12 +54,6 @@ dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "alloy-chains" version = "0.1.43" @@ -137,26 +96,6 @@ dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde", - "serde", -] - -[[package]] -name = "alloy-dyn-abi" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80759b3f57b3b20fa7cd8fef6479930fc95461b58ff8adea6e87e618449c8a1d" -dependencies = [ - "alloy-json-abi", - "alloy-primitives", - "alloy-sol-type-parser", - "alloy-sol-types", - "const-hex", - "derive_more", - "itoa", - "serde", - "serde_json", - "winnow", ] [[package]] @@ -205,7 +144,7 @@ dependencies = [ "ethereum_ssz_derive", "once_cell", "serde", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -220,57 +159,6 @@ dependencies = [ "serde", ] -[[package]] -name = "alloy-json-abi" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac4b22b3e51cac09fd2adfcc73b55f447b4df669f983c13f7894ec82b607c63f" -dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-json-rpc" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa077efe0b834bcd89ff4ba547f48fb081e4fdc3673dd7da1b295a2cf2bb7b7" -dependencies = [ - "alloy-primitives", - "alloy-sol-types", - "serde", - "serde_json", - "thiserror 2.0.4", - "tracing", -] - -[[package]] -name = "alloy-network" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209a1882a08e21aca4aac6e2a674dc6fcf614058ef8cb02947d63782b1899552" -dependencies = [ - "alloy-consensus", - "alloy-consensus-any", - "alloy-eips", - "alloy-json-rpc", - "alloy-network-primitives", - "alloy-primitives", - "alloy-rpc-types-any", - "alloy-rpc-types-eth", - "alloy-serde", - "alloy-signer", - "alloy-sol-types", - "async-trait", - "auto_impl", - "futures-utils-wasm", - "serde", - "serde_json", - "thiserror 2.0.4", -] - [[package]] name = "alloy-network-primitives" version = "0.7.3" @@ -351,56 +239,6 @@ dependencies = [ "serde", ] -[[package]] -name = "alloy-rpc-types-admin" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f0874a976ccdf83a178ad93b64bec5b8c91a47428d714d544ca70258acfa07b" -dependencies = [ - "alloy-genesis", - "alloy-primitives", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-rpc-types-anvil" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d33bc190844626c08e21897736dbd7956ab323c09e6f141b118d1c8b7aff689e" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "serde", -] - -[[package]] -name = "alloy-rpc-types-any" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "200661999b6e235d9840be5d60a6e8ae2f0af9eb2a256dd378786744660e36ec" -dependencies = [ - "alloy-consensus-any", - "alloy-rpc-types-eth", - "alloy-serde", -] - -[[package]] -name = "alloy-rpc-types-beacon" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc37861dc8cbf5da35d346139fbe6e03ee7823cc21138a2c4a590d3b0b4b24be" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "alloy-rpc-types-engine", - "alloy-serde", - "serde", - "serde_with", - "thiserror 2.0.4", -] - [[package]] name = "alloy-rpc-types-debug" version = "0.7.3" @@ -451,46 +289,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "alloy-rpc-types-mev" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093d618d5a42808e7ae26062f415a1e816fc27d3d32662c6ed52d0871b154894" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "serde", - "serde_json", -] - -[[package]] -name = "alloy-rpc-types-trace" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e073ab0e67429c60be281e181731132fd07d82e091c10c29ace6935101034bb" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "serde", - "serde_json", - "thiserror 2.0.4", -] - -[[package]] -name = "alloy-rpc-types-txpool" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7435f6bfb93912f16d64bb61f4278fa698469e054784f477337ef87ec0b2527b" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "serde", -] - [[package]] name = "alloy-serde" version = "0.7.3" @@ -503,20 +301,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "alloy-signer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2cbff01a673936c2efd7e00d4c0e9a4dbbd6d600e2ce298078d33efbb19cd7" -dependencies = [ - "alloy-primitives", - "async-trait", - "auto_impl", - "elliptic-curve", - "k256", - "thiserror 2.0.4", -] - [[package]] name = "alloy-sol-macro" version = "0.8.14" @@ -564,27 +348,15 @@ dependencies = [ "syn-solidity", ] -[[package]] -name = "alloy-sol-type-parser" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e60b084fe1aef8acecda2743ff2d93c18ff3eb67a2d3b12f62582a1e66ef5e" -dependencies = [ - "serde", - "winnow", -] - [[package]] name = "alloy-sol-types" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9dc0fffe397aa17628160e16b89f704098bf3c9d74d5d369ebc239575936de5" dependencies = [ - "alloy-json-abi", "alloy-primitives", "alloy-sol-macro", "const-hex", - "serde", ] [[package]] @@ -820,12 +592,6 @@ dependencies = [ "rand", ] -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - [[package]] name = "arrayvec" version = "0.7.6" @@ -835,12 +601,6 @@ dependencies = [ "serde", ] -[[package]] -name = "asn1_der" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" - [[package]] name = "assert_cmd" version = "2.0.16" @@ -1028,24 +788,12 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -1157,15 +905,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -1175,15 +914,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - [[package]] name = "blst" version = "0.3.13" @@ -1217,15 +947,6 @@ dependencies = [ "alloc-stdlib", ] -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - [[package]] name = "bstr" version = "1.10.0" @@ -1285,38 +1006,6 @@ dependencies = [ "serde", ] -[[package]] -name = "camino" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver 1.0.23", - "serde", - "serde_json", - "thiserror 1.0.65", -] - [[package]] name = "cc" version = "1.1.31" @@ -1364,16 +1053,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clang-sys" version = "1.8.1" @@ -1450,15 +1129,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "concat-kdf" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d72c1252426a83be2092dd5884a5f6e3b8e7180f6891b6263d2c21b92ec8816" -dependencies = [ - "digest 0.10.7", -] - [[package]] name = "const-hex" version = "1.14.0" @@ -1479,40 +1149,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] -name = "const_format" -version = "0.2.34" +name = "convert_case" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" dependencies = [ - "const_format_proc_macros", - "konst", + "unicode-segmentation", ] [[package]] -name = "const_format_proc_macros" -version = "0.2.34" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -1524,15 +1173,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - [[package]] name = "cpufeatures" version = "0.2.14" @@ -1631,56 +1271,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest 0.10.7", - "fiat-crypto", - "rustc_version 0.4.1", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "darling" version = "0.20.10" @@ -1728,44 +1321,6 @@ dependencies = [ "lock_api", "once_cell", "parking_lot_core", - "serde", -] - -[[package]] -name = "data-encoding" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" - -[[package]] -name = "data-encoding-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b16d9d0d88a5273d830dac8b78ceb217ffc9b1d5404e5597a3542515329405b" -dependencies = [ - "data-encoding", - "data-encoding-macro-internal", -] - -[[package]] -name = "data-encoding-macro-internal" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145d32e826a7748b69ee8fc62d3e6355ff7f1051df53141e7048162fc90481b" -dependencies = [ - "data-encoding", - "syn 2.0.90", -] - -[[package]] -name = "delay_map" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df941644b671f05f59433e481ba0d31ac10e3667de725236a4c0d587c496fba1" -dependencies = [ - "futures", - "tokio", - "tokio-util", ] [[package]] @@ -1853,87 +1408,12 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "const-oid", "crypto-common", "subtle", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "discv5" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e6b70634e26c909d1edbb3142b3eaf3b89da0e52f284f00ca7c80d9901ad9e" -dependencies = [ - "aes", - "aes-gcm", - "alloy-rlp", - "arrayvec", - "ctr", - "delay_map", - "enr", - "fnv", - "futures", - "hashlink", - "hex", - "hkdf", - "lazy_static", - "libp2p-identity", - "lru 0.12.5", - "more-asserts", - "multiaddr", - "parking_lot", - "rand", - "smallvec", - "socket2", - "tokio", - "tracing", - "uint 0.10.0", - "zeroize", -] - [[package]] name = "doc-comment" version = "0.3.3" @@ -1972,31 +1452,6 @@ dependencies = [ "spki", ] -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core", - "serde", - "sha2 0.10.8", - "subtle", - "zeroize", -] - [[package]] name = "either" version = "1.13.0" @@ -2046,29 +1501,13 @@ dependencies = [ "alloy-rlp", "base64 0.21.7", "bytes", - "ed25519-dalek", "hex", - "k256", "log", "rand", - "secp256k1", - "serde", "sha3", "zeroize", ] -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "enumn" version = "0.1.14" @@ -2174,12 +1613,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - [[package]] name = "filetime" version = "0.2.25" @@ -2379,19 +1812,12 @@ dependencies = [ "slab", ] -[[package]] -name = "futures-utils-wasm" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" - [[package]] name = "generic-array" version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "serde", "typenum", "version_check", "zeroize", @@ -2410,16 +1836,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "gimli" version = "0.31.1" @@ -2528,9 +1944,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] [[package]] name = "hashbrown" @@ -2538,21 +1951,10 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ - "allocator-api2", - "equivalent", "foldhash", "serde", ] -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown 0.14.5", -] - [[package]] name = "heck" version = "0.5.0" @@ -2580,25 +1982,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.12.1" @@ -2608,17 +1991,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "hmac-drbg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" -dependencies = [ - "digest 0.9.0", - "generic-array", - "hmac 0.8.1", -] - [[package]] name = "home" version = "0.5.9" @@ -2628,17 +2000,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - [[package]] name = "http" version = "0.2.12" @@ -2713,22 +2074,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime", - "serde", -] - [[package]] name = "hyper" version = "1.5.0" @@ -2846,16 +2191,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.5.0" @@ -2866,16 +2201,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "if-addrs" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78a89907582615b19f6f0da1af18abf6ff08be259395669b834b057a7ee92d8" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "impl-codec" version = "0.6.0" @@ -2965,38 +2290,16 @@ dependencies = [ ] [[package]] -name = "inout" -version = "0.1.3" +name = "ipnet" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding", - "generic-array", -] +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" [[package]] -name = "ipconfig" -version = "0.3.2" +name = "iri-string" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2", - "widestring", - "windows-sys 0.48.0", - "winreg", -] - -[[package]] -name = "ipnet" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" - -[[package]] -name = "iri-string" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea" +checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea" dependencies = [ "memchr", "serde", @@ -3275,8 +2578,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.8", - "signature", + "sha2", ] [[package]] @@ -3298,21 +2600,6 @@ dependencies = [ "sha3-asm", ] -[[package]] -name = "konst" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f0e13e6483b8c34885f7e6c9f19b1a7bd449c673fbb948a51c99d66ef74f4" -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 = "kqueue" version = "1.0.8" @@ -3370,25 +2657,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" -[[package]] -name = "libp2p-identity" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b5621d159b32282eac446bed6670c39c7dc68a200a992d8f056afa0066f6d" -dependencies = [ - "asn1_der", - "bs58", - "ed25519-dalek", - "hkdf", - "libsecp256k1", - "multihash", - "quick-protobuf", - "sha2 0.10.8", - "thiserror 1.0.65", - "tracing", - "zeroize", -] - [[package]] name = "libproc" version = "0.14.10" @@ -3411,70 +2679,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "libsecp256k1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" -dependencies = [ - "arrayref", - "base64 0.13.1", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand", - "serde", - "sha2 0.9.9", - "typenum", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linked_hash_set" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae85b5be22d9843c80e5fc80e9b64c8a3b1f98f867c709956eca3efff4e92e2" -dependencies = [ - "linked-hash-map", - "serde", -] - [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -3507,24 +2711,6 @@ dependencies = [ "hashbrown 0.13.2", ] -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.0", -] - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "lz4_flex" version = "0.11.3" @@ -3540,12 +2726,6 @@ dependencies = [ "libc", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -3735,52 +2915,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "more-asserts" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" - -[[package]] -name = "multiaddr" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961" -dependencies = [ - "arrayref", - "byteorder", - "data-encoding", - "libp2p-identity", - "multibase", - "multihash", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", -] - -[[package]] -name = "multibase" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" -dependencies = [ - "base-x", - "data-encoding", - "data-encoding-macro", -] - -[[package]] -name = "multihash" -version = "0.19.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" -dependencies = [ - "core2", - "unsigned-varint", -] - [[package]] name = "native-tls" version = "0.2.12" @@ -3837,7 +2971,6 @@ dependencies = [ "libc", "log", "mio 0.8.11", - "serde", "walkdir", "windows-sys 0.48.0", ] @@ -3971,15 +3104,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - [[package]] name = "nybbles" version = "0.2.1" @@ -4116,12 +3240,6 @@ dependencies = [ "thiserror 2.0.4", ] -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "openssl" version = "0.10.68" @@ -4249,12 +3367,6 @@ dependencies = [ "tokio-stream", ] -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "ordered-float" version = "4.4.0" @@ -4279,7 +3391,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -4423,18 +3535,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "portable-atomic" version = "1.9.0" @@ -4513,7 +3613,7 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", - "uint 0.9.5", + "uint", ] [[package]] @@ -4653,15 +3753,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-protobuf" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" -dependencies = [ - "byteorder", -] - [[package]] name = "quote" version = "1.0.37" @@ -4765,17 +3856,6 @@ dependencies = [ "bitflags 2.6.0", ] -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror 1.0.65", -] - [[package]] name = "regex" version = "1.11.1" @@ -4864,16 +3944,6 @@ dependencies = [ "windows-registry", ] -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error", -] - [[package]] name = "reth-basic-payload-builder" version = "1.1.2" @@ -4903,46 +3973,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "reth-beacon-consensus" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rpc-types-engine", - "futures", - "itertools 0.13.0", - "metrics", - "reth-blockchain-tree-api", - "reth-codecs", - "reth-db-api", - "reth-engine-primitives", - "reth-errors", - "reth-ethereum-consensus", - "reth-metrics", - "reth-network-p2p", - "reth-node-types", - "reth-payload-builder", - "reth-payload-builder-primitives", - "reth-payload-primitives", - "reth-payload-validator", - "reth-primitives", - "reth-primitives-traits", - "reth-provider", - "reth-prune", - "reth-stages-api", - "reth-static-file", - "reth-tasks", - "reth-tokio-util", - "schnellru", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tracing", -] - [[package]] name = "reth-blockchain-tree-api" version = "1.1.2" @@ -5003,23 +4033,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "reth-cli-util" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "cfg-if", - "eyre", - "libc", - "rand", - "reth-fs-util", - "secp256k1", - "serde", - "thiserror 2.0.4", -] - [[package]] name = "reth-codecs" version = "1.1.2" @@ -5048,20 +4061,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "reth-config" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "eyre", - "humantime-serde", - "reth-network-types", - "reth-prune-types", - "reth-stages-types", - "serde", - "toml", -] - [[package]] name = "reth-consensus" version = "1.1.2" @@ -5162,133 +4161,28 @@ dependencies = [ ] [[package]] -name = "reth-discv4" +name = "reth-engine-primitives" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "alloy-consensus", "alloy-primitives", - "alloy-rlp", - "discv5", - "enr", - "generic-array", - "itertools 0.13.0", - "parking_lot", - "rand", - "reth-ethereum-forks", - "reth-net-banlist", - "reth-net-nat", - "reth-network-peers", - "schnellru", - "secp256k1", + "alloy-rpc-types-engine", + "futures", + "reth-errors", + "reth-execution-types", + "reth-payload-builder-primitives", + "reth-payload-primitives", + "reth-primitives", + "reth-primitives-traits", + "reth-trie", "serde", "thiserror 2.0.4", "tokio", - "tokio-stream", - "tracing", ] [[package]] -name = "reth-discv5" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "derive_more", - "discv5", - "enr", - "futures", - "itertools 0.13.0", - "metrics", - "rand", - "reth-chainspec", - "reth-ethereum-forks", - "reth-metrics", - "reth-network-peers", - "secp256k1", - "thiserror 2.0.4", - "tokio", - "tracing", -] - -[[package]] -name = "reth-dns-discovery" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", - "data-encoding", - "enr", - "linked_hash_set", - "parking_lot", - "reth-ethereum-forks", - "reth-network-peers", - "reth-tokio-util", - "schnellru", - "secp256k1", - "serde", - "serde_with", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tracing", - "trust-dns-resolver", -] - -[[package]] -name = "reth-ecies" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "aes", - "alloy-primitives", - "alloy-rlp", - "block-padding", - "byteorder", - "cipher", - "concat-kdf", - "ctr", - "digest 0.10.7", - "futures", - "generic-array", - "hmac 0.12.1", - "pin-project", - "rand", - "reth-network-peers", - "secp256k1", - "sha2 0.10.8", - "sha3", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "typenum", -] - -[[package]] -name = "reth-engine-primitives" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-consensus", - "alloy-primitives", - "alloy-rpc-types-engine", - "futures", - "reth-errors", - "reth-execution-types", - "reth-payload-builder-primitives", - "reth-payload-primitives", - "reth-primitives", - "reth-primitives-traits", - "reth-trie", - "serde", - "thiserror 2.0.4", - "tokio", -] - -[[package]] -name = "reth-errors" +name = "reth-errors" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ @@ -5300,34 +4194,6 @@ dependencies = [ "thiserror 2.0.4", ] -[[package]] -name = "reth-eth-wire" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-chains", - "alloy-primitives", - "alloy-rlp", - "bytes", - "derive_more", - "futures", - "pin-project", - "reth-codecs", - "reth-ecies", - "reth-eth-wire-types", - "reth-ethereum-forks", - "reth-metrics", - "reth-network-peers", - "reth-primitives-traits", - "serde", - "snap", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", -] - [[package]] name = "reth-eth-wire-types" version = "1.1.2" @@ -5349,22 +4215,6 @@ dependencies = [ "thiserror 2.0.4", ] -[[package]] -name = "reth-ethereum-consensus" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "reth-chainspec", - "reth-consensus", - "reth-consensus-common", - "reth-primitives", - "reth-primitives-traits", - "tracing", -] - [[package]] name = "reth-ethereum-engine-primitives" version = "1.1.2" @@ -5382,7 +4232,7 @@ dependencies = [ "reth-primitives", "reth-rpc-types-compat", "serde", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -5462,18 +4312,6 @@ dependencies = [ "serde_with", ] -[[package]] -name = "reth-exex-types" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "reth-chain-state", - "reth-execution-types", - "reth-primitives-traits", -] - [[package]] name = "reth-fs-util" version = "1.1.2" @@ -5515,11 +4353,8 @@ name = "reth-metrics" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "futures", "metrics", "metrics-derive", - "tokio", - "tokio-util", ] [[package]] @@ -5530,98 +4365,6 @@ dependencies = [ "alloy-primitives", ] -[[package]] -name = "reth-net-nat" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "futures-util", - "if-addrs", - "reqwest", - "serde_with", - "thiserror 2.0.4", - "tokio", - "tracing", -] - -[[package]] -name = "reth-network" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "aquamarine", - "auto_impl", - "derive_more", - "discv5", - "enr", - "futures", - "itertools 0.13.0", - "metrics", - "parking_lot", - "pin-project", - "rand", - "reth-chainspec", - "reth-consensus", - "reth-discv4", - "reth-discv5", - "reth-dns-discovery", - "reth-ecies", - "reth-eth-wire", - "reth-eth-wire-types", - "reth-ethereum-forks", - "reth-fs-util", - "reth-metrics", - "reth-net-banlist", - "reth-network-api", - "reth-network-p2p", - "reth-network-peers", - "reth-network-types", - "reth-primitives", - "reth-primitives-traits", - "reth-provider", - "reth-storage-api", - "reth-tasks", - "reth-tokio-util", - "reth-transaction-pool", - "rustc-hash 2.0.0", - "schnellru", - "secp256k1", - "serde", - "smallvec", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", -] - -[[package]] -name = "reth-network-api" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types-admin", - "auto_impl", - "derive_more", - "enr", - "futures", - "reth-eth-wire-types", - "reth-ethereum-forks", - "reth-network-p2p", - "reth-network-peers", - "reth-network-types", - "reth-tokio-util", - "serde", - "thiserror 2.0.4", - "tokio", - "tokio-stream", -] - [[package]] name = "reth-network-p2p" version = "1.1.2" @@ -5655,7 +4398,6 @@ dependencies = [ "secp256k1", "serde_with", "thiserror 2.0.4", - "tokio", "url", ] @@ -5664,11 +4406,9 @@ name = "reth-network-types" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "humantime-serde", "reth-ethereum-forks", "reth-net-banlist", "reth-network-peers", - "serde", "serde_json", "tracing", ] @@ -5690,77 +4430,6 @@ dependencies = [ "zstd", ] -[[package]] -name = "reth-node-api" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-rpc-types-engine", - "eyre", - "reth-beacon-consensus", - "reth-consensus", - "reth-engine-primitives", - "reth-evm", - "reth-network-api", - "reth-node-core", - "reth-node-types", - "reth-payload-builder-primitives", - "reth-payload-primitives", - "reth-provider", - "reth-tasks", - "reth-transaction-pool", -] - -[[package]] -name = "reth-node-core" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rpc-types-engine", - "clap", - "const_format", - "derive_more", - "dirs-next", - "eyre", - "futures", - "humantime", - "rand", - "reth-chainspec", - "reth-cli-util", - "reth-config", - "reth-consensus", - "reth-db", - "reth-discv4", - "reth-discv5", - "reth-ethereum-forks", - "reth-net-nat", - "reth-network", - "reth-network-p2p", - "reth-network-peers", - "reth-primitives", - "reth-primitives-traits", - "reth-prune-types", - "reth-rpc-eth-types", - "reth-rpc-server-types", - "reth-rpc-types-compat", - "reth-stages-types", - "reth-storage-api", - "reth-storage-errors", - "reth-tracing", - "reth-transaction-pool", - "secp256k1", - "serde", - "shellexpand", - "strum", - "thiserror 2.0.4", - "toml", - "tracing", - "vergen", -] - [[package]] name = "reth-node-types" version = "1.1.2" @@ -5886,7 +4555,7 @@ dependencies = [ "reth-rpc-types-compat", "reth-transaction-pool", "revm", - "sha2 0.10.8", + "sha2", "thiserror 2.0.4", "tracing", ] @@ -6063,194 +4732,54 @@ dependencies = [ "reth-evm", "reth-execution-types", "reth-fs-util", - "reth-metrics", - "reth-network-p2p", - "reth-nippy-jar", - "reth-node-types", - "reth-optimism-primitives", - "reth-primitives", - "reth-primitives-traits", - "reth-prune-types", - "reth-stages-types", - "reth-storage-api", - "reth-storage-errors", - "reth-trie", - "reth-trie-db", - "revm", - "strum", - "tokio", - "tracing", -] - -[[package]] -name = "reth-prune" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "itertools 0.13.0", - "metrics", - "rayon", - "reth-chainspec", - "reth-config", - "reth-db", - "reth-db-api", - "reth-errors", - "reth-exex-types", - "reth-metrics", - "reth-primitives-traits", - "reth-provider", - "reth-prune-types", - "reth-static-file-types", - "reth-tokio-util", - "rustc-hash 2.0.0", - "thiserror 2.0.4", - "tokio", - "tracing", -] - -[[package]] -name = "reth-prune-types" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", - "bytes", - "derive_more", - "modular-bitfield", - "reth-codecs", - "serde", - "thiserror 2.0.4", -] - -[[package]] -name = "reth-revm" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "reth-execution-errors", - "reth-primitives", - "reth-primitives-traits", - "reth-prune-types", - "reth-storage-api", - "reth-storage-errors", - "reth-trie", - "revm", -] - -[[package]] -name = "reth-rpc-api" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-eips", - "alloy-json-rpc", - "alloy-primitives", - "alloy-rpc-types", - "alloy-rpc-types-admin", - "alloy-rpc-types-anvil", - "alloy-rpc-types-beacon", - "alloy-rpc-types-debug", - "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-rpc-types-mev", - "alloy-rpc-types-trace", - "alloy-rpc-types-txpool", - "alloy-serde", - "jsonrpsee", - "reth-engine-primitives", - "reth-network-peers", - "reth-rpc-eth-api", -] - -[[package]] -name = "reth-rpc-eth-api" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-consensus", - "alloy-dyn-abi", - "alloy-eips", - "alloy-json-rpc", - "alloy-network", - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-rpc-types-mev", - "alloy-serde", - "async-trait", - "auto_impl", - "dyn-clone", - "futures", - "jsonrpsee", - "jsonrpsee-types", - "parking_lot", - "reth-chainspec", - "reth-errors", - "reth-evm", - "reth-execution-types", - "reth-network-api", - "reth-node-api", + "reth-metrics", + "reth-network-p2p", + "reth-nippy-jar", + "reth-node-types", + "reth-optimism-primitives", "reth-primitives", "reth-primitives-traits", - "reth-provider", - "reth-revm", - "reth-rpc-eth-types", - "reth-rpc-server-types", - "reth-rpc-types-compat", - "reth-tasks", - "reth-transaction-pool", - "reth-trie-common", + "reth-prune-types", + "reth-stages-types", + "reth-storage-api", + "reth-storage-errors", + "reth-trie", + "reth-trie-db", "revm", - "revm-inspectors", - "revm-primitives", + "strum", "tokio", "tracing", ] [[package]] -name = "reth-rpc-eth-types" +name = "reth-prune-types" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "alloy-consensus", - "alloy-eips", "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-sol-types", + "bytes", "derive_more", - "futures", - "itertools 0.13.0", - "jsonrpsee-core", - "jsonrpsee-types", - "metrics", - "rand", - "reth-chain-state", - "reth-chainspec", - "reth-errors", - "reth-execution-types", - "reth-metrics", + "modular-bitfield", + "reth-codecs", + "serde", + "thiserror 2.0.4", +] + +[[package]] +name = "reth-revm" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "reth-execution-errors", "reth-primitives", "reth-primitives-traits", - "reth-revm", - "reth-rpc-server-types", - "reth-rpc-types-compat", + "reth-prune-types", "reth-storage-api", - "reth-tasks", - "reth-transaction-pool", + "reth-storage-errors", "reth-trie", "revm", - "revm-inspectors", - "revm-primitives", - "schnellru", - "serde", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tracing", ] [[package]] @@ -6267,22 +4796,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "reth-rpc-server-types" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "alloy-rpc-types-engine", - "jsonrpsee-core", - "jsonrpsee-types", - "reth-errors", - "reth-network-api", - "serde", - "strum", -] - [[package]] name = "reth-rpc-types-compat" version = "1.1.2" @@ -6299,32 +4812,6 @@ dependencies = [ "serde", ] -[[package]] -name = "reth-stages-api" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", - "aquamarine", - "auto_impl", - "futures-util", - "metrics", - "reth-consensus", - "reth-errors", - "reth-metrics", - "reth-network-p2p", - "reth-primitives-traits", - "reth-provider", - "reth-prune", - "reth-stages-types", - "reth-static-file", - "reth-static-file-types", - "reth-tokio-util", - "thiserror 2.0.4", - "tokio", - "tracing", -] - [[package]] name = "reth-stages-types" version = "1.1.2" @@ -6338,27 +4825,6 @@ dependencies = [ "serde", ] -[[package]] -name = "reth-static-file" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", - "parking_lot", - "rayon", - "reth-codecs", - "reth-db", - "reth-db-api", - "reth-primitives-traits", - "reth-provider", - "reth-prune-types", - "reth-stages-types", - "reth-static-file-types", - "reth-storage-errors", - "reth-tokio-util", - "tracing", -] - [[package]] name = "reth-static-file-types" version = "1.1.2" @@ -6418,8 +4884,6 @@ dependencies = [ "dyn-clone", "futures-util", "metrics", - "pin-project", - "rayon", "reth-metrics", "thiserror 2.0.4", "tokio", @@ -6427,16 +4891,6 @@ dependencies = [ "tracing-futures", ] -[[package]] -name = "reth-tokio-util" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "tokio", - "tokio-stream", - "tracing", -] - [[package]] name = "reth-tracing" version = "1.1.2" @@ -6524,7 +4978,6 @@ dependencies = [ "alloy-primitives", "alloy-rlp", "alloy-rpc-types-eth", - "alloy-serde", "alloy-trie", "bytes", "derive_more", @@ -6553,7 +5006,6 @@ dependencies = [ "reth-storage-errors", "reth-trie", "revm", - "serde", "tracing", ] @@ -6587,23 +5039,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "revm-inspectors" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7f5f8a2deafb3c76f357bbf9e71b73bddb915c4994bbbe3208fbfbe8fc7f8e" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-rpc-types-trace", - "alloy-sol-types", - "anstyle", - "colorchoice", - "revm", - "serde_json", - "thiserror 1.0.65", -] - [[package]] name = "revm-interpreter" version = "14.0.0" @@ -6629,7 +5064,7 @@ dependencies = [ "revm-primitives", "ripemd", "secp256k1", - "sha2 0.10.8", + "sha2", "substrate-bn", ] @@ -6659,7 +5094,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac 0.12.1", + "hmac", "subtle", ] @@ -6743,7 +5178,7 @@ dependencies = [ "hyper", "hyper-util", "jsonrpsee", - "lru 0.10.1", + "lru", "metrics", "metrics-derive", "metrics-exporter-prometheus", @@ -6760,7 +5195,6 @@ dependencies = [ "reqwest", "reth-optimism-payload-builder", "reth-payload-primitives", - "reth-rpc-api", "reth-rpc-layer", "serde", "serde_json", @@ -7094,9 +5528,6 @@ name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -dependencies = [ - "serde", -] [[package]] name = "semver-parser" @@ -7157,15 +5588,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -7219,19 +5641,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.8" @@ -7272,15 +5681,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shellexpand" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" -dependencies = [ - "dirs", -] - [[package]] name = "shlex" version = "1.3.0" @@ -7618,9 +6018,7 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", - "libc", "num-conv", - "num_threads", "powerfmt", "serde", "time-core", @@ -7740,30 +6138,14 @@ dependencies = [ "futures-io", "futures-sink", "pin-project-lite", - "slab", "tokio", ] -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] [[package]] name = "toml_edit" @@ -7772,8 +6154,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.6.0", - "serde", - "serde_spanned", "toml_datetime", "winnow", ] @@ -8023,53 +6403,6 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "trust-dns-proto" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.4.0", - "ipnet", - "once_cell", - "rand", - "smallvec", - "thiserror 1.0.65", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand", - "resolv-conf", - "serde", - "smallvec", - "thiserror 1.0.65", - "tokio", - "tracing", - "trust-dns-proto", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -8100,18 +6433,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "uint" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unarray" version = "0.1.4" @@ -8157,16 +6478,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - [[package]] name = "unsigned-varint" version = "0.8.0" @@ -8186,7 +6497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -8217,20 +6528,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vergen" -version = "8.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" -dependencies = [ - "anyhow", - "cargo_metadata", - "cfg-if", - "regex", - "rustversion", - "time", -] - [[package]] name = "version_check" version = "0.9.5" @@ -8379,12 +6676,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "widestring" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" - [[package]] name = "winapi" version = "0.3.9" @@ -8710,16 +7001,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index bd861c6c2cfb8..14b2288d89777 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,9 +28,6 @@ hyper = { version = "1.4.1", features = ["full"] } hyper-util = { version = "0.1", features = ["full"] } serde_json = "1.0.96" reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" } -reth-rpc-api = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222", features = [ - "client", -] } reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222", features = [ "optimism", ] } From 9aab81625223a8da85a541e62baf40fb8ae77eef Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Mon, 27 Jan 2025 16:28:02 -0500 Subject: [PATCH 053/429] fix set max da size impl (flashbots/rollup-boost) --- src/server.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/server.rs b/src/server.rs index 0f0105a915c61..3cd406d153176 100644 --- a/src/server.rs +++ b/src/server.rs @@ -330,13 +330,12 @@ where }) }); - match tokio::task::block_in_place(|| { - tokio::runtime::Handle::current().block_on( - self.l2_client - .client - .set_max_da_size(max_tx_size, max_block_size), - ) - }) { + match self + .l2_client + .client + .set_max_da_size(max_tx_size, max_block_size) + .await + { Ok(result) => Ok(result), Err(e) => match e { ClientError::Call(err) => Err(err), From e0245ce98e62d2fc72f0f3ad0cc64be901ef98d6 Mon Sep 17 00:00:00 2001 From: 0xKitsune <77890308+0xKitsune@users.noreply.github.com> Date: Wed, 29 Jan 2025 12:26:57 -0500 Subject: [PATCH 054/429] fix: Fix RPC initialization (flashbots/rollup-boost#1) * merge all api modules * fix import * simplify error handling * update RollupBoostClient to RollupBoostServer * remove unused error * fix imports * update function name for clarity * add miner api ext server * update into_merged_rpc, wip test * wip tests * update to use try_into * expose rollup boost as a lib * add temp logging * simplifying changes * remove test, update logging --- Cargo.lock | 1 + Cargo.toml | 1 + src/error.rs | 13 ---------- src/main.rs | 71 +++++++++++++++++---------------------------------- src/proxy.rs | 6 ++++- src/server.rs | 57 ++++++++++++++++++++++++++++++++--------- 6 files changed, 76 insertions(+), 73 deletions(-) delete mode 100644 src/error.rs diff --git a/Cargo.lock b/Cargo.lock index 5dd28b0d12fcd..1c60c3833b80d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5171,6 +5171,7 @@ dependencies = [ "assert_cmd", "clap", "dotenv", + "eyre", "futures", "http 1.1.0", "http-body 0.4.6", diff --git a/Cargo.toml b/Cargo.toml index 14b2288d89777..be7913aefee84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ metrics = "0.24.0" metrics-exporter-prometheus = "0.16.0" metrics-process = "2.3.1" metrics-util = "0.18.0" +eyre = "0.6.12" [dev-dependencies] anyhow = "1.0" diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 734dd7c47a9ae..0000000000000 --- a/src/error.rs +++ /dev/null @@ -1,13 +0,0 @@ -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum Error { - #[error("Invalid arguments: {0}")] - InvalidArgs(String), - #[error("Error Initializing RPC Client: {0}")] - InitRPCClient(String), - #[error("Error Initializing RPC Server: {0}")] - InitRPCServer(String), - #[error("Error Initializing Prometheus Metrics: {0}")] - InitMetrics(String), -} diff --git a/src/main.rs b/src/main.rs index 9741efb086f58..4d8aa819fba72 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ use clap::{arg, ArgGroup, Parser}; use dotenv::dotenv; -use error::Error; use http::{StatusCode, Uri}; use hyper::service::service_fn; use hyper::{server::conn::http1, Request, Response}; @@ -19,8 +18,8 @@ use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; -use server::{EngineApiServer, EthEngineApi, HttpClientWrapper}; -use std::net::AddrParseError; +use server::{HttpClientWrapper, RollupBoostServer}; + use std::sync::Arc; use std::time::Duration; use std::{net::SocketAddr, path::PathBuf}; @@ -29,7 +28,6 @@ use tracing::error; use tracing::{info, Level}; use tracing_subscriber::EnvFilter; -mod error; mod metrics; mod proxy; mod server; @@ -111,10 +109,8 @@ struct Args { l2_timeout: u64, } -type Result = core::result::Result; - #[tokio::main] -async fn main() -> Result<()> { +async fn main() -> eyre::Result<()> { // Load .env file dotenv().ok(); let args: Args = Args::parse(); @@ -144,14 +140,11 @@ async fn main() -> Result<()> { // Build metrics stack Stack::new(recorder) .push(PrefixLayer::new("rollup-boost")) - .install() - .map_err(|e| Error::InitMetrics(e.to_string()))?; + .install()?; // Start the metrics server let metrics_addr = format!("{}:{}", args.metrics_host, args.metrics_port); - let addr: SocketAddr = metrics_addr - .parse() - .map_err(|e: AddrParseError| Error::InitMetrics(e.to_string()))?; + let addr: SocketAddr = metrics_addr.parse()?; tokio::spawn(init_metrics_server(addr, handle)); // Run the metrics server in a separate task Some(Arc::new(ServerMetrics::default())) @@ -168,27 +161,21 @@ async fn main() -> Result<()> { let jwt_secret = match (args.jwt_path, args.jwt_token) { (Some(file), None) => { // Read JWT secret from file - JwtSecret::from_file(&file).map_err(|e| Error::InvalidArgs(e.to_string()))? + JwtSecret::from_file(&file)? } (None, Some(secret)) => { // Use the provided JWT secret - JwtSecret::from_hex(secret).map_err(|e| Error::InvalidArgs(e.to_string()))? + JwtSecret::from_hex(secret)? } _ => { // This case should not happen due to ArgGroup - return Err(Error::InvalidArgs( - "Either jwt_file or jwt_secret must be provided".into(), - )); + eyre::bail!("Either jwt_file or jwt_secret must be provided"); } }; let builder_jwt_secret = match (args.builder_jwt_path, args.builder_jwt_token) { - (Some(file), None) => { - JwtSecret::from_file(&file).map_err(|e| Error::InvalidArgs(e.to_string()))? - } - (None, Some(secret)) => { - JwtSecret::from_hex(secret).map_err(|e| Error::InvalidArgs(e.to_string()))? - } + (Some(file), None) => JwtSecret::from_file(&file)?, + (None, Some(secret)) => JwtSecret::from_hex(secret)?, _ => jwt_secret, }; @@ -199,34 +186,26 @@ async fn main() -> Result<()> { let builder_client = create_client(&args.builder_url, builder_jwt_secret, args.builder_timeout)?; - let eth_engine_api = EthEngineApi::new( + let rollup_boost = RollupBoostServer::new( Arc::new(l2_client), Arc::new(builder_client), args.boost_sync, metrics, ); - let mut module: RpcModule<()> = RpcModule::new(()); - module - .merge(eth_engine_api.into_rpc()) - .map_err(|e| Error::InitRPCServer(e.to_string()))?; + + let module: RpcModule<()> = rollup_boost.try_into()?; // server setup info!("Starting server on :{}", args.rpc_port); - let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new( - args.l2_url - .parse::() - .map_err(|e| Error::InvalidArgs(e.to_string()))?, - )); + let service_builder = + tower::ServiceBuilder::new().layer(ProxyLayer::new(args.l2_url.parse::()?)); let server = Server::builder() .set_http_middleware(service_builder) - .build( - format!("{}:{}", args.rpc_host, args.rpc_port) - .parse::() - .map_err(|e| Error::InitRPCServer(e.to_string()))?, - ) - .await - .map_err(|e| Error::InitRPCServer(e.to_string()))?; + .build(format!("{}:{}", args.rpc_host, args.rpc_port).parse::()?) + .await?; let handle = server.start(module); + + // TODO: handle.stopped().await; Ok(()) @@ -236,7 +215,7 @@ fn create_client( url: &str, jwt_secret: JwtSecret, timeout: u64, -) -> Result>>> { +) -> eyre::Result>>> { // Create a middleware that adds a new JWT token to every request. let auth_layer = AuthClientLayer::new(jwt_secret); let client_middleware = tower::ServiceBuilder::new().layer(auth_layer); @@ -244,8 +223,7 @@ fn create_client( let client = HttpClientBuilder::new() .set_http_middleware(client_middleware) .request_timeout(Duration::from_millis(timeout)) - .build(url) - .map_err(|e| Error::InitRPCClient(e.to_string()))?; + .build(url)?; Ok(HttpClientWrapper::new(client, url.to_string())) } @@ -272,10 +250,8 @@ fn init_tracing(endpoint: &str) { } } -async fn init_metrics_server(addr: SocketAddr, handle: PrometheusHandle) -> Result<()> { - let listener = TcpListener::bind(addr) - .await - .map_err(|e| Error::InitMetrics(e.to_string()))?; +async fn init_metrics_server(addr: SocketAddr, handle: PrometheusHandle) -> eyre::Result<()> { + let listener = TcpListener::bind(addr).await?; info!("Metrics server running on {}", addr); loop { @@ -313,6 +289,7 @@ mod tests { use assert_cmd::Command; use jsonrpsee::core::client::ClientT; use jsonrpsee::http_client::transport::Error as TransportError; + use jsonrpsee::RpcModule; use jsonrpsee::{ core::ClientError, rpc_params, diff --git a/src/proxy.rs b/src/proxy.rs index 5e1e586133b74..d08a2f0fecba3 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -7,7 +7,7 @@ use jsonrpsee::http_client::{HttpBody, HttpRequest, HttpResponse}; use std::task::{Context, Poll}; use std::{future::Future, pin::Pin}; use tower::{Layer, Service}; -use tracing::debug; +use tracing::{debug, info}; const MULTIPLEX_METHODS: [&str; 3] = ["engine_", "eth_sendRawTransaction", "miner_"]; @@ -94,10 +94,14 @@ where .iter() .any(|&m| method.method.starts_with(m)) { + info!(target: "proxy::call", message = "proxying request to rollup-boost server", ?method); + // let rpc server handle engine rpc requests let res = inner.call(req).await.map_err(|e| e.into())?; Ok(res) } else { + info!(target: "proxy::call", message = "forwarding request to l2 client", ?method); + // Modify the URI *req.uri_mut() = target_url; diff --git a/src/server.rs b/src/server.rs index 3cd406d153176..22b1988e05fb6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -4,12 +4,13 @@ use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, }; -use jsonrpsee::core::{async_trait, ClientError, RpcResult}; +use jsonrpsee::core::{async_trait, ClientError, RegisterMethodError, RpcResult}; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::HttpClient; use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::error::INVALID_REQUEST_CODE; use jsonrpsee::types::{ErrorCode, ErrorObject}; +use jsonrpsee::RpcModule; use lru::LruCache; use op_alloy_rpc_jsonrpsee::traits::{MinerApiExtClient, MinerApiExtServer}; use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; @@ -117,6 +118,7 @@ pub trait EthApi { async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult; } +#[derive(Clone)] pub struct HttpClientWrapper>> { pub client: C, pub url: String, @@ -128,7 +130,8 @@ impl HttpClientWrapper { } } -pub struct EthEngineApi { +#[derive(Clone)] +pub struct RollupBoostServer { l2_client: Arc, builder_client: Arc, boost_sync: bool, @@ -136,7 +139,7 @@ pub struct EthEngineApi { payload_trace_context: Arc, } -impl EthEngineApi { +impl RollupBoostServer { pub fn new( l2_client: Arc, builder_client: Arc, @@ -153,8 +156,29 @@ impl EthEngineApi { } } +impl TryInto> for RollupBoostServer +where + Self: EngineApiServer + EthApiServer + MinerApiServer + MinerApiExtServer + Clone, +{ + type Error = RegisterMethodError; + + fn try_into(self) -> Result, Self::Error> { + let mut module: RpcModule<()> = RpcModule::new(()); + module.merge(EngineApiServer::into_rpc(self.clone()))?; + module.merge(EthApiServer::into_rpc(self.clone()))?; + module.merge(MinerApiServer::into_rpc(self.clone()))?; + module.merge(MinerApiExtServer::into_rpc(self))?; + + for method in module.method_names() { + info!(?method, "method registered"); + } + + Ok(module) + } +} + #[async_trait] -impl EthApiServer for EthEngineApi> +impl EthApiServer for RollupBoostServer> where C: EthApiClient + Send + Sync + Clone + 'static, { @@ -217,7 +241,7 @@ pub trait MinerApi { } #[async_trait] -impl MinerApiServer for EthEngineApi> +impl MinerApiServer for RollupBoostServer> where C: MinerApiClient + Send + Sync + Clone + 'static, { @@ -311,12 +335,13 @@ where } #[async_trait] -impl MinerApiExtServer for EthEngineApi> +impl MinerApiExtServer for RollupBoostServer> where C: MinerApiExtClient + Send + Sync + Clone + 'static, { async fn set_max_da_size(&self, max_tx_size: U64, max_block_size: U64) -> RpcResult { debug!( + target: "server::set_max_da_size", message = "received miner_setMaxDASize", ?max_tx_size, ?max_block_size @@ -326,7 +351,7 @@ where let url = self.builder_client.url.clone(); tokio::spawn(async move { builder_client.set_max_da_size(max_tx_size, max_block_size).await.map_err(|e| { - error!(message = "error calling miner_setMaxDASize for builder", "url" =url, "error" = %e); + error!(target: "server::set_max_da_size", message = "error calling miner_setMaxDASize for builder", "url" =url, "error" = %e); }) }); @@ -338,7 +363,10 @@ where { Ok(result) => Ok(result), Err(e) => match e { - ClientError::Call(err) => Err(err), + ClientError::Call(err) => { + error!(target: "server::set_max_da_size", message = "error forwarding miner_setMaxDASize to l2 client", ?err); + Err(err) + } other_error => { error!( message = "error calling miner_setMaxDASize for l2 client", @@ -353,7 +381,7 @@ where } #[async_trait] -impl EngineApiServer for EthEngineApi> +impl EngineApiServer for RollupBoostServer> where C: EngineApiClient + Send + Sync + Clone + 'static, { @@ -620,16 +648,21 @@ mod tests { use std::net::SocketAddr; use std::sync::Mutex; + use crate::proxy::ProxyLayer; + use super::*; use alloy_primitives::hex; + use alloy_primitives::U64; use alloy_primitives::{FixedBytes, U256}; use alloy_rpc_types_engine::{ BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, PayloadStatusEnum, }; + use http::Uri; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; - use jsonrpsee::server::{ServerBuilder, ServerHandle}; + use jsonrpsee::server::{Server, ServerBuilder, ServerHandle}; use jsonrpsee::RpcModule; + use std::sync::Arc; const L2_ADDR: &str = "0.0.0.0:8554"; const BUILDER_ADDR: &str = "0.0.0.0:8555"; @@ -712,7 +745,7 @@ mod tests { .build(format!("http://{BUILDER_ADDR}")) .unwrap(); - let eth_engine_api = EthEngineApi::new( + let rollup_boost_client = RollupBoostServer::new( Arc::new(HttpClientWrapper::new( l2_client, format!("http://{L2_ADDR}"), @@ -726,7 +759,7 @@ mod tests { ); let mut module: RpcModule<()> = RpcModule::new(()); module - .merge(EngineApiServer::into_rpc(eth_engine_api)) + .merge(EngineApiServer::into_rpc(rollup_boost_client)) .unwrap(); let proxy_server = ServerBuilder::default() From 7170a7b0b6fd75ff9110e08474029addd67cb5bc Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 13:06:33 -0500 Subject: [PATCH 055/429] use RpcServerArgs (flashbots/rollup-boost) --- Cargo.lock | 1397 +++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/main.rs | 75 ++- 3 files changed, 1399 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c60c3833b80d..7549a4845212f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,41 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.8.11" @@ -54,6 +89,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "alloy-chains" version = "0.1.43" @@ -144,7 +185,7 @@ dependencies = [ "ethereum_ssz_derive", "once_cell", "serde", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -159,6 +200,18 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-json-abi" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac4b22b3e51cac09fd2adfcc73b55f447b4df669f983c13f7894ec82b607c63f" +dependencies = [ + "alloy-primitives", + "alloy-sol-type-parser", + "serde", + "serde_json", +] + [[package]] name = "alloy-network-primitives" version = "0.7.3" @@ -239,6 +292,18 @@ dependencies = [ "serde", ] +[[package]] +name = "alloy-rpc-types-admin" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f0874a976ccdf83a178ad93b64bec5b8c91a47428d714d544ca70258acfa07b" +dependencies = [ + "alloy-genesis", + "alloy-primitives", + "serde", + "serde_json", +] + [[package]] name = "alloy-rpc-types-debug" version = "0.7.3" @@ -289,6 +354,20 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-rpc-types-trace" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e073ab0e67429c60be281e181731132fd07d82e091c10c29ace6935101034bb" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "serde", + "serde_json", + "thiserror 2.0.4", +] + [[package]] name = "alloy-serde" version = "0.7.3" @@ -348,15 +427,27 @@ dependencies = [ "syn-solidity", ] +[[package]] +name = "alloy-sol-type-parser" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e60b084fe1aef8acecda2743ff2d93c18ff3eb67a2d3b12f62582a1e66ef5e" +dependencies = [ + "serde", + "winnow", +] + [[package]] name = "alloy-sol-types" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9dc0fffe397aa17628160e16b89f704098bf3c9d74d5d369ebc239575936de5" dependencies = [ + "alloy-json-abi", "alloy-primitives", "alloy-sol-macro", "const-hex", + "serde", ] [[package]] @@ -592,6 +683,12 @@ dependencies = [ "rand", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -601,6 +698,12 @@ dependencies = [ "serde", ] +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + [[package]] name = "assert_cmd" version = "2.0.16" @@ -788,12 +891,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -905,6 +1020,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -914,6 +1038,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + [[package]] name = "blst" version = "0.3.13" @@ -947,6 +1080,15 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bstr" version = "1.10.0" @@ -1006,6 +1148,38 @@ dependencies = [ "serde", ] +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.23", + "serde", + "serde_json", + "thiserror 1.0.65", +] + [[package]] name = "cc" version = "1.1.31" @@ -1053,6 +1227,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clang-sys" version = "1.8.1" @@ -1129,6 +1313,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "concat-kdf" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d72c1252426a83be2092dd5884a5f6e3b8e7180f6891b6263d2c21b92ec8816" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "const-hex" version = "1.14.0" @@ -1148,6 +1341,27 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "convert_case" version = "0.6.0" @@ -1173,6 +1387,15 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.14" @@ -1271,9 +1494,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "darling" version = "0.20.10" @@ -1321,6 +1591,44 @@ dependencies = [ "lock_api", "once_cell", "parking_lot_core", + "serde", +] + +[[package]] +name = "data-encoding" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" + +[[package]] +name = "data-encoding-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b16d9d0d88a5273d830dac8b78ceb217ffc9b1d5404e5597a3542515329405b" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1145d32e826a7748b69ee8fc62d3e6355ff7f1051df53141e7048162fc90481b" +dependencies = [ + "data-encoding", + "syn 2.0.90", +] + +[[package]] +name = "delay_map" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df941644b671f05f59433e481ba0d31ac10e3667de725236a4c0d587c496fba1" +dependencies = [ + "futures", + "tokio", + "tokio-util", ] [[package]] @@ -1408,12 +1716,87 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", "crypto-common", "subtle", ] +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "discv5" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e6b70634e26c909d1edbb3142b3eaf3b89da0e52f284f00ca7c80d9901ad9e" +dependencies = [ + "aes", + "aes-gcm", + "alloy-rlp", + "arrayvec", + "ctr", + "delay_map", + "enr", + "fnv", + "futures", + "hashlink", + "hex", + "hkdf", + "lazy_static", + "libp2p-identity", + "lru 0.12.5", + "more-asserts", + "multiaddr", + "parking_lot", + "rand", + "smallvec", + "socket2", + "tokio", + "tracing", + "uint 0.10.0", + "zeroize", +] + [[package]] name = "doc-comment" version = "0.3.3" @@ -1452,6 +1835,31 @@ dependencies = [ "spki", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand_core", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.13.0" @@ -1501,13 +1909,29 @@ dependencies = [ "alloy-rlp", "base64 0.21.7", "bytes", + "ed25519-dalek", "hex", + "k256", "log", "rand", + "secp256k1", + "serde", "sha3", "zeroize", ] +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "enumn" version = "0.1.14" @@ -1613,6 +2037,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "filetime" version = "0.2.25" @@ -1818,6 +2248,7 @@ version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ + "serde", "typenum", "version_check", "zeroize", @@ -1836,6 +2267,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gimli" version = "0.31.1" @@ -1944,6 +2385,9 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] [[package]] name = "hashbrown" @@ -1951,10 +2395,21 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ + "allocator-api2", + "equivalent", "foldhash", "serde", ] +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] + [[package]] name = "heck" version = "0.5.0" @@ -1982,6 +2437,25 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1991,6 +2465,17 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] + [[package]] name = "home" version = "0.5.9" @@ -2000,6 +2485,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "http" version = "0.2.12" @@ -2074,6 +2570,22 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "humantime-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" +dependencies = [ + "humantime", + "serde", +] + [[package]] name = "hyper" version = "1.5.0" @@ -2191,6 +2703,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -2201,6 +2723,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "if-addrs" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78a89907582615b19f6f0da1af18abf6ff08be259395669b834b057a7ee92d8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "impl-codec" version = "0.6.0" @@ -2289,6 +2821,28 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + [[package]] name = "ipnet" version = "2.10.1" @@ -2578,7 +3132,8 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2", + "sha2 0.10.8", + "signature", ] [[package]] @@ -2600,6 +3155,21 @@ dependencies = [ "sha3-asm", ] +[[package]] +name = "konst" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330f0e13e6483b8c34885f7e6c9f19b1a7bd449c673fbb948a51c99d66ef74f4" +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 = "kqueue" version = "1.0.8" @@ -2657,6 +3227,25 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +[[package]] +name = "libp2p-identity" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b5621d159b32282eac446bed6670c39c7dc68a200a992d8f056afa0066f6d" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "hkdf", + "libsecp256k1", + "multihash", + "quick-protobuf", + "sha2 0.10.8", + "thiserror 1.0.65", + "tracing", + "zeroize", +] + [[package]] name = "libproc" version = "0.14.10" @@ -2679,6 +3268,70 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linked_hash_set" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae85b5be22d9843c80e5fc80e9b64c8a3b1f98f867c709956eca3efff4e92e2" +dependencies = [ + "linked-hash-map", + "serde", +] + [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -2711,6 +3364,24 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown 0.15.0", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "lz4_flex" version = "0.11.3" @@ -2726,6 +3397,12 @@ dependencies = [ "libc", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -2889,30 +3566,76 @@ dependencies = [ ] [[package]] -name = "mirai-annotations" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" - -[[package]] -name = "modular-bitfield" -version = "0.11.2" +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + +[[package]] +name = "modular-bitfield" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +dependencies = [ + "modular-bitfield-impl", + "static_assertions", +] + +[[package]] +name = "modular-bitfield-impl" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "more-asserts" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" + +[[package]] +name = "multiaddr" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "libp2p-identity", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a53d79ba8304ac1c4f9eb3b9d281f21f7be9d4626f72ce7df4ad8fbde4f38a74" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" dependencies = [ - "modular-bitfield-impl", - "static_assertions", + "base-x", + "data-encoding", + "data-encoding-macro", ] [[package]] -name = "modular-bitfield-impl" -version = "0.11.2" +name = "multihash" +version = "0.19.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" +checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "core2", + "unsigned-varint", ] [[package]] @@ -2971,6 +3694,7 @@ dependencies = [ "libc", "log", "mio 0.8.11", + "serde", "walkdir", "windows-sys 0.48.0", ] @@ -3104,6 +3828,15 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + [[package]] name = "nybbles" version = "0.2.1" @@ -3240,6 +3973,12 @@ dependencies = [ "thiserror 2.0.4", ] +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + [[package]] name = "openssl" version = "0.10.68" @@ -3367,6 +4106,12 @@ dependencies = [ "tokio-stream", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "ordered-float" version = "4.4.0" @@ -3391,7 +4136,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -3535,6 +4280,18 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "portable-atomic" version = "1.9.0" @@ -3613,7 +4370,7 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", - "uint", + "uint 0.9.5", ] [[package]] @@ -3753,6 +4510,15 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + [[package]] name = "quote" version = "1.0.37" @@ -3856,6 +4622,17 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror 1.0.65", +] + [[package]] name = "regex" version = "1.11.1" @@ -3944,6 +4721,16 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + [[package]] name = "reth-basic-payload-builder" version = "1.1.2" @@ -4033,6 +4820,23 @@ dependencies = [ "serde_json", ] +[[package]] +name = "reth-cli-util" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "cfg-if", + "eyre", + "libc", + "rand", + "reth-fs-util", + "secp256k1", + "serde", + "thiserror 2.0.4", +] + [[package]] name = "reth-codecs" version = "1.1.2" @@ -4061,6 +4865,20 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "reth-config" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "eyre", + "humantime-serde", + "reth-network-types", + "reth-prune-types", + "reth-stages-types", + "serde", + "toml", +] + [[package]] name = "reth-consensus" version = "1.1.2" @@ -4160,6 +4978,111 @@ dependencies = [ "serde", ] +[[package]] +name = "reth-discv4" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "discv5", + "enr", + "generic-array", + "itertools 0.13.0", + "parking_lot", + "rand", + "reth-ethereum-forks", + "reth-net-banlist", + "reth-net-nat", + "reth-network-peers", + "schnellru", + "secp256k1", + "serde", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-discv5" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "alloy-rlp", + "derive_more", + "discv5", + "enr", + "futures", + "itertools 0.13.0", + "metrics", + "rand", + "reth-chainspec", + "reth-ethereum-forks", + "reth-metrics", + "reth-network-peers", + "secp256k1", + "thiserror 2.0.4", + "tokio", + "tracing", +] + +[[package]] +name = "reth-dns-discovery" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", + "data-encoding", + "enr", + "linked_hash_set", + "parking_lot", + "reth-ethereum-forks", + "reth-network-peers", + "reth-tokio-util", + "schnellru", + "secp256k1", + "serde", + "serde_with", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tracing", + "trust-dns-resolver", +] + +[[package]] +name = "reth-ecies" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "aes", + "alloy-primitives", + "alloy-rlp", + "block-padding", + "byteorder", + "cipher", + "concat-kdf", + "ctr", + "digest 0.10.7", + "futures", + "generic-array", + "hmac 0.12.1", + "pin-project", + "rand", + "reth-network-peers", + "secp256k1", + "sha2 0.10.8", + "sha3", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", + "typenum", +] + [[package]] name = "reth-engine-primitives" version = "1.1.2" @@ -4194,6 +5117,34 @@ dependencies = [ "thiserror 2.0.4", ] +[[package]] +name = "reth-eth-wire" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-chains", + "alloy-primitives", + "alloy-rlp", + "bytes", + "derive_more", + "futures", + "pin-project", + "reth-codecs", + "reth-ecies", + "reth-eth-wire-types", + "reth-ethereum-forks", + "reth-metrics", + "reth-network-peers", + "reth-primitives-traits", + "serde", + "snap", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", +] + [[package]] name = "reth-eth-wire-types" version = "1.1.2" @@ -4232,7 +5183,7 @@ dependencies = [ "reth-primitives", "reth-rpc-types-compat", "serde", - "sha2", + "sha2 0.10.8", ] [[package]] @@ -4353,16 +5304,111 @@ name = "reth-metrics" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "futures", "metrics", "metrics-derive", + "tokio", + "tokio-util", +] + +[[package]] +name = "reth-net-banlist" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-primitives", +] + +[[package]] +name = "reth-net-nat" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "futures-util", + "if-addrs", + "reqwest", + "serde_with", + "thiserror 2.0.4", + "tokio", + "tracing", +] + +[[package]] +name = "reth-network" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "aquamarine", + "auto_impl", + "derive_more", + "discv5", + "enr", + "futures", + "itertools 0.13.0", + "metrics", + "parking_lot", + "pin-project", + "rand", + "reth-chainspec", + "reth-consensus", + "reth-discv4", + "reth-discv5", + "reth-dns-discovery", + "reth-ecies", + "reth-eth-wire", + "reth-eth-wire-types", + "reth-ethereum-forks", + "reth-fs-util", + "reth-metrics", + "reth-net-banlist", + "reth-network-api", + "reth-network-p2p", + "reth-network-peers", + "reth-network-types", + "reth-primitives", + "reth-primitives-traits", + "reth-provider", + "reth-storage-api", + "reth-tasks", + "reth-tokio-util", + "reth-transaction-pool", + "rustc-hash 2.0.0", + "schnellru", + "secp256k1", + "serde", + "smallvec", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", ] [[package]] -name = "reth-net-banlist" +name = "reth-network-api" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-primitives", + "alloy-rpc-types-admin", + "auto_impl", + "derive_more", + "enr", + "futures", + "reth-eth-wire-types", + "reth-ethereum-forks", + "reth-network-p2p", + "reth-network-peers", + "reth-network-types", + "reth-tokio-util", + "serde", + "thiserror 2.0.4", + "tokio", + "tokio-stream", ] [[package]] @@ -4398,6 +5444,7 @@ dependencies = [ "secp256k1", "serde_with", "thiserror 2.0.4", + "tokio", "url", ] @@ -4406,9 +5453,11 @@ name = "reth-network-types" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ + "humantime-serde", "reth-ethereum-forks", "reth-net-banlist", "reth-network-peers", + "serde", "serde_json", "tracing", ] @@ -4430,6 +5479,56 @@ dependencies = [ "zstd", ] +[[package]] +name = "reth-node-core" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "clap", + "const_format", + "derive_more", + "dirs-next", + "eyre", + "futures", + "humantime", + "rand", + "reth-chainspec", + "reth-cli-util", + "reth-config", + "reth-consensus", + "reth-db", + "reth-discv4", + "reth-discv5", + "reth-ethereum-forks", + "reth-net-nat", + "reth-network", + "reth-network-p2p", + "reth-network-peers", + "reth-primitives", + "reth-primitives-traits", + "reth-prune-types", + "reth-rpc-eth-types", + "reth-rpc-server-types", + "reth-rpc-types-compat", + "reth-stages-types", + "reth-storage-api", + "reth-storage-errors", + "reth-tracing", + "reth-transaction-pool", + "secp256k1", + "serde", + "shellexpand", + "strum", + "thiserror 2.0.4", + "toml", + "tracing", + "vergen", +] + [[package]] name = "reth-node-types" version = "1.1.2" @@ -4555,7 +5654,7 @@ dependencies = [ "reth-rpc-types-compat", "reth-transaction-pool", "revm", - "sha2", + "sha2 0.10.8", "thiserror 2.0.4", "tracing", ] @@ -4782,6 +5881,48 @@ dependencies = [ "revm", ] +[[package]] +name = "reth-rpc-eth-types" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-sol-types", + "derive_more", + "futures", + "itertools 0.13.0", + "jsonrpsee-core", + "jsonrpsee-types", + "metrics", + "rand", + "reth-chain-state", + "reth-chainspec", + "reth-errors", + "reth-execution-types", + "reth-metrics", + "reth-primitives", + "reth-primitives-traits", + "reth-revm", + "reth-rpc-server-types", + "reth-rpc-types-compat", + "reth-storage-api", + "reth-tasks", + "reth-transaction-pool", + "reth-trie", + "revm", + "revm-inspectors", + "revm-primitives", + "schnellru", + "serde", + "thiserror 2.0.4", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "reth-rpc-layer" version = "1.1.2" @@ -4796,6 +5937,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "reth-rpc-server-types" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-rpc-types-engine", + "jsonrpsee-core", + "jsonrpsee-types", + "reth-errors", + "reth-network-api", + "serde", + "strum", +] + [[package]] name = "reth-rpc-types-compat" version = "1.1.2" @@ -4891,6 +6048,16 @@ dependencies = [ "tracing-futures", ] +[[package]] +name = "reth-tokio-util" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "reth-tracing" version = "1.1.2" @@ -5006,6 +6173,7 @@ dependencies = [ "reth-storage-errors", "reth-trie", "revm", + "serde", "tracing", ] @@ -5039,6 +6207,23 @@ dependencies = [ "serde_json", ] +[[package]] +name = "revm-inspectors" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7f5f8a2deafb3c76f357bbf9e71b73bddb915c4994bbbe3208fbfbe8fc7f8e" +dependencies = [ + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-rpc-types-trace", + "alloy-sol-types", + "anstyle", + "colorchoice", + "revm", + "serde_json", + "thiserror 1.0.65", +] + [[package]] name = "revm-interpreter" version = "14.0.0" @@ -5064,7 +6249,7 @@ dependencies = [ "revm-primitives", "ripemd", "secp256k1", - "sha2", + "sha2 0.10.8", "substrate-bn", ] @@ -5094,7 +6279,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac", + "hmac 0.12.1", "subtle", ] @@ -5179,7 +6364,7 @@ dependencies = [ "hyper", "hyper-util", "jsonrpsee", - "lru", + "lru 0.10.1", "metrics", "metrics-derive", "metrics-exporter-prometheus", @@ -5194,6 +6379,7 @@ dependencies = [ "opentelemetry_sdk", "predicates", "reqwest", + "reth-node-core", "reth-optimism-payload-builder", "reth-payload-primitives", "reth-rpc-layer", @@ -5529,6 +6715,9 @@ name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] [[package]] name = "semver-parser" @@ -5589,6 +6778,15 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -5642,6 +6840,19 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + [[package]] name = "sha2" version = "0.10.8" @@ -5682,6 +6893,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shellexpand" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +dependencies = [ + "dirs", +] + [[package]] name = "shlex" version = "1.3.0" @@ -6019,7 +7239,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", "serde", "time-core", @@ -6139,14 +7361,30 @@ dependencies = [ "futures-io", "futures-sink", "pin-project-lite", + "slab", "tokio", ] +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -6155,6 +7393,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.6.0", + "serde", + "serde_spanned", "toml_datetime", "winnow", ] @@ -6404,6 +7644,53 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "trust-dns-proto" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "smallvec", + "thiserror 1.0.65", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" +dependencies = [ + "cfg-if", + "futures-util", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "serde", + "smallvec", + "thiserror 1.0.65", + "tokio", + "tracing", + "trust-dns-proto", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -6434,6 +7721,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "uint" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + [[package]] name = "unarray" version = "0.1.4" @@ -6479,6 +7778,16 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "unsigned-varint" version = "0.8.0" @@ -6498,7 +7807,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", ] @@ -6529,6 +7838,20 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "vergen" +version = "8.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" +dependencies = [ + "anyhow", + "cargo_metadata", + "cfg-if", + "regex", + "rustversion", + "time", +] + [[package]] name = "version_check" version = "0.9.5" @@ -6677,6 +8000,12 @@ dependencies = [ "rustix", ] +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + [[package]] name = "winapi" version = "0.3.9" @@ -7002,6 +8331,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index be7913aefee84..a4e0d68a5ac56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,7 @@ reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222", features = [ "optimism", ] } +reth-node-core = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" } reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" } opentelemetry = { version = "0.26.0", features = ["trace"] } opentelemetry-http = "0.26.0" diff --git a/src/main.rs b/src/main.rs index 4d8aa819fba72..2dc4bd0001081 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use opentelemetry_sdk::propagation::TraceContextPropagator; use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; +use reth_node_core::args::RpcServerArgs; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use server::{HttpClientWrapper, RollupBoostServer}; @@ -36,29 +37,11 @@ mod server; #[clap(author, version, about)] #[clap(group(ArgGroup::new("jwt").required(true).multiple(false).args(&["jwt_token", "jwt_path"])))] struct Args { - /// JWT token for authentication - #[arg(long, env)] - jwt_token: Option, + #[clap(flatten)] + builder: RpcServerArgs, - /// Path to the JWT secret file - #[arg(long, env)] - jwt_path: Option, - - /// JWT token for authentication for the builder - #[arg(long, env)] - builder_jwt_token: Option, - - /// Path to the JWT secret file for the builder - #[arg(long, env)] - builder_jwt_path: Option, - - /// URL of the local l2 execution engine - #[arg(long, env, default_value = "http://localhost:8551")] - l2_url: String, - - /// URL of the builder execution engine - #[arg(long, env)] - builder_url: String, + #[clap(flatten)] + l2_client: RpcServerArgs, /// Use the proposer to sync the builder node #[arg(long, env, default_value = "false")] @@ -157,34 +140,35 @@ async fn main() -> eyre::Result<()> { init_tracing(&args.otlp_endpoint); } - // Handle JWT secret - let jwt_secret = match (args.jwt_path, args.jwt_token) { - (Some(file), None) => { - // Read JWT secret from file - JwtSecret::from_file(&file)? - } - (None, Some(secret)) => { - // Use the provided JWT secret - JwtSecret::from_hex(secret)? - } - _ => { - // This case should not happen due to ArgGroup - eyre::bail!("Either jwt_file or jwt_secret must be provided"); - } + let l2_jwt_secret = if let Some(jwt_secret) = args.l2_client.rpc_jwtsecret { + jwt_secret + } else if let Some(path) = args.l2_client.auth_jwtsecret { + JwtSecret::from_file(&path)? + } else { + eyre::bail!("Either l2_client.rpc_jwtsecret or l2_client.auth_jwtsecret must be provided"); }; - let builder_jwt_secret = match (args.builder_jwt_path, args.builder_jwt_token) { - (Some(file), None) => JwtSecret::from_file(&file)?, - (None, Some(secret)) => JwtSecret::from_hex(secret)?, - _ => jwt_secret, + let builder_jwt_secret = if let Some(jwt_secret) = args.builder.rpc_jwtsecret { + jwt_secret + } else if let Some(path) = args.builder.auth_jwtsecret { + JwtSecret::from_file(&path)? + } else { + eyre::bail!("Either builder.rpc_jwtsecret or builder.auth_jwtsecret must be provided"); }; // Initialize the l2 client - let l2_client = create_client(&args.l2_url, jwt_secret, args.l2_timeout)?; + let l2_client = create_client( + &args.l2_client.http_addr.to_string(), + l2_jwt_secret, + args.l2_timeout, + )?; // Initialize the builder client - let builder_client = - create_client(&args.builder_url, builder_jwt_secret, args.builder_timeout)?; + let builder_client = create_client( + &args.builder.http_addr.to_string(), + builder_jwt_secret, + args.builder_timeout, + )?; let rollup_boost = RollupBoostServer::new( Arc::new(l2_client), @@ -197,8 +181,9 @@ async fn main() -> eyre::Result<()> { // server setup info!("Starting server on :{}", args.rpc_port); - let service_builder = - tower::ServiceBuilder::new().layer(ProxyLayer::new(args.l2_url.parse::()?)); + let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new( + args.l2_client.http_addr.to_string().parse::()?, + )); let server = Server::builder() .set_http_middleware(service_builder) .build(format!("{}:{}", args.rpc_host, args.rpc_port).parse::()?) From 2b63838e09dff027173b075b7803403d517b0cfb Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 13:22:10 -0500 Subject: [PATCH 056/429] create RPCClient (flashbots/rollup-boost) --- src/server.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/server.rs b/src/server.rs index 22b1988e05fb6..5343bf4221c8c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -130,6 +130,11 @@ impl HttpClientWrapper { } } +pub struct RpcClient { + pub client: HttpClient, + pub auth_client: HttpClient>, +} + #[derive(Clone)] pub struct RollupBoostServer { l2_client: Arc, From 1b288dd8b87f0d596df067459fdf529c13f007b2 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 13:51:23 -0500 Subject: [PATCH 057/429] update rpc client args (flashbots/rollup-boost) --- Cargo.lock | 1461 ++------------------------------------------- Cargo.toml | 1 - src/main.rs | 16 +- src/rpc_client.rs | 100 ++++ src/server.rs | 11 +- 5 files changed, 178 insertions(+), 1411 deletions(-) create mode 100644 src/rpc_client.rs diff --git a/Cargo.lock b/Cargo.lock index 7549a4845212f..1c60c3833b80d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,41 +17,6 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - [[package]] name = "ahash" version = "0.8.11" @@ -89,12 +54,6 @@ dependencies = [ "alloc-no-stdlib", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "alloy-chains" version = "0.1.43" @@ -185,7 +144,7 @@ dependencies = [ "ethereum_ssz_derive", "once_cell", "serde", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -200,18 +159,6 @@ dependencies = [ "serde", ] -[[package]] -name = "alloy-json-abi" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac4b22b3e51cac09fd2adfcc73b55f447b4df669f983c13f7894ec82b607c63f" -dependencies = [ - "alloy-primitives", - "alloy-sol-type-parser", - "serde", - "serde_json", -] - [[package]] name = "alloy-network-primitives" version = "0.7.3" @@ -292,18 +239,6 @@ dependencies = [ "serde", ] -[[package]] -name = "alloy-rpc-types-admin" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f0874a976ccdf83a178ad93b64bec5b8c91a47428d714d544ca70258acfa07b" -dependencies = [ - "alloy-genesis", - "alloy-primitives", - "serde", - "serde_json", -] - [[package]] name = "alloy-rpc-types-debug" version = "0.7.3" @@ -354,20 +289,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "alloy-rpc-types-trace" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e073ab0e67429c60be281e181731132fd07d82e091c10c29ace6935101034bb" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-serde", - "serde", - "serde_json", - "thiserror 2.0.4", -] - [[package]] name = "alloy-serde" version = "0.7.3" @@ -427,27 +348,15 @@ dependencies = [ "syn-solidity", ] -[[package]] -name = "alloy-sol-type-parser" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e60b084fe1aef8acecda2743ff2d93c18ff3eb67a2d3b12f62582a1e66ef5e" -dependencies = [ - "serde", - "winnow", -] - [[package]] name = "alloy-sol-types" version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9dc0fffe397aa17628160e16b89f704098bf3c9d74d5d369ebc239575936de5" dependencies = [ - "alloy-json-abi", "alloy-primitives", "alloy-sol-macro", "const-hex", - "serde", ] [[package]] @@ -683,12 +592,6 @@ dependencies = [ "rand", ] -[[package]] -name = "arrayref" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" - [[package]] name = "arrayvec" version = "0.7.6" @@ -698,12 +601,6 @@ dependencies = [ "serde", ] -[[package]] -name = "asn1_der" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" - [[package]] name = "assert_cmd" version = "2.0.16" @@ -891,24 +788,12 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - [[package]] name = "base16ct" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -1020,15 +905,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -1038,15 +914,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" -dependencies = [ - "generic-array", -] - [[package]] name = "blst" version = "0.3.13" @@ -1080,15 +947,6 @@ dependencies = [ "alloc-stdlib", ] -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - [[package]] name = "bstr" version = "1.10.0" @@ -1148,38 +1006,6 @@ dependencies = [ "serde", ] -[[package]] -name = "camino" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-platform" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver 1.0.23", - "serde", - "serde_json", - "thiserror 1.0.65", -] - [[package]] name = "cc" version = "1.1.31" @@ -1227,16 +1053,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - [[package]] name = "clang-sys" version = "1.8.1" @@ -1313,15 +1129,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "concat-kdf" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d72c1252426a83be2092dd5884a5f6e3b8e7180f6891b6263d2c21b92ec8816" -dependencies = [ - "digest 0.10.7", -] - [[package]] name = "const-hex" version = "1.14.0" @@ -1341,27 +1148,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "const_format" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" -dependencies = [ - "const_format_proc_macros", - "konst", -] - -[[package]] -name = "const_format_proc_macros" -version = "0.2.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - [[package]] name = "convert_case" version = "0.6.0" @@ -1387,15 +1173,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - [[package]] name = "cpufeatures" version = "0.2.14" @@ -1494,56 +1271,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest 0.10.7", - "fiat-crypto", - "rustc_version 0.4.1", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "darling" version = "0.20.10" @@ -1591,44 +1321,6 @@ dependencies = [ "lock_api", "once_cell", "parking_lot_core", - "serde", -] - -[[package]] -name = "data-encoding" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" - -[[package]] -name = "data-encoding-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b16d9d0d88a5273d830dac8b78ceb217ffc9b1d5404e5597a3542515329405b" -dependencies = [ - "data-encoding", - "data-encoding-macro-internal", -] - -[[package]] -name = "data-encoding-macro-internal" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145d32e826a7748b69ee8fc62d3e6355ff7f1051df53141e7048162fc90481b" -dependencies = [ - "data-encoding", - "syn 2.0.90", -] - -[[package]] -name = "delay_map" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df941644b671f05f59433e481ba0d31ac10e3667de725236a4c0d587c496fba1" -dependencies = [ - "futures", - "tokio", - "tokio-util", ] [[package]] @@ -1716,116 +1408,41 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "const-oid", "crypto-common", "subtle", ] [[package]] -name = "dirs" -version = "5.0.1" +name = "doc-comment" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] -name = "dirs-next" -version = "2.0.0" +name = "dotenv" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" [[package]] -name = "dirs-sys" -version = "0.4.1" +name = "dunce" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.48.0", -] +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "dyn-clone" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] -name = "discv5" -version = "0.8.0" +name = "ecdsa" +version = "0.16.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e6b70634e26c909d1edbb3142b3eaf3b89da0e52f284f00ca7c80d9901ad9e" -dependencies = [ - "aes", - "aes-gcm", - "alloy-rlp", - "arrayvec", - "ctr", - "delay_map", - "enr", - "fnv", - "futures", - "hashlink", - "hex", - "hkdf", - "lazy_static", - "libp2p-identity", - "lru 0.12.5", - "more-asserts", - "multiaddr", - "parking_lot", - "rand", - "smallvec", - "socket2", - "tokio", - "tracing", - "uint 0.10.0", - "zeroize", -] - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - -[[package]] -name = "dotenv" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "dyn-clone" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", "digest 0.10.7", @@ -1835,31 +1452,6 @@ dependencies = [ "spki", ] -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand_core", - "serde", - "sha2 0.10.8", - "subtle", - "zeroize", -] - [[package]] name = "either" version = "1.13.0" @@ -1909,29 +1501,13 @@ dependencies = [ "alloy-rlp", "base64 0.21.7", "bytes", - "ed25519-dalek", "hex", - "k256", "log", "rand", - "secp256k1", - "serde", "sha3", "zeroize", ] -[[package]] -name = "enum-as-inner" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.90", -] - [[package]] name = "enumn" version = "0.1.14" @@ -2037,12 +1613,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - [[package]] name = "filetime" version = "0.2.25" @@ -2248,7 +1818,6 @@ version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "serde", "typenum", "version_check", "zeroize", @@ -2267,16 +1836,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "ghash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "gimli" version = "0.31.1" @@ -2385,9 +1944,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] [[package]] name = "hashbrown" @@ -2395,21 +1951,10 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" dependencies = [ - "allocator-api2", - "equivalent", "foldhash", "serde", ] -[[package]] -name = "hashlink" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" -dependencies = [ - "hashbrown 0.14.5", -] - [[package]] name = "heck" version = "0.5.0" @@ -2437,25 +1982,6 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" -[[package]] -name = "hkdf" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - [[package]] name = "hmac" version = "0.12.1" @@ -2465,17 +1991,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "hmac-drbg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" -dependencies = [ - "digest 0.9.0", - "generic-array", - "hmac 0.8.1", -] - [[package]] name = "home" version = "0.5.9" @@ -2485,17 +2000,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi", -] - [[package]] name = "http" version = "0.2.12" @@ -2570,22 +2074,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "humantime-serde" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c" -dependencies = [ - "humantime", - "serde", -] - [[package]] name = "hyper" version = "1.5.0" @@ -2703,16 +2191,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.5.0" @@ -2723,16 +2201,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "if-addrs" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78a89907582615b19f6f0da1af18abf6ff08be259395669b834b057a7ee92d8" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "impl-codec" version = "0.6.0" @@ -2821,28 +2289,6 @@ dependencies = [ "libc", ] -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "ipconfig" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" -dependencies = [ - "socket2", - "widestring", - "windows-sys 0.48.0", - "winreg", -] - [[package]] name = "ipnet" version = "2.10.1" @@ -3132,8 +2578,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.8", - "signature", + "sha2", ] [[package]] @@ -3155,21 +2600,6 @@ dependencies = [ "sha3-asm", ] -[[package]] -name = "konst" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330f0e13e6483b8c34885f7e6c9f19b1a7bd449c673fbb948a51c99d66ef74f4" -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 = "kqueue" version = "1.0.8" @@ -3227,25 +2657,6 @@ version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" -[[package]] -name = "libp2p-identity" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b5621d159b32282eac446bed6670c39c7dc68a200a992d8f056afa0066f6d" -dependencies = [ - "asn1_der", - "bs58", - "ed25519-dalek", - "hkdf", - "libsecp256k1", - "multihash", - "quick-protobuf", - "sha2 0.10.8", - "thiserror 1.0.65", - "tracing", - "zeroize", -] - [[package]] name = "libproc" version = "0.14.10" @@ -3268,70 +2679,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "libsecp256k1" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" -dependencies = [ - "arrayref", - "base64 0.13.1", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand", - "serde", - "sha2 0.9.9", - "typenum", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linked_hash_set" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae85b5be22d9843c80e5fc80e9b64c8a3b1f98f867c709956eca3efff4e92e2" -dependencies = [ - "linked-hash-map", - "serde", -] - [[package]] name = "linux-raw-sys" version = "0.4.14" @@ -3364,24 +2711,6 @@ dependencies = [ "hashbrown 0.13.2", ] -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.0", -] - -[[package]] -name = "lru-cache" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -dependencies = [ - "linked-hash-map", -] - [[package]] name = "lz4_flex" version = "0.11.3" @@ -3397,12 +2726,6 @@ dependencies = [ "libc", ] -[[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - [[package]] name = "matchers" version = "0.1.0" @@ -3593,73 +2916,27 @@ dependencies = [ ] [[package]] -name = "more-asserts" -version = "0.3.1" +name = "native-tls" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] [[package]] -name = "multiaddr" -version = "0.18.2" +name = "nibble_vec" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6351f60b488e04c1d21bc69e56b89cb3f5e8f5d22557d6e8031bdfd79b6961" -dependencies = [ - "arrayref", - "byteorder", - "data-encoding", - "libp2p-identity", - "multibase", - "multihash", - "percent-encoding", - "serde", - "static_assertions", - "unsigned-varint", - "url", -] - -[[package]] -name = "multibase" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" -dependencies = [ - "base-x", - "data-encoding", - "data-encoding-macro", -] - -[[package]] -name = "multihash" -version = "0.19.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" -dependencies = [ - "core2", - "unsigned-varint", -] - -[[package]] -name = "native-tls" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" dependencies = [ "smallvec", ] @@ -3694,7 +2971,6 @@ dependencies = [ "libc", "log", "mio 0.8.11", - "serde", "walkdir", "windows-sys 0.48.0", ] @@ -3828,15 +3104,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - [[package]] name = "nybbles" version = "0.2.1" @@ -3973,12 +3240,6 @@ dependencies = [ "thiserror 2.0.4", ] -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - [[package]] name = "openssl" version = "0.10.68" @@ -4106,12 +3367,6 @@ dependencies = [ "tokio-stream", ] -[[package]] -name = "option-ext" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" - [[package]] name = "ordered-float" version = "4.4.0" @@ -4136,7 +3391,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -4280,18 +3535,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" -[[package]] -name = "polyval" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - [[package]] name = "portable-atomic" version = "1.9.0" @@ -4370,7 +3613,7 @@ checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" dependencies = [ "fixed-hash", "impl-codec", - "uint 0.9.5", + "uint", ] [[package]] @@ -4510,15 +3753,6 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" -[[package]] -name = "quick-protobuf" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" -dependencies = [ - "byteorder", -] - [[package]] name = "quote" version = "1.0.37" @@ -4622,17 +3856,6 @@ dependencies = [ "bitflags 2.6.0", ] -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror 1.0.65", -] - [[package]] name = "regex" version = "1.11.1" @@ -4721,16 +3944,6 @@ dependencies = [ "windows-registry", ] -[[package]] -name = "resolv-conf" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" -dependencies = [ - "hostname", - "quick-error", -] - [[package]] name = "reth-basic-payload-builder" version = "1.1.2" @@ -4820,23 +4033,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "reth-cli-util" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "cfg-if", - "eyre", - "libc", - "rand", - "reth-fs-util", - "secp256k1", - "serde", - "thiserror 2.0.4", -] - [[package]] name = "reth-codecs" version = "1.1.2" @@ -4865,20 +4061,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "reth-config" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "eyre", - "humantime-serde", - "reth-network-types", - "reth-prune-types", - "reth-stages-types", - "serde", - "toml", -] - [[package]] name = "reth-consensus" version = "1.1.2" @@ -4978,111 +4160,6 @@ dependencies = [ "serde", ] -[[package]] -name = "reth-discv4" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "discv5", - "enr", - "generic-array", - "itertools 0.13.0", - "parking_lot", - "rand", - "reth-ethereum-forks", - "reth-net-banlist", - "reth-net-nat", - "reth-network-peers", - "schnellru", - "secp256k1", - "serde", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tracing", -] - -[[package]] -name = "reth-discv5" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", - "alloy-rlp", - "derive_more", - "discv5", - "enr", - "futures", - "itertools 0.13.0", - "metrics", - "rand", - "reth-chainspec", - "reth-ethereum-forks", - "reth-metrics", - "reth-network-peers", - "secp256k1", - "thiserror 2.0.4", - "tokio", - "tracing", -] - -[[package]] -name = "reth-dns-discovery" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", - "data-encoding", - "enr", - "linked_hash_set", - "parking_lot", - "reth-ethereum-forks", - "reth-network-peers", - "reth-tokio-util", - "schnellru", - "secp256k1", - "serde", - "serde_with", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tracing", - "trust-dns-resolver", -] - -[[package]] -name = "reth-ecies" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "aes", - "alloy-primitives", - "alloy-rlp", - "block-padding", - "byteorder", - "cipher", - "concat-kdf", - "ctr", - "digest 0.10.7", - "futures", - "generic-array", - "hmac 0.12.1", - "pin-project", - "rand", - "reth-network-peers", - "secp256k1", - "sha2 0.10.8", - "sha3", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", - "typenum", -] - [[package]] name = "reth-engine-primitives" version = "1.1.2" @@ -5117,34 +4194,6 @@ dependencies = [ "thiserror 2.0.4", ] -[[package]] -name = "reth-eth-wire" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-chains", - "alloy-primitives", - "alloy-rlp", - "bytes", - "derive_more", - "futures", - "pin-project", - "reth-codecs", - "reth-ecies", - "reth-eth-wire-types", - "reth-ethereum-forks", - "reth-metrics", - "reth-network-peers", - "reth-primitives-traits", - "serde", - "snap", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", -] - [[package]] name = "reth-eth-wire-types" version = "1.1.2" @@ -5183,7 +4232,7 @@ dependencies = [ "reth-primitives", "reth-rpc-types-compat", "serde", - "sha2 0.10.8", + "sha2", ] [[package]] @@ -5289,126 +4338,31 @@ dependencies = [ "thiserror 2.0.4", "tracing", ] - -[[package]] -name = "reth-mdbx-sys" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "bindgen 0.70.1", - "cc", -] - -[[package]] -name = "reth-metrics" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "futures", - "metrics", - "metrics-derive", - "tokio", - "tokio-util", -] - -[[package]] -name = "reth-net-banlist" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-primitives", -] - -[[package]] -name = "reth-net-nat" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "futures-util", - "if-addrs", - "reqwest", - "serde_with", - "thiserror 2.0.4", - "tokio", - "tracing", -] - -[[package]] -name = "reth-network" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rlp", - "aquamarine", - "auto_impl", - "derive_more", - "discv5", - "enr", - "futures", - "itertools 0.13.0", - "metrics", - "parking_lot", - "pin-project", - "rand", - "reth-chainspec", - "reth-consensus", - "reth-discv4", - "reth-discv5", - "reth-dns-discovery", - "reth-ecies", - "reth-eth-wire", - "reth-eth-wire-types", - "reth-ethereum-forks", - "reth-fs-util", - "reth-metrics", - "reth-net-banlist", - "reth-network-api", - "reth-network-p2p", - "reth-network-peers", - "reth-network-types", - "reth-primitives", - "reth-primitives-traits", - "reth-provider", - "reth-storage-api", - "reth-tasks", - "reth-tokio-util", - "reth-transaction-pool", - "rustc-hash 2.0.0", - "schnellru", - "secp256k1", - "serde", - "smallvec", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tokio-util", - "tracing", + +[[package]] +name = "reth-mdbx-sys" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "bindgen 0.70.1", + "cc", +] + +[[package]] +name = "reth-metrics" +version = "1.1.2" +source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" +dependencies = [ + "metrics", + "metrics-derive", ] [[package]] -name = "reth-network-api" +name = "reth-net-banlist" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ "alloy-primitives", - "alloy-rpc-types-admin", - "auto_impl", - "derive_more", - "enr", - "futures", - "reth-eth-wire-types", - "reth-ethereum-forks", - "reth-network-p2p", - "reth-network-peers", - "reth-network-types", - "reth-tokio-util", - "serde", - "thiserror 2.0.4", - "tokio", - "tokio-stream", ] [[package]] @@ -5444,7 +4398,6 @@ dependencies = [ "secp256k1", "serde_with", "thiserror 2.0.4", - "tokio", "url", ] @@ -5453,11 +4406,9 @@ name = "reth-network-types" version = "1.1.2" source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" dependencies = [ - "humantime-serde", "reth-ethereum-forks", "reth-net-banlist", "reth-network-peers", - "serde", "serde_json", "tracing", ] @@ -5479,56 +4430,6 @@ dependencies = [ "zstd", ] -[[package]] -name = "reth-node-core" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rpc-types-engine", - "clap", - "const_format", - "derive_more", - "dirs-next", - "eyre", - "futures", - "humantime", - "rand", - "reth-chainspec", - "reth-cli-util", - "reth-config", - "reth-consensus", - "reth-db", - "reth-discv4", - "reth-discv5", - "reth-ethereum-forks", - "reth-net-nat", - "reth-network", - "reth-network-p2p", - "reth-network-peers", - "reth-primitives", - "reth-primitives-traits", - "reth-prune-types", - "reth-rpc-eth-types", - "reth-rpc-server-types", - "reth-rpc-types-compat", - "reth-stages-types", - "reth-storage-api", - "reth-storage-errors", - "reth-tracing", - "reth-transaction-pool", - "secp256k1", - "serde", - "shellexpand", - "strum", - "thiserror 2.0.4", - "toml", - "tracing", - "vergen", -] - [[package]] name = "reth-node-types" version = "1.1.2" @@ -5654,7 +4555,7 @@ dependencies = [ "reth-rpc-types-compat", "reth-transaction-pool", "revm", - "sha2 0.10.8", + "sha2", "thiserror 2.0.4", "tracing", ] @@ -5881,48 +4782,6 @@ dependencies = [ "revm", ] -[[package]] -name = "reth-rpc-eth-types" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-sol-types", - "derive_more", - "futures", - "itertools 0.13.0", - "jsonrpsee-core", - "jsonrpsee-types", - "metrics", - "rand", - "reth-chain-state", - "reth-chainspec", - "reth-errors", - "reth-execution-types", - "reth-metrics", - "reth-primitives", - "reth-primitives-traits", - "reth-revm", - "reth-rpc-server-types", - "reth-rpc-types-compat", - "reth-storage-api", - "reth-tasks", - "reth-transaction-pool", - "reth-trie", - "revm", - "revm-inspectors", - "revm-primitives", - "schnellru", - "serde", - "thiserror 2.0.4", - "tokio", - "tokio-stream", - "tracing", -] - [[package]] name = "reth-rpc-layer" version = "1.1.2" @@ -5937,22 +4796,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "reth-rpc-server-types" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "alloy-eips", - "alloy-primitives", - "alloy-rpc-types-engine", - "jsonrpsee-core", - "jsonrpsee-types", - "reth-errors", - "reth-network-api", - "serde", - "strum", -] - [[package]] name = "reth-rpc-types-compat" version = "1.1.2" @@ -6048,16 +4891,6 @@ dependencies = [ "tracing-futures", ] -[[package]] -name = "reth-tokio-util" -version = "1.1.2" -source = "git+https://github.com/paradigmxyz/reth.git?rev=e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222#e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" -dependencies = [ - "tokio", - "tokio-stream", - "tracing", -] - [[package]] name = "reth-tracing" version = "1.1.2" @@ -6173,7 +5006,6 @@ dependencies = [ "reth-storage-errors", "reth-trie", "revm", - "serde", "tracing", ] @@ -6207,23 +5039,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "revm-inspectors" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7f5f8a2deafb3c76f357bbf9e71b73bddb915c4994bbbe3208fbfbe8fc7f8e" -dependencies = [ - "alloy-primitives", - "alloy-rpc-types-eth", - "alloy-rpc-types-trace", - "alloy-sol-types", - "anstyle", - "colorchoice", - "revm", - "serde_json", - "thiserror 1.0.65", -] - [[package]] name = "revm-interpreter" version = "14.0.0" @@ -6249,7 +5064,7 @@ dependencies = [ "revm-primitives", "ripemd", "secp256k1", - "sha2 0.10.8", + "sha2", "substrate-bn", ] @@ -6279,7 +5094,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac 0.12.1", + "hmac", "subtle", ] @@ -6364,7 +5179,7 @@ dependencies = [ "hyper", "hyper-util", "jsonrpsee", - "lru 0.10.1", + "lru", "metrics", "metrics-derive", "metrics-exporter-prometheus", @@ -6379,7 +5194,6 @@ dependencies = [ "opentelemetry_sdk", "predicates", "reqwest", - "reth-node-core", "reth-optimism-payload-builder", "reth-payload-primitives", "reth-rpc-layer", @@ -6715,9 +5529,6 @@ name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" -dependencies = [ - "serde", -] [[package]] name = "semver-parser" @@ -6778,15 +5589,6 @@ dependencies = [ "syn 2.0.90", ] -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -6840,19 +5642,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.8" @@ -6893,15 +5682,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shellexpand" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" -dependencies = [ - "dirs", -] - [[package]] name = "shlex" version = "1.3.0" @@ -7239,9 +6019,7 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", - "libc", "num-conv", - "num_threads", "powerfmt", "serde", "time-core", @@ -7361,30 +6139,14 @@ dependencies = [ "futures-io", "futures-sink", "pin-project-lite", - "slab", "tokio", ] -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] [[package]] name = "toml_edit" @@ -7393,8 +6155,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.6.0", - "serde", - "serde_spanned", "toml_datetime", "winnow", ] @@ -7644,53 +6404,6 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "trust-dns-proto" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.4.0", - "ipnet", - "once_cell", - "rand", - "smallvec", - "thiserror 1.0.65", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand", - "resolv-conf", - "serde", - "smallvec", - "thiserror 1.0.65", - "tokio", - "tracing", - "trust-dns-proto", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -7721,18 +6434,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "uint" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" -dependencies = [ - "byteorder", - "crunchy", - "hex", - "static_assertions", -] - [[package]] name = "unarray" version = "0.1.4" @@ -7778,16 +6479,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - [[package]] name = "unsigned-varint" version = "0.8.0" @@ -7807,7 +6498,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna", "percent-encoding", ] @@ -7838,20 +6529,6 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" -[[package]] -name = "vergen" -version = "8.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" -dependencies = [ - "anyhow", - "cargo_metadata", - "cfg-if", - "regex", - "rustversion", - "time", -] - [[package]] name = "version_check" version = "0.9.5" @@ -8000,12 +6677,6 @@ dependencies = [ "rustix", ] -[[package]] -name = "widestring" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" - [[package]] name = "winapi" version = "0.3.9" @@ -8331,16 +7002,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "wyz" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml index a4e0d68a5ac56..be7913aefee84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222", features = [ "optimism", ] } -reth-node-core = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" } reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth.git", rev = "e022b6fd92a33cd44e3ae51ee2fc2ecc0f773222" } opentelemetry = { version = "0.26.0", features = ["trace"] } opentelemetry-http = "0.26.0" diff --git a/src/main.rs b/src/main.rs index 2dc4bd0001081..a245f8a297064 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,8 +17,8 @@ use opentelemetry_sdk::propagation::TraceContextPropagator; use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; -use reth_node_core::args::RpcServerArgs; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; +use rpc_client::{RpcClient, RpcClientArgs}; use server::{HttpClientWrapper, RollupBoostServer}; use std::sync::Arc; @@ -31,6 +31,7 @@ use tracing_subscriber::EnvFilter; mod metrics; mod proxy; +mod rpc_client; mod server; #[derive(Parser, Debug)] @@ -38,10 +39,10 @@ mod server; #[clap(group(ArgGroup::new("jwt").required(true).multiple(false).args(&["jwt_token", "jwt_path"])))] struct Args { #[clap(flatten)] - builder: RpcServerArgs, + builder: RpcClientArgs, #[clap(flatten)] - l2_client: RpcServerArgs, + l2_client: RpcClientArgs, /// Use the proposer to sync the builder node #[arg(long, env, default_value = "false")] @@ -163,6 +164,15 @@ async fn main() -> eyre::Result<()> { args.l2_timeout, )?; + let l2_client = RpcClient::new( + args.l2_client.http_addr, + args.l2_client.http_port, + args.l2_client.auth_addr, + args.l2_client.auth_port, + l2_jwt_secret, + args.l2_timeout, + )?; + // Initialize the builder client let builder_client = create_client( &args.builder.http_addr.to_string(), diff --git a/src/rpc_client.rs b/src/rpc_client.rs new file mode 100644 index 0000000000000..c6a630490f3ca --- /dev/null +++ b/src/rpc_client.rs @@ -0,0 +1,100 @@ +use clap::{arg, ArgGroup, Parser}; +use clap::{ + builder::{PossibleValue, RangedU64ValueParser, TypedValueParser}, + Arg, Args, Command, +}; +use jsonrpsee::http_client::transport::HttpBackend; +use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; +use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::path::PathBuf; +use std::time::Duration; + +/// Parameters for configuring the rpc more granularity via CLI +#[derive(Debug, Clone, Args, PartialEq, Eq)] +#[command(next_help_heading = "RPC")] +pub struct RpcClientArgs { + /// Http server address to listen on + #[arg(long = "http.addr")] + pub http_addr: IpAddr, + + /// Http server port to listen on + #[arg(long = "http.port")] + pub http_port: u16, + + /// Http Corsdomain to allow request from + #[arg(long = "http.corsdomain")] + pub http_corsdomain: Option, + + /// Auth server address to listen on + #[arg(long = "authrpc.addr")] + pub auth_addr: IpAddr, + + /// Auth server port to listen on + #[arg(long = "authrpc.port")] + pub auth_port: u16, + + /// Path to a JWT secret to use for the authenticated engine-API RPC server. + /// + /// This will enforce JWT authentication for all requests coming from the consensus layer. + /// + /// If no path is provided, a secret will be generated and stored in the datadir under + /// `//jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default. + #[arg( + long = "authrpc.jwtsecret", + value_name = "PATH", + global = true, + required = false + )] + pub auth_jwtsecret: Option, + + /// Filename for auth IPC socket/pipe within the datadir + #[arg(long = "auth-ipc.path")] + pub auth_ipc_path: Option, + + /// Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and + /// `--ws.api`. + /// + /// This is __not__ used for the authenticated engine-API RPC server, see + /// `--authrpc.jwtsecret`. + #[arg( + long = "rpc.jwtsecret", + value_name = "HEX", + global = true, + required = false + )] + pub rpc_jwtsecret: Option, +} + +pub struct RpcClient { + pub client: HttpClient, + pub auth_client: HttpClient>, +} + +impl RpcClient { + pub fn new( + http_addr: IpAddr, + http_port: u16, + auth_addr: IpAddr, + auth_port: u16, + jwt_secret: JwtSecret, + timeout: u64, + ) -> Result { + let http_socket = SocketAddr::new(http_addr, http_port); + let client = HttpClientBuilder::new() + .request_timeout(Duration::from_millis(timeout)) + .build(format!("http://{}", http_socket))?; + + let auth_layer = AuthClientLayer::new(jwt_secret); + let auth_socket = SocketAddr::new(auth_addr, auth_port); + let auth_client = HttpClientBuilder::new() + .set_http_middleware(tower::ServiceBuilder::new().layer(auth_layer)) + .request_timeout(Duration::from_millis(timeout)) + .build(format!("http://{}", auth_socket))?; + + Ok(Self { + client, + auth_client, + }) + } +} diff --git a/src/server.rs b/src/server.rs index 5343bf4221c8c..23e26c8d17392 100644 --- a/src/server.rs +++ b/src/server.rs @@ -6,7 +6,7 @@ use alloy_rpc_types_engine::{ }; use jsonrpsee::core::{async_trait, ClientError, RegisterMethodError, RpcResult}; use jsonrpsee::http_client::transport::HttpBackend; -use jsonrpsee::http_client::HttpClient; +use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::proc_macros::rpc; use jsonrpsee::types::error::INVALID_REQUEST_CODE; use jsonrpsee::types::{ErrorCode, ErrorObject}; @@ -19,9 +19,11 @@ use opentelemetry::trace::{Span, TraceContextExt, Tracer}; use opentelemetry::{Context, KeyValue}; use reth_optimism_payload_builder::{OpPayloadAttributes, OpPayloadBuilderAttributes}; use reth_payload_primitives::PayloadBuilderAttributes; -use reth_rpc_layer::AuthClientService; +use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; +use std::net::{IpAddr, SocketAddr}; use std::num::NonZero; use std::sync::Arc; +use std::time::Duration; use tokio::sync::Mutex; use tracing::{debug, error, info}; @@ -130,11 +132,6 @@ impl HttpClientWrapper { } } -pub struct RpcClient { - pub client: HttpClient, - pub auth_client: HttpClient>, -} - #[derive(Clone)] pub struct RollupBoostServer { l2_client: Arc, From 14725afdf6f152e753bfc37fd3aa147860db4ea4 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 13:54:17 -0500 Subject: [PATCH 058/429] wip (flashbots/rollup-boost) --- src/server.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/server.rs b/src/server.rs index 23e26c8d17392..4c4b585506708 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,4 +1,5 @@ use crate::metrics::ServerMetrics; +use crate::rpc_client::RpcClient; use alloy_primitives::{Bytes, B256, U128, U64}; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, @@ -133,24 +134,24 @@ impl HttpClientWrapper { } #[derive(Clone)] -pub struct RollupBoostServer { - l2_client: Arc, - builder_client: Arc, +pub struct RollupBoostServer { + l2_client: Arc, + builder_client: Arc, boost_sync: bool, metrics: Option>, payload_trace_context: Arc, } -impl RollupBoostServer { +impl RollupBoostServer { pub fn new( - l2_client: Arc, - builder_client: Arc, + l2_client: RpcClient, + builder_client: RpcClient, boost_sync: bool, metrics: Option>, ) -> Self { Self { - l2_client, - builder_client, + l2_client: Arc::new(l2_client), + builder_client: Arc::new(builder_client), boost_sync, metrics, payload_trace_context: Arc::new(PayloadTraceContext::new()), @@ -158,10 +159,7 @@ impl RollupBoostServer { } } -impl TryInto> for RollupBoostServer -where - Self: EngineApiServer + EthApiServer + MinerApiServer + MinerApiExtServer + Clone, -{ +impl TryInto> for RollupBoostServer { type Error = RegisterMethodError; fn try_into(self) -> Result, Self::Error> { @@ -180,10 +178,7 @@ where } #[async_trait] -impl EthApiServer for RollupBoostServer> -where - C: EthApiClient + Send + Sync + Clone + 'static, -{ +impl EthApiServer for RollupBoostServer { async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult { debug!( message = "received send_raw_transaction", @@ -195,7 +190,6 @@ where } let builder_client = self.builder_client.client.clone(); - let url = self.builder_client.url.clone(); let tx_bytes = bytes.clone(); tokio::spawn(async move { builder_client.send_raw_transaction(tx_bytes).await.map_err(|e| { From bd4e590f9975288b173e7973d7fe69b5b8738cde Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 15:34:35 -0500 Subject: [PATCH 059/429] wip (flashbots/rollup-boost) --- src/main.rs | 30 +++---- src/rpc.rs | 211 ++++++++++++++++++++++++++++++++++++++++++++++ src/rpc_client.rs | 100 ---------------------- src/server.rs | 67 +-------------- 4 files changed, 230 insertions(+), 178 deletions(-) create mode 100644 src/rpc.rs delete mode 100644 src/rpc_client.rs diff --git a/src/main.rs b/src/main.rs index a245f8a297064..7c3b90831aa6f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; -use rpc_client::{RpcClient, RpcClientArgs}; +use rpc::{ExecutionClient, RpcClientArgs}; use server::{HttpClientWrapper, RollupBoostServer}; use std::sync::Arc; @@ -31,9 +31,8 @@ use tracing_subscriber::EnvFilter; mod metrics; mod proxy; -mod rpc_client; +mod rpc; mod server; - #[derive(Parser, Debug)] #[clap(author, version, about)] #[clap(group(ArgGroup::new("jwt").required(true).multiple(false).args(&["jwt_token", "jwt_path"])))] @@ -83,14 +82,6 @@ struct Args { /// Log format #[arg(long, env, default_value = "text")] log_format: String, - - /// Timeout for the builder client calls in milliseconds - #[arg(long, env, default_value = "200")] - builder_timeout: u64, - - /// Timeout for the l2 client calls in milliseconds - #[arg(long, env, default_value = "2000")] - l2_timeout: u64, } #[tokio::main] @@ -161,23 +152,32 @@ async fn main() -> eyre::Result<()> { let l2_client = create_client( &args.l2_client.http_addr.to_string(), l2_jwt_secret, - args.l2_timeout, + args.l2_client.timeout, )?; - let l2_client = RpcClient::new( + let l2_client = ExecutionClient::new( args.l2_client.http_addr, args.l2_client.http_port, args.l2_client.auth_addr, args.l2_client.auth_port, l2_jwt_secret, - args.l2_timeout, + args.l2_client.timeout, )?; // Initialize the builder client let builder_client = create_client( &args.builder.http_addr.to_string(), builder_jwt_secret, - args.builder_timeout, + args.builder.timeout, + )?; + + let builder_client = ExecutionClient::new( + args.builder.http_addr, + args.builder.http_port, + args.builder.auth_addr, + args.builder.auth_port, + builder_jwt_secret, + args.builder.timeout, )?; let rollup_boost = RollupBoostServer::new( diff --git a/src/rpc.rs b/src/rpc.rs new file mode 100644 index 0000000000000..868b676ffaed2 --- /dev/null +++ b/src/rpc.rs @@ -0,0 +1,211 @@ +use crate::metrics::ServerMetrics; +use alloy_primitives::{Bytes, B256, U128, U64}; +use alloy_rpc_types_engine::{ + ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, + PayloadStatus, +}; +use jsonrpsee::core::{async_trait, ClientError, RegisterMethodError, RpcResult}; +use jsonrpsee::http_client::transport::HttpBackend; +use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; +use jsonrpsee::proc_macros::rpc; +use jsonrpsee::types::error::INVALID_REQUEST_CODE; +use jsonrpsee::types::{ErrorCode, ErrorObject}; +use jsonrpsee::RpcModule; +use lru::LruCache; +use op_alloy_rpc_jsonrpsee::traits::{MinerApiExtClient, MinerApiExtServer}; +use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; +use opentelemetry::global::{self, BoxedSpan, BoxedTracer}; +use opentelemetry::trace::{Span, TraceContextExt, Tracer}; +use opentelemetry::{Context, KeyValue}; +use reth_optimism_payload_builder::{OpPayloadAttributes, OpPayloadBuilderAttributes}; +use reth_payload_primitives::PayloadBuilderAttributes; +use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; +use std::net::{IpAddr, SocketAddr}; +use std::num::NonZero; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::Mutex; +use tracing::{debug, error, info}; + +use clap::{arg, ArgGroup, Parser}; +use clap::{ + builder::{PossibleValue, RangedU64ValueParser, TypedValueParser}, + Arg, Args, Command, +}; +use std::path::PathBuf; + +/// Parameters for configuring the rpc more granularity via CLI +#[derive(Debug, Clone, Args, PartialEq, Eq)] +#[command(next_help_heading = "RPC")] +#[command(group( + ArgGroup::new("jwt") + .required(true) + .args(&["auth_jwtsecret", "rpc_jwtsecret"]), +))] +pub struct RpcClientArgs { + /// Http server address to listen on + #[arg(long = "http.addr")] + pub http_addr: IpAddr, + + /// Http server port to listen on + #[arg(long = "http.port")] + pub http_port: u16, + + /// Http Corsdomain to allow request from + #[arg(long = "http.corsdomain")] + pub http_corsdomain: Option, + + /// Auth server address to listen on + #[arg(long = "authrpc.addr")] + pub auth_addr: IpAddr, + + /// Auth server port to listen on + #[arg(long = "authrpc.port")] + pub auth_port: u16, + + /// Path to a JWT secret to use for the authenticated engine-API RPC server. + /// + /// This will enforce JWT authentication for all requests coming from the consensus layer. + /// + /// If no path is provided, a secret will be generated and stored in the datadir under + /// `//jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default. + #[arg( + long = "authrpc.jwtsecret", + value_name = "PATH", + global = true, + required = false + )] + pub auth_jwtsecret: Option, + + /// Filename for auth IPC socket/pipe within the datadir + #[arg(long = "auth-ipc.path")] + pub auth_ipc_path: Option, + + /// Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and + /// `--ws.api`. + /// + /// This is __not__ used for the authenticated engine-API RPC server, see + /// `--authrpc.jwtsecret`. + #[arg( + long = "rpc.jwtsecret", + value_name = "HEX", + global = true, + required = false + )] + pub rpc_jwtsecret: Option, + + /// Timeout for http calls in milliseconds + #[arg(long, env)] + pub timeout: u64, +} + +pub struct ExecutionClient { + pub client: HttpClient, + pub auth_client: HttpClient>, +} + +impl ExecutionClient { + pub fn new( + http_addr: IpAddr, + http_port: u16, + auth_addr: IpAddr, + auth_port: u16, + jwt_secret: JwtSecret, + timeout: u64, + ) -> Result { + let http_socket = SocketAddr::new(http_addr, http_port); + let client = HttpClientBuilder::new() + .request_timeout(Duration::from_millis(timeout)) + .build(format!("http://{}", http_socket))?; + + let auth_layer = AuthClientLayer::new(jwt_secret); + let auth_socket = SocketAddr::new(auth_addr, auth_port); + let auth_client = HttpClientBuilder::new() + .set_http_middleware(tower::ServiceBuilder::new().layer(auth_layer)) + .request_timeout(Duration::from_millis(timeout)) + .build(format!("http://{}", auth_socket))?; + + Ok(Self { + client, + auth_client, + }) + } +} + +#[rpc(server, client, namespace = "engine")] +pub trait EngineApi { + #[method(name = "forkchoiceUpdatedV3")] + async fn fork_choice_updated_v3( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> RpcResult; + + #[method(name = "getPayloadV3")] + async fn get_payload_v3( + &self, + payload_id: PayloadId, + ) -> RpcResult; + + #[method(name = "newPayloadV3")] + async fn new_payload_v3( + &self, + payload: ExecutionPayloadV3, + versioned_hashes: Vec, + parent_beacon_block_root: B256, + ) -> RpcResult; +} + +#[rpc(server, client, namespace = "eth")] +pub trait EthApi { + #[method(name = "sendRawTransaction")] + async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult; +} + +/*TODO: Remove this in favor of the `MinerApi` from Reth once the + trait methods are updated to be async +*/ +/// Miner namespace rpc interface that can control miner/builder settings +#[rpc(server, client, namespace = "miner")] +pub trait MinerApi { + /// Sets the extra data string that is included when this miner mines a block. + /// + /// Returns an error if the extra data is too long. + #[method(name = "setExtra")] + async fn set_extra(&self, record: Bytes) -> RpcResult; + + /// Sets the minimum accepted gas price for the miner. + #[method(name = "setGasPrice")] + async fn set_gas_price(&self, gas_price: U128) -> RpcResult; + + /// Sets the gaslimit to target towards during mining. + #[method(name = "setGasLimit")] + async fn set_gas_limit(&self, gas_price: U128) -> RpcResult; +} + +// #[async_trait] +// impl EngineApiClient for ExecutionClient { +// async fn fork_choice_updated_v3( +// &self, +// fork_choice_state: ForkchoiceState, +// payload_attributes: Option, +// ) -> RpcResult { +// todo!() +// } + +// async fn get_payload_v3( +// &self, +// payload_id: PayloadId, +// ) -> RpcResult { +// todo!() +// } + +// async fn new_payload_v3( +// &self, +// payload: ExecutionPayloadV3, +// versioned_hashes: Vec, +// parent_beacon_block_root: B256, +// ) -> RpcResult { +// todo!() +// } +// } diff --git a/src/rpc_client.rs b/src/rpc_client.rs deleted file mode 100644 index c6a630490f3ca..0000000000000 --- a/src/rpc_client.rs +++ /dev/null @@ -1,100 +0,0 @@ -use clap::{arg, ArgGroup, Parser}; -use clap::{ - builder::{PossibleValue, RangedU64ValueParser, TypedValueParser}, - Arg, Args, Command, -}; -use jsonrpsee::http_client::transport::HttpBackend; -use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; -use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::path::PathBuf; -use std::time::Duration; - -/// Parameters for configuring the rpc more granularity via CLI -#[derive(Debug, Clone, Args, PartialEq, Eq)] -#[command(next_help_heading = "RPC")] -pub struct RpcClientArgs { - /// Http server address to listen on - #[arg(long = "http.addr")] - pub http_addr: IpAddr, - - /// Http server port to listen on - #[arg(long = "http.port")] - pub http_port: u16, - - /// Http Corsdomain to allow request from - #[arg(long = "http.corsdomain")] - pub http_corsdomain: Option, - - /// Auth server address to listen on - #[arg(long = "authrpc.addr")] - pub auth_addr: IpAddr, - - /// Auth server port to listen on - #[arg(long = "authrpc.port")] - pub auth_port: u16, - - /// Path to a JWT secret to use for the authenticated engine-API RPC server. - /// - /// This will enforce JWT authentication for all requests coming from the consensus layer. - /// - /// If no path is provided, a secret will be generated and stored in the datadir under - /// `//jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default. - #[arg( - long = "authrpc.jwtsecret", - value_name = "PATH", - global = true, - required = false - )] - pub auth_jwtsecret: Option, - - /// Filename for auth IPC socket/pipe within the datadir - #[arg(long = "auth-ipc.path")] - pub auth_ipc_path: Option, - - /// Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and - /// `--ws.api`. - /// - /// This is __not__ used for the authenticated engine-API RPC server, see - /// `--authrpc.jwtsecret`. - #[arg( - long = "rpc.jwtsecret", - value_name = "HEX", - global = true, - required = false - )] - pub rpc_jwtsecret: Option, -} - -pub struct RpcClient { - pub client: HttpClient, - pub auth_client: HttpClient>, -} - -impl RpcClient { - pub fn new( - http_addr: IpAddr, - http_port: u16, - auth_addr: IpAddr, - auth_port: u16, - jwt_secret: JwtSecret, - timeout: u64, - ) -> Result { - let http_socket = SocketAddr::new(http_addr, http_port); - let client = HttpClientBuilder::new() - .request_timeout(Duration::from_millis(timeout)) - .build(format!("http://{}", http_socket))?; - - let auth_layer = AuthClientLayer::new(jwt_secret); - let auth_socket = SocketAddr::new(auth_addr, auth_port); - let auth_client = HttpClientBuilder::new() - .set_http_middleware(tower::ServiceBuilder::new().layer(auth_layer)) - .request_timeout(Duration::from_millis(timeout)) - .build(format!("http://{}", auth_socket))?; - - Ok(Self { - client, - auth_client, - }) - } -} diff --git a/src/server.rs b/src/server.rs index 4c4b585506708..3397cf952ece6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,4 +1,5 @@ use crate::metrics::ServerMetrics; +use crate::rpc::{EngineApiServer, EthApiServer, MinerApiServer}; use crate::rpc_client::RpcClient; use alloy_primitives::{Bytes, B256, U128, U64}; use alloy_rpc_types_engine::{ @@ -91,36 +92,6 @@ impl PayloadTraceContext { } } -#[rpc(server, client, namespace = "engine")] -pub trait EngineApi { - #[method(name = "forkchoiceUpdatedV3")] - async fn fork_choice_updated_v3( - &self, - fork_choice_state: ForkchoiceState, - payload_attributes: Option, - ) -> RpcResult; - - #[method(name = "getPayloadV3")] - async fn get_payload_v3( - &self, - payload_id: PayloadId, - ) -> RpcResult; - - #[method(name = "newPayloadV3")] - async fn new_payload_v3( - &self, - payload: ExecutionPayloadV3, - versioned_hashes: Vec, - parent_beacon_block_root: B256, - ) -> RpcResult; -} - -#[rpc(server, client, namespace = "eth")] -pub trait EthApi { - #[method(name = "sendRawTransaction")] - async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult; -} - #[derive(Clone)] pub struct HttpClientWrapper>> { pub client: C, @@ -215,32 +186,8 @@ impl EthApiServer for RollupBoostServer { } } -/*TODO: Remove this in favor of the `MinerApi` from Reth once the - trait methods are updated to be async -*/ -/// Miner namespace rpc interface that can control miner/builder settings -#[rpc(server, client, namespace = "miner")] -pub trait MinerApi { - /// Sets the extra data string that is included when this miner mines a block. - /// - /// Returns an error if the extra data is too long. - #[method(name = "setExtra")] - async fn set_extra(&self, record: Bytes) -> RpcResult; - - /// Sets the minimum accepted gas price for the miner. - #[method(name = "setGasPrice")] - async fn set_gas_price(&self, gas_price: U128) -> RpcResult; - - /// Sets the gaslimit to target towards during mining. - #[method(name = "setGasLimit")] - async fn set_gas_limit(&self, gas_price: U128) -> RpcResult; -} - #[async_trait] -impl MinerApiServer for RollupBoostServer> -where - C: MinerApiClient + Send + Sync + Clone + 'static, -{ +impl MinerApiServer for RollupBoostServer { async fn set_extra(&self, record: Bytes) -> RpcResult { debug!( message = "received miner_setExtra", @@ -331,10 +278,7 @@ where } #[async_trait] -impl MinerApiExtServer for RollupBoostServer> -where - C: MinerApiExtClient + Send + Sync + Clone + 'static, -{ +impl MinerApiExtServer for RollupBoostServer { async fn set_max_da_size(&self, max_tx_size: U64, max_block_size: U64) -> RpcResult { debug!( target: "server::set_max_da_size", @@ -377,10 +321,7 @@ where } #[async_trait] -impl EngineApiServer for RollupBoostServer> -where - C: EngineApiClient + Send + Sync + Clone + 'static, -{ +impl EngineApiServer for RollupBoostServer { async fn fork_choice_updated_v3( &self, fork_choice_state: ForkchoiceState, From 6420026f3c464d28d0b435c0a86b7a451d864393 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 16:06:29 -0500 Subject: [PATCH 060/429] wip (flashbots/rollup-boost) --- src/main.rs | 14 -------------- src/rpc.rs | 31 ++++--------------------------- src/server.rs | 19 +++++++++---------- 3 files changed, 13 insertions(+), 51 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7c3b90831aa6f..48f70dc40e2b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -148,13 +148,6 @@ async fn main() -> eyre::Result<()> { eyre::bail!("Either builder.rpc_jwtsecret or builder.auth_jwtsecret must be provided"); }; - // Initialize the l2 client - let l2_client = create_client( - &args.l2_client.http_addr.to_string(), - l2_jwt_secret, - args.l2_client.timeout, - )?; - let l2_client = ExecutionClient::new( args.l2_client.http_addr, args.l2_client.http_port, @@ -164,13 +157,6 @@ async fn main() -> eyre::Result<()> { args.l2_client.timeout, )?; - // Initialize the builder client - let builder_client = create_client( - &args.builder.http_addr.to_string(), - builder_jwt_secret, - args.builder.timeout, - )?; - let builder_client = ExecutionClient::new( args.builder.http_addr, args.builder.http_port, diff --git a/src/rpc.rs b/src/rpc.rs index 868b676ffaed2..f54aa6f424ab1 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -101,7 +101,9 @@ pub struct RpcClientArgs { pub struct ExecutionClient { pub client: HttpClient, + pub http_socket: SocketAddr, pub auth_client: HttpClient>, + pub auth_socket: SocketAddr, } impl ExecutionClient { @@ -127,7 +129,9 @@ impl ExecutionClient { Ok(Self { client, + http_socket, auth_client, + auth_socket, }) } } @@ -182,30 +186,3 @@ pub trait MinerApi { #[method(name = "setGasLimit")] async fn set_gas_limit(&self, gas_price: U128) -> RpcResult; } - -// #[async_trait] -// impl EngineApiClient for ExecutionClient { -// async fn fork_choice_updated_v3( -// &self, -// fork_choice_state: ForkchoiceState, -// payload_attributes: Option, -// ) -> RpcResult { -// todo!() -// } - -// async fn get_payload_v3( -// &self, -// payload_id: PayloadId, -// ) -> RpcResult { -// todo!() -// } - -// async fn new_payload_v3( -// &self, -// payload: ExecutionPayloadV3, -// versioned_hashes: Vec, -// parent_beacon_block_root: B256, -// ) -> RpcResult { -// todo!() -// } -// } diff --git a/src/server.rs b/src/server.rs index 3397cf952ece6..e6c0fc444aa01 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,5 @@ use crate::metrics::ServerMetrics; -use crate::rpc::{EngineApiServer, EthApiServer, MinerApiServer}; -use crate::rpc_client::RpcClient; +use crate::rpc::{EngineApiServer, EthApiClient, EthApiServer, ExecutionClient, MinerApiServer}; use alloy_primitives::{Bytes, B256, U128, U64}; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, @@ -106,8 +105,8 @@ impl HttpClientWrapper { #[derive(Clone)] pub struct RollupBoostServer { - l2_client: Arc, - builder_client: Arc, + l2_client: Arc, + builder_client: Arc, boost_sync: bool, metrics: Option>, payload_trace_context: Arc, @@ -115,8 +114,8 @@ pub struct RollupBoostServer { impl RollupBoostServer { pub fn new( - l2_client: RpcClient, - builder_client: RpcClient, + l2_client: ExecutionClient, + builder_client: ExecutionClient, boost_sync: bool, metrics: Option>, ) -> Self { @@ -160,11 +159,11 @@ impl EthApiServer for RollupBoostServer { metrics.send_raw_tx_count.increment(1); } - let builder_client = self.builder_client.client.clone(); + let builder_client = self.builder_client.clone(); let tx_bytes = bytes.clone(); tokio::spawn(async move { - builder_client.send_raw_transaction(tx_bytes).await.map_err(|e| { - error!(message = "error calling send_raw_transaction for builder", "url" = url, "error" = %e); + builder_client.client.send_raw_transaction(tx_bytes).await.map_err(|e| { + error!(message = "error calling send_raw_transaction for builder", "url" = ?builder_client.http_socket, "error" = %e); }) }); @@ -177,7 +176,7 @@ impl EthApiServer for RollupBoostServer { other_error => { error!( message = "error calling send_raw_transaction for l2 client", - "url" = self.l2_client.url, + "url" = ?self.l2_client.http_socket, "error" = %other_error, ); ErrorCode::InternalError.into() From d82bd1679c097a61e776a8e286ccb0eb9d044d6b Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 16:20:24 -0500 Subject: [PATCH 061/429] wip (flashbots/rollup-boost) --- src/main.rs | 7 +---- src/server.rs | 78 +++++++++++++++++++++++++-------------------------- 2 files changed, 39 insertions(+), 46 deletions(-) diff --git a/src/main.rs b/src/main.rs index 48f70dc40e2b9..9da51af74bd15 100644 --- a/src/main.rs +++ b/src/main.rs @@ -166,12 +166,7 @@ async fn main() -> eyre::Result<()> { args.builder.timeout, )?; - let rollup_boost = RollupBoostServer::new( - Arc::new(l2_client), - Arc::new(builder_client), - args.boost_sync, - metrics, - ); + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, args.boost_sync, metrics); let module: RpcModule<()> = rollup_boost.try_into()?; diff --git a/src/server.rs b/src/server.rs index e6c0fc444aa01..d89d52c28279c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,5 +1,8 @@ use crate::metrics::ServerMetrics; -use crate::rpc::{EngineApiServer, EthApiClient, EthApiServer, ExecutionClient, MinerApiServer}; +use crate::rpc::{ + EngineApiClient, EngineApiServer, EthApiClient, EthApiServer, ExecutionClient, MinerApiClient, + MinerApiServer, +}; use alloy_primitives::{Bytes, B256, U128, U64}; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, @@ -163,7 +166,7 @@ impl EthApiServer for RollupBoostServer { let tx_bytes = bytes.clone(); tokio::spawn(async move { builder_client.client.send_raw_transaction(tx_bytes).await.map_err(|e| { - error!(message = "error calling send_raw_transaction for builder", "url" = ?builder_client.http_socket, "error" = %e); + error!(message = "error calling send_raw_transaction for builder", ?builder_client.http_socket, "error" = %e); }) }); @@ -193,12 +196,11 @@ impl MinerApiServer for RollupBoostServer { "record_len" = record.len() ); - let builder_client = self.builder_client.client.clone(); - let url = self.builder_client.url.clone(); + let builder_client = self.builder_client.clone(); let rec = record.clone(); tokio::spawn(async move { - builder_client.set_extra(rec).await.map_err(|e| { - error!(message = "error calling miner_setExtra for builder", "url" =url, "error" = %e); + builder_client.client.set_extra(rec).await.map_err(|e| { + error!(message = "error calling miner_setExtra for builder", ?builder_client.http_socket, "error" = %e); }) }); @@ -209,7 +211,7 @@ impl MinerApiServer for RollupBoostServer { other_error => { error!( message = "error calling miner_setExtra for l2 client", - "url" = self.l2_client.url, + "url" = ?self.l2_client.http_socket, "error" = %other_error, ); Err(ErrorCode::InternalError.into()) @@ -224,11 +226,10 @@ impl MinerApiServer for RollupBoostServer { "gas_price" = ?gas_price ); - let builder_client = self.builder_client.client.clone(); - let url = self.builder_client.url.clone(); + let builder_client = self.builder_client.clone(); tokio::spawn(async move { - builder_client.set_gas_limit(gas_price).await.map_err(|e| { - error!(message = "error calling miner_setGasLimit for builder", "url" =url, "error" = %e); + builder_client.client.set_gas_limit(gas_price).await.map_err(|e| { + error!(message = "error calling miner_setGasLimit for builder", ?builder_client.http_socket, "error" = %e); }) }); @@ -239,7 +240,7 @@ impl MinerApiServer for RollupBoostServer { other_error => { error!( message = "error calling miner_setGasLimit for l2 client", - "url" = self.l2_client.url, + "url" = ?self.l2_client.http_socket, "error" = %other_error, ); Err(ErrorCode::InternalError.into()) @@ -251,11 +252,10 @@ impl MinerApiServer for RollupBoostServer { async fn set_gas_price(&self, gas_price: U128) -> RpcResult { debug!(message = "received miner_setGasPrice", ?gas_price); - let builder_client = self.builder_client.client.clone(); - let url = self.builder_client.url.clone(); + let builder_client = self.builder_client.clone(); tokio::spawn(async move { - builder_client.set_gas_price(gas_price).await.map_err(|e| { - error!(message = "error calling miner_setGasPrice for builder", "url" =url, "error" = %e); + builder_client.client.set_gas_price(gas_price).await.map_err(|e| { + error!(message = "error calling miner_setGasPrice for builder", ?builder_client.http_socket, "error" = %e); }) }); @@ -266,7 +266,7 @@ impl MinerApiServer for RollupBoostServer { other_error => { error!( message = "error calling miner_setGasPrice for l2 client", - "url" = self.l2_client.url, + "url" = ?self.l2_client.http_socket, "error" = %other_error, ); Err(ErrorCode::InternalError.into()) @@ -286,11 +286,10 @@ impl MinerApiExtServer for RollupBoostServer { ?max_block_size ); - let builder_client = self.builder_client.client.clone(); - let url = self.builder_client.url.clone(); + let builder_client = self.builder_client.clone(); tokio::spawn(async move { - builder_client.set_max_da_size(max_tx_size, max_block_size).await.map_err(|e| { - error!(target: "server::set_max_da_size", message = "error calling miner_setMaxDASize for builder", "url" =url, "error" = %e); + builder_client.client.set_max_da_size(max_tx_size, max_block_size).await.map_err(|e| { + error!(target: "server::set_max_da_size", message = "error calling miner_setMaxDASize for builder", ?builder_client.http_socket, "error" = %e); }) }); @@ -309,7 +308,7 @@ impl MinerApiExtServer for RollupBoostServer { other_error => { error!( message = "error calling miner_setMaxDASize for l2 client", - "url" = self.l2_client.url, + "url" = ?self.l2_client.http_socket, "error" = %other_error, ); Err(ErrorCode::InternalError.into()) @@ -383,21 +382,21 @@ impl EngineApiServer for RollupBoostServer { if let Some(metrics) = &self.metrics { metrics.fcu_count.increment(1); } - let builder = self.builder_client.clone(); + let builder_client = self.builder_client.clone(); let attr = payload_attributes.clone(); tokio::spawn(async move { - let _ = builder.client.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { + let _ = builder_client.client.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { let payload_id_str = response.payload_id.map(|id| id.to_string()).unwrap_or_default(); if response.is_invalid() { - error!(message = "builder rejected fork_choice_updated_v3 with attributes", "url" = builder.url, "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); + error!(message = "builder rejected fork_choice_updated_v3 with attributes", ?builder_client.http_socket, "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); } else { - info!(message = "called fork_choice_updated_v3 to builder with payload attributes", "url" = builder.url, "payload_status" = %response.payload_status.status, "payload_id" = payload_id_str); + info!(message = "called fork_choice_updated_v3 to builder with payload attributes", ?builder_client.http_socket, "payload_status" = %response.payload_status.status, "payload_id" = payload_id_str); } }) .map_err(|e| { error!( message = "error calling fork_choice_updated_v3 to builder", - "url" = builder.url, + ?builder_client.http_socket, "error" = %e, "head_block_hash" = %fork_choice_state.head_block_hash ); @@ -419,7 +418,7 @@ impl EngineApiServer for RollupBoostServer { other_error => { error!( message = "error calling fork_choice_updated_v3 for l2 client", - "url" = self.l2_client.url, + "url" = ?self.l2_client.http_socket, "error" = %other_error, "head_block_hash" = %fork_choice_state.head_block_hash, ); @@ -449,9 +448,9 @@ impl EngineApiServer for RollupBoostServer { ) }); - let builder = self.builder_client.clone(); - let payload = builder.client.get_payload_v3(payload_id).await.map_err(|e| { - error!(message = "error calling get_payload_v3 from builder", "url" = builder.url, "error" = %e, "payload_id" = %payload_id); + let builder_client = self.builder_client.clone(); + let payload = builder_client.client.get_payload_v3(payload_id).await.map_err(|e| { + error!(message = "error calling get_payload_v3 from builder", ?builder_client.http_socket, "error" = %e, "payload_id" = %payload_id); e })?; @@ -465,7 +464,7 @@ impl EngineApiServer for RollupBoostServer { metrics.new_payload_count.increment(1); } let payload_status = self.l2_client.client.new_payload_v3(payload.execution_payload.clone(), vec![], payload.parent_beacon_block_root).await.map_err(|e| { - error!(message = "error calling new_payload_v3 to validate builder payload", "url" = self.l2_client.url, "error" = %e, "payload_id" = %payload_id); + error!(message = "error calling new_payload_v3 to validate builder payload", "url" = ?self.l2_client.http_socket, "error" = %e, "payload_id" = %payload_id); e })?; if let Some(mut s) = span { @@ -478,7 +477,7 @@ impl EngineApiServer for RollupBoostServer { } }; if payload_status.is_invalid() { - error!(message = "builder payload was not valid", "url" = builder.url, "payload_status" = %payload_status.status, "payload_id" = %payload_id); + error!(message = "builder payload was not valid", "payload_status" = %payload_status.status, "payload_id" = %payload_id); Err(ClientError::Call(ErrorObject::owned( INVALID_REQUEST_CODE, "Builder payload was not valid", @@ -496,7 +495,7 @@ impl EngineApiServer for RollupBoostServer { other_error => { error!( message = "error calling get_payload_v3", - "url" = self.builder_client.url, + builder_client.http_socket = ?self.builder_client.http_socket, "error" = %other_error, "payload_id" = %payload_id ); @@ -540,20 +539,19 @@ impl EngineApiServer for RollupBoostServer { .remove_by_parent_hash(&parent_hash) .await; - let builder = self.builder_client.client.clone(); - let builder_url = self.builder_client.url.clone(); + let builder = self.builder_client.clone(); let builder_payload = payload.clone(); let builder_versioned_hashes = versioned_hashes.clone(); tokio::spawn(async move { - let _ = builder.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await + let _ = builder.client.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await .map(|response: PayloadStatus| { if response.is_invalid() { - error!(message = "builder rejected new_payload_v3", "url" = builder_url, "block_hash" = %block_hash); + error!(message = "builder rejected new_payload_v3", "url" = ?builder.http_socket, "block_hash" = %block_hash); } else { - info!(message = "called new_payload_v3 to builder", "url" = builder_url, "payload_status" = %response.status, "block_hash" = %block_hash); + info!(message = "called new_payload_v3 to builder", "url" = ?builder.http_socket, "payload_status" = %response.status, "block_hash" = %block_hash); } }).map_err(|e| { - error!(message = "error calling new_payload_v3 to builder", "url" = builder_url, "error" = %e, "block_hash" = %block_hash); + error!(message = "error calling new_payload_v3 to builder", "url" = ?builder.http_socket, "error" = %e, "block_hash" = %block_hash); e }); if let Some(mut spans) = spans { From e4f0b4411dac5c4c5a1334d02ab77eb945e60c56 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 16:24:31 -0500 Subject: [PATCH 062/429] update engine api to use auth client (flashbots/rollup-boost) --- src/server.rs | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/server.rs b/src/server.rs index d89d52c28279c..398cbf9679483 100644 --- a/src/server.rs +++ b/src/server.rs @@ -166,7 +166,7 @@ impl EthApiServer for RollupBoostServer { let tx_bytes = bytes.clone(); tokio::spawn(async move { builder_client.client.send_raw_transaction(tx_bytes).await.map_err(|e| { - error!(message = "error calling send_raw_transaction for builder", ?builder_client.http_socket, "error" = %e); + error!(message = "error calling send_raw_transaction for builder", "url" = ?builder_client.http_socket, "error" = %e); }) }); @@ -200,7 +200,7 @@ impl MinerApiServer for RollupBoostServer { let rec = record.clone(); tokio::spawn(async move { builder_client.client.set_extra(rec).await.map_err(|e| { - error!(message = "error calling miner_setExtra for builder", ?builder_client.http_socket, "error" = %e); + error!(message = "error calling miner_setExtra for builder", "url" = ?builder_client.http_socket, "error" = %e); }) }); @@ -229,7 +229,7 @@ impl MinerApiServer for RollupBoostServer { let builder_client = self.builder_client.clone(); tokio::spawn(async move { builder_client.client.set_gas_limit(gas_price).await.map_err(|e| { - error!(message = "error calling miner_setGasLimit for builder", ?builder_client.http_socket, "error" = %e); + error!(message = "error calling miner_setGasLimit for builder", "url" = ?builder_client.http_socket, "error" = %e); }) }); @@ -255,7 +255,7 @@ impl MinerApiServer for RollupBoostServer { let builder_client = self.builder_client.clone(); tokio::spawn(async move { builder_client.client.set_gas_price(gas_price).await.map_err(|e| { - error!(message = "error calling miner_setGasPrice for builder", ?builder_client.http_socket, "error" = %e); + error!(message = "error calling miner_setGasPrice for builder", "url" = ?builder_client.http_socket, "error" = %e); }) }); @@ -289,7 +289,7 @@ impl MinerApiExtServer for RollupBoostServer { let builder_client = self.builder_client.clone(); tokio::spawn(async move { builder_client.client.set_max_da_size(max_tx_size, max_block_size).await.map_err(|e| { - error!(target: "server::set_max_da_size", message = "error calling miner_setMaxDASize for builder", ?builder_client.http_socket, "error" = %e); + error!(target: "server::set_max_da_size", message = "error calling miner_setMaxDASize for builder", "url" = ?builder_client.http_socket, "error" = %e); }) }); @@ -385,18 +385,18 @@ impl EngineApiServer for RollupBoostServer { let builder_client = self.builder_client.clone(); let attr = payload_attributes.clone(); tokio::spawn(async move { - let _ = builder_client.client.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { + let _ = builder_client.auth_client.fork_choice_updated_v3(fork_choice_state, attr).await.map(|response| { let payload_id_str = response.payload_id.map(|id| id.to_string()).unwrap_or_default(); if response.is_invalid() { - error!(message = "builder rejected fork_choice_updated_v3 with attributes", ?builder_client.http_socket, "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); + error!(message = "builder rejected fork_choice_updated_v3 with attributes", "url" = ?builder_client.auth_socket, "payload_id" = payload_id_str, "validation_error" = %response.payload_status.status); } else { - info!(message = "called fork_choice_updated_v3 to builder with payload attributes", ?builder_client.http_socket, "payload_status" = %response.payload_status.status, "payload_id" = payload_id_str); + info!(message = "called fork_choice_updated_v3 to builder with payload attributes", "url" = ?builder_client.auth_socket, "payload_status" = %response.payload_status.status, "payload_id" = payload_id_str); } }) .map_err(|e| { error!( message = "error calling fork_choice_updated_v3 to builder", - ?builder_client.http_socket, + "url" = ?builder_client.auth_socket, "error" = %e, "head_block_hash" = %fork_choice_state.head_block_hash ); @@ -410,7 +410,7 @@ impl EngineApiServer for RollupBoostServer { } self.l2_client - .client + .auth_client .fork_choice_updated_v3(fork_choice_state, payload_attributes) .await .map_err(|e| match e { @@ -418,7 +418,7 @@ impl EngineApiServer for RollupBoostServer { other_error => { error!( message = "error calling fork_choice_updated_v3 for l2 client", - "url" = ?self.l2_client.http_socket, + "url" = ?self.l2_client.auth_socket, "error" = %other_error, "head_block_hash" = %fork_choice_state.head_block_hash, ); @@ -432,7 +432,7 @@ impl EngineApiServer for RollupBoostServer { payload_id: PayloadId, ) -> RpcResult { info!(message = "received get_payload_v3", "payload_id" = %payload_id); - let l2_client_future = self.l2_client.client.get_payload_v3(payload_id); + let l2_client_future = self.l2_client.auth_client.get_payload_v3(payload_id); let builder_client_future = Box::pin(async move { if let Some(metrics) = &self.metrics { metrics.get_payload_count.increment(1); @@ -449,8 +449,8 @@ impl EngineApiServer for RollupBoostServer { }); let builder_client = self.builder_client.clone(); - let payload = builder_client.client.get_payload_v3(payload_id).await.map_err(|e| { - error!(message = "error calling get_payload_v3 from builder", ?builder_client.http_socket, "error" = %e, "payload_id" = %payload_id); + let payload = builder_client.auth_client.get_payload_v3(payload_id).await.map_err(|e| { + error!(message = "error calling get_payload_v3 from builder", "url" = ?builder_client.auth_socket, "error" = %e, "payload_id" = %payload_id); e })?; @@ -463,8 +463,8 @@ impl EngineApiServer for RollupBoostServer { if let Some(metrics) = &self.metrics { metrics.new_payload_count.increment(1); } - let payload_status = self.l2_client.client.new_payload_v3(payload.execution_payload.clone(), vec![], payload.parent_beacon_block_root).await.map_err(|e| { - error!(message = "error calling new_payload_v3 to validate builder payload", "url" = ?self.l2_client.http_socket, "error" = %e, "payload_id" = %payload_id); + let payload_status = self.l2_client.auth_client.new_payload_v3(payload.execution_payload.clone(), vec![], payload.parent_beacon_block_root).await.map_err(|e| { + error!(message = "error calling new_payload_v3 to validate builder payload", "url" = ?self.l2_client.auth_socket, "error" = %e, "payload_id" = %payload_id); e })?; if let Some(mut s) = span { @@ -495,7 +495,7 @@ impl EngineApiServer for RollupBoostServer { other_error => { error!( message = "error calling get_payload_v3", - builder_client.http_socket = ?self.builder_client.http_socket, + builder_client.http_socket = ?self.builder_client.auth_socket, "error" = %other_error, "payload_id" = %payload_id ); @@ -543,15 +543,15 @@ impl EngineApiServer for RollupBoostServer { let builder_payload = payload.clone(); let builder_versioned_hashes = versioned_hashes.clone(); tokio::spawn(async move { - let _ = builder.client.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await + let _ = builder.auth_client.new_payload_v3(builder_payload, builder_versioned_hashes, parent_beacon_block_root).await .map(|response: PayloadStatus| { if response.is_invalid() { - error!(message = "builder rejected new_payload_v3", "url" = ?builder.http_socket, "block_hash" = %block_hash); + error!(message = "builder rejected new_payload_v3", "url" = ?builder.auth_socket, "block_hash" = %block_hash); } else { - info!(message = "called new_payload_v3 to builder", "url" = ?builder.http_socket, "payload_status" = %response.status, "block_hash" = %block_hash); + info!(message = "called new_payload_v3 to builder", "url" = ?builder.auth_socket, "payload_status" = %response.status, "block_hash" = %block_hash); } }).map_err(|e| { - error!(message = "error calling new_payload_v3 to builder", "url" = ?builder.http_socket, "error" = %e, "block_hash" = %block_hash); + error!(message = "error calling new_payload_v3 to builder", "url" = ?builder.auth_socket, "error" = %e, "block_hash" = %block_hash); e }); if let Some(mut spans) = spans { @@ -560,7 +560,7 @@ impl EngineApiServer for RollupBoostServer { }); } self.l2_client - .client + .auth_client .new_payload_v3(payload, versioned_hashes, parent_beacon_block_root) .await .map_err(|e| match e { @@ -568,6 +568,7 @@ impl EngineApiServer for RollupBoostServer { other_error => { error!( message = "error calling new_payload_v3", + "url" = ?self.l2_client.auth_socket, "error" = %other_error, "block_hash" = %block_hash ); From 0d0bbd0668b0f7b0cb92c262ecf4eb5382df4e80 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 19:21:37 -0500 Subject: [PATCH 063/429] update builder and l2 args (flashbots/rollup-boost) --- Cargo.lock | 1 + Cargo.toml | 1 + src/main.rs | 36 ++++++++++++++++++------------------ src/rpc.rs | 41 +++++++++++++++++++++++++++++++++++++++++ src/server.rs | 12 ------------ 5 files changed, 61 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1c60c3833b80d..59fb82cad6979 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5192,6 +5192,7 @@ dependencies = [ "opentelemetry-http", "opentelemetry-otlp", "opentelemetry_sdk", + "paste", "predicates", "reqwest", "reth-optimism-payload-builder", diff --git a/Cargo.toml b/Cargo.toml index be7913aefee84..f3847b4d935f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ metrics-exporter-prometheus = "0.16.0" metrics-process = "2.3.1" metrics-util = "0.18.0" eyre = "0.6.12" +paste = "1.0.15" [dev-dependencies] anyhow = "1.0" diff --git a/src/main.rs b/src/main.rs index 9da51af74bd15..0215021d4dc36 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; -use rpc::{ExecutionClient, RpcClientArgs}; +use rpc::{BuilderArgs, ExecutionClient, L2ClientArgs, RpcClientArgs}; use server::{HttpClientWrapper, RollupBoostServer}; use std::sync::Arc; @@ -38,10 +38,10 @@ mod server; #[clap(group(ArgGroup::new("jwt").required(true).multiple(false).args(&["jwt_token", "jwt_path"])))] struct Args { #[clap(flatten)] - builder: RpcClientArgs, + builder: BuilderArgs, #[clap(flatten)] - l2_client: RpcClientArgs, + l2_client: L2ClientArgs, /// Use the proposer to sync the builder node #[arg(long, env, default_value = "false")] @@ -132,38 +132,38 @@ async fn main() -> eyre::Result<()> { init_tracing(&args.otlp_endpoint); } - let l2_jwt_secret = if let Some(jwt_secret) = args.l2_client.rpc_jwtsecret { + let l2_jwt_secret = if let Some(jwt_secret) = args.l2_client.l2_rpc_jwtsecret { jwt_secret - } else if let Some(path) = args.l2_client.auth_jwtsecret { + } else if let Some(path) = args.l2_client.l2_auth_jwtsecret { JwtSecret::from_file(&path)? } else { eyre::bail!("Either l2_client.rpc_jwtsecret or l2_client.auth_jwtsecret must be provided"); }; - let builder_jwt_secret = if let Some(jwt_secret) = args.builder.rpc_jwtsecret { + let builder_jwt_secret = if let Some(jwt_secret) = args.builder.builder_rpc_jwtsecret { jwt_secret - } else if let Some(path) = args.builder.auth_jwtsecret { + } else if let Some(path) = args.builder.builder_auth_jwtsecret { JwtSecret::from_file(&path)? } else { eyre::bail!("Either builder.rpc_jwtsecret or builder.auth_jwtsecret must be provided"); }; let l2_client = ExecutionClient::new( - args.l2_client.http_addr, - args.l2_client.http_port, - args.l2_client.auth_addr, - args.l2_client.auth_port, + args.l2_client.l2_http_addr, + args.l2_client.l2_http_port, + args.l2_client.l2_auth_addr, + args.l2_client.l2_auth_port, l2_jwt_secret, - args.l2_client.timeout, + args.l2_client.l2_timeout, )?; let builder_client = ExecutionClient::new( - args.builder.http_addr, - args.builder.http_port, - args.builder.auth_addr, - args.builder.auth_port, + args.builder.builder_http_addr, + args.builder.builder_http_port, + args.builder.builder_auth_addr, + args.builder.builder_auth_port, builder_jwt_secret, - args.builder.timeout, + args.builder.builder_timeout, )?; let rollup_boost = RollupBoostServer::new(l2_client, builder_client, args.boost_sync, metrics); @@ -173,7 +173,7 @@ async fn main() -> eyre::Result<()> { // server setup info!("Starting server on :{}", args.rpc_port); let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new( - args.l2_client.http_addr.to_string().parse::()?, + args.l2_client.l2_http_addr.to_string().parse::()?, )); let server = Server::builder() .set_http_middleware(service_builder) diff --git a/src/rpc.rs b/src/rpc.rs index f54aa6f424ab1..7a8c9dd496f77 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -17,6 +17,7 @@ use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; use opentelemetry::global::{self, BoxedSpan, BoxedTracer}; use opentelemetry::trace::{Span, TraceContextExt, Tracer}; use opentelemetry::{Context, KeyValue}; +use paste::paste; use reth_optimism_payload_builder::{OpPayloadAttributes, OpPayloadBuilderAttributes}; use reth_payload_primitives::PayloadBuilderAttributes; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; @@ -186,3 +187,43 @@ pub trait MinerApi { #[method(name = "setGasLimit")] async fn set_gas_limit(&self, gas_price: U128) -> RpcResult; } + +macro_rules! define_rpc_args { + ($(($name:ident, $prefix:ident)),*) => { + $( + paste! { + #[derive(Debug, Clone, Args, PartialEq, Eq)] + pub struct $name { + #[arg(long)] + pub [<$prefix _http_addr>]: IpAddr, + + #[arg(long)] + pub [<$prefix _http_port>]: u16, + + #[arg(long)] + pub [<$prefix _http_corsdomain>]: Option, + + #[arg(long)] + pub [<$prefix _auth_addr>]: IpAddr, + + #[arg(long)] + pub [<$prefix _auth_port>]: u16, + + #[arg(long, value_name = "PATH", global = true)] + pub [<$prefix _auth_jwtsecret>]: Option, + + #[arg(long)] + pub [<$prefix _auth_ipc_path>]: Option, + + #[arg(long, value_name = "HEX", global = true)] + pub [<$prefix _rpc_jwtsecret>]: Option, + + #[arg(long)] + pub [<$prefix _timeout>]: u64, + } + } + )* + }; +} + +define_rpc_args!((BuilderArgs, builder), (L2ClientArgs, l2)); diff --git a/src/server.rs b/src/server.rs index 398cbf9679483..752b559e97481 100644 --- a/src/server.rs +++ b/src/server.rs @@ -94,18 +94,6 @@ impl PayloadTraceContext { } } -#[derive(Clone)] -pub struct HttpClientWrapper>> { - pub client: C, - pub url: String, -} - -impl HttpClientWrapper { - pub fn new(client: C, url: String) -> Self { - Self { client, url } - } -} - #[derive(Clone)] pub struct RollupBoostServer { l2_client: Arc, From 65ad13910df669e02c1280c24a566c8e071c53b6 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 19:39:11 -0500 Subject: [PATCH 064/429] updated args (flashbots/rollup-boost) --- src/main.rs | 12 +++---- src/rpc.rs | 87 +++++++++------------------------------------------ src/server.rs | 12 +++++++ 3 files changed, 33 insertions(+), 78 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0215021d4dc36..4c4d848fb117e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,7 +18,7 @@ use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; -use rpc::{BuilderArgs, ExecutionClient, L2ClientArgs, RpcClientArgs}; +use rpc::{BuilderArgs, ExecutionClient, L2ClientArgs}; use server::{HttpClientWrapper, RollupBoostServer}; use std::sync::Arc; @@ -33,9 +33,9 @@ mod metrics; mod proxy; mod rpc; mod server; + #[derive(Parser, Debug)] #[clap(author, version, about)] -#[clap(group(ArgGroup::new("jwt").required(true).multiple(false).args(&["jwt_token", "jwt_path"])))] struct Args { #[clap(flatten)] builder: BuilderArgs, @@ -132,17 +132,17 @@ async fn main() -> eyre::Result<()> { init_tracing(&args.otlp_endpoint); } - let l2_jwt_secret = if let Some(jwt_secret) = args.l2_client.l2_rpc_jwtsecret { + let l2_jwt_secret = if let Some(jwt_secret) = args.l2_client.l2_jwtsecret { jwt_secret - } else if let Some(path) = args.l2_client.l2_auth_jwtsecret { + } else if let Some(path) = args.l2_client.l2_jwtsecret_path { JwtSecret::from_file(&path)? } else { eyre::bail!("Either l2_client.rpc_jwtsecret or l2_client.auth_jwtsecret must be provided"); }; - let builder_jwt_secret = if let Some(jwt_secret) = args.builder.builder_rpc_jwtsecret { + let builder_jwt_secret = if let Some(jwt_secret) = args.builder.builder_jwtsecret { jwt_secret - } else if let Some(path) = args.builder.builder_auth_jwtsecret { + } else if let Some(path) = args.builder.builder_jwtsecret_path { JwtSecret::from_file(&path)? } else { eyre::bail!("Either builder.rpc_jwtsecret or builder.auth_jwtsecret must be provided"); diff --git a/src/rpc.rs b/src/rpc.rs index 7a8c9dd496f77..98117b8490d14 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -35,71 +35,6 @@ use clap::{ }; use std::path::PathBuf; -/// Parameters for configuring the rpc more granularity via CLI -#[derive(Debug, Clone, Args, PartialEq, Eq)] -#[command(next_help_heading = "RPC")] -#[command(group( - ArgGroup::new("jwt") - .required(true) - .args(&["auth_jwtsecret", "rpc_jwtsecret"]), -))] -pub struct RpcClientArgs { - /// Http server address to listen on - #[arg(long = "http.addr")] - pub http_addr: IpAddr, - - /// Http server port to listen on - #[arg(long = "http.port")] - pub http_port: u16, - - /// Http Corsdomain to allow request from - #[arg(long = "http.corsdomain")] - pub http_corsdomain: Option, - - /// Auth server address to listen on - #[arg(long = "authrpc.addr")] - pub auth_addr: IpAddr, - - /// Auth server port to listen on - #[arg(long = "authrpc.port")] - pub auth_port: u16, - - /// Path to a JWT secret to use for the authenticated engine-API RPC server. - /// - /// This will enforce JWT authentication for all requests coming from the consensus layer. - /// - /// If no path is provided, a secret will be generated and stored in the datadir under - /// `//jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default. - #[arg( - long = "authrpc.jwtsecret", - value_name = "PATH", - global = true, - required = false - )] - pub auth_jwtsecret: Option, - - /// Filename for auth IPC socket/pipe within the datadir - #[arg(long = "auth-ipc.path")] - pub auth_ipc_path: Option, - - /// Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and - /// `--ws.api`. - /// - /// This is __not__ used for the authenticated engine-API RPC server, see - /// `--authrpc.jwtsecret`. - #[arg( - long = "rpc.jwtsecret", - value_name = "HEX", - global = true, - required = false - )] - pub rpc_jwtsecret: Option, - - /// Timeout for http calls in milliseconds - #[arg(long, env)] - pub timeout: u64, -} - pub struct ExecutionClient { pub client: HttpClient, pub http_socket: SocketAddr, @@ -188,11 +123,20 @@ pub trait MinerApi { async fn set_gas_limit(&self, gas_price: U128) -> RpcResult; } +/// Generates Clap argument structs with a prefix to create a unique namespace when specifing RPC client config via the CLI. macro_rules! define_rpc_args { ($(($name:ident, $prefix:ident)),*) => { $( paste! { - #[derive(Debug, Clone, Args, PartialEq, Eq)] + #[derive(Parser, Debug, Clone, PartialEq, Eq)] + #[clap(group(ArgGroup::new(concat!(stringify!($prefix), "_jwt")) + .required(true) + .multiple(false) + .args(&[ + concat!(stringify!($prefix), "_jwtsecret"), + concat!(stringify!($prefix), "_jwtsecret_path") + ]) + ))] pub struct $name { #[arg(long)] pub [<$prefix _http_addr>]: IpAddr, @@ -200,23 +144,22 @@ macro_rules! define_rpc_args { #[arg(long)] pub [<$prefix _http_port>]: u16, - #[arg(long)] - pub [<$prefix _http_corsdomain>]: Option, - #[arg(long)] pub [<$prefix _auth_addr>]: IpAddr, #[arg(long)] pub [<$prefix _auth_port>]: u16, + #[arg(long, value_name = "HEX", global = true)] + pub [<$prefix _jwtsecret>]: Option, + #[arg(long, value_name = "PATH", global = true)] - pub [<$prefix _auth_jwtsecret>]: Option, + pub [<$prefix _jwtsecret_path>]: Option, #[arg(long)] pub [<$prefix _auth_ipc_path>]: Option, - #[arg(long, value_name = "HEX", global = true)] - pub [<$prefix _rpc_jwtsecret>]: Option, + #[arg(long)] pub [<$prefix _timeout>]: u64, diff --git a/src/server.rs b/src/server.rs index 752b559e97481..398cbf9679483 100644 --- a/src/server.rs +++ b/src/server.rs @@ -94,6 +94,18 @@ impl PayloadTraceContext { } } +#[derive(Clone)] +pub struct HttpClientWrapper>> { + pub client: C, + pub url: String, +} + +impl HttpClientWrapper { + pub fn new(client: C, url: String) -> Self { + Self { client, url } + } +} + #[derive(Clone)] pub struct RollupBoostServer { l2_client: Arc, From e536ad3d261286d76237a9bba8a103805510dca7 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 19:42:56 -0500 Subject: [PATCH 065/429] update comments (flashbots/rollup-boost) --- src/rpc.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/rpc.rs b/src/rpc.rs index 98117b8490d14..e57643a440c6f 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -138,29 +138,43 @@ macro_rules! define_rpc_args { ]) ))] pub struct $name { + /// Http server address #[arg(long)] pub [<$prefix _http_addr>]: IpAddr, + /// Http server port #[arg(long)] pub [<$prefix _http_port>]: u16, + /// Auth server address #[arg(long)] pub [<$prefix _auth_addr>]: IpAddr, + /// Auth server port #[arg(long)] pub [<$prefix _auth_port>]: u16, + /// Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and + /// `--ws.api`. + /// + /// This is __not__ used for the authenticated engine-API RPC server, see + /// `--authrpc.jwtsecret`. + // TODO: #[arg(long, value_name = "HEX", global = true)] pub [<$prefix _jwtsecret>]: Option, + /// Path to a JWT secret to use for the authenticated engine-API RPC server. + /// + /// If no path is provided, a secret will be generated and stored in the datadir under + /// `//jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default. #[arg(long, value_name = "PATH", global = true)] pub [<$prefix _jwtsecret_path>]: Option, + /// Filename for auth IPC socket/pipe within the datadir #[arg(long)] pub [<$prefix _auth_ipc_path>]: Option, - - + /// Timeout for http calls in milliseconds #[arg(long)] pub [<$prefix _timeout>]: u64, } From a1fa00295d0aa9cdc8b14eb5ccb1f4cc36f70b5c Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 20:10:42 -0500 Subject: [PATCH 066/429] wip, fixing auth layer (flashbots/rollup-boost) --- src/main.rs | 22 ++++------------------ src/rpc.rs | 34 ++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/src/main.rs b/src/main.rs index 4c4d848fb117e..87727f4d1c037 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,37 +132,23 @@ async fn main() -> eyre::Result<()> { init_tracing(&args.otlp_endpoint); } - let l2_jwt_secret = if let Some(jwt_secret) = args.l2_client.l2_jwtsecret { - jwt_secret - } else if let Some(path) = args.l2_client.l2_jwtsecret_path { - JwtSecret::from_file(&path)? - } else { - eyre::bail!("Either l2_client.rpc_jwtsecret or l2_client.auth_jwtsecret must be provided"); - }; - - let builder_jwt_secret = if let Some(jwt_secret) = args.builder.builder_jwtsecret { - jwt_secret - } else if let Some(path) = args.builder.builder_jwtsecret_path { - JwtSecret::from_file(&path)? - } else { - eyre::bail!("Either builder.rpc_jwtsecret or builder.auth_jwtsecret must be provided"); - }; - let l2_client = ExecutionClient::new( args.l2_client.l2_http_addr, args.l2_client.l2_http_port, + args.l2_client.l2_rpc_jwtsecret, args.l2_client.l2_auth_addr, args.l2_client.l2_auth_port, - l2_jwt_secret, + args.l2_client.l2_auth_rpc_jwtsecret, args.l2_client.l2_timeout, )?; let builder_client = ExecutionClient::new( args.builder.builder_http_addr, args.builder.builder_http_port, + args.builder.builder_rpc_jwtsecret, args.builder.builder_auth_addr, args.builder.builder_auth_port, - builder_jwt_secret, + args.builder.builder_auth_rpc_jwtsecret, args.builder.builder_timeout, )?; diff --git a/src/rpc.rs b/src/rpc.rs index e57643a440c6f..40ddd0d9b8ca3 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -35,10 +35,13 @@ use clap::{ }; use std::path::PathBuf; -pub struct ExecutionClient { - pub client: HttpClient, +pub struct ExecutionClient< + C = HttpClient, + A = HttpClient>, +> { + pub client: C, pub http_socket: SocketAddr, - pub auth_client: HttpClient>, + pub auth_client: A, pub auth_socket: SocketAddr, } @@ -46,17 +49,20 @@ impl ExecutionClient { pub fn new( http_addr: IpAddr, http_port: u16, + rpc_jwtsecret: Option, auth_addr: IpAddr, auth_port: u16, - jwt_secret: JwtSecret, + auth_rpc_jwtsecret: JwtSecret, timeout: u64, ) -> Result { + // TODO: add optional auth layer for regular rpc + let http_socket = SocketAddr::new(http_addr, http_port); let client = HttpClientBuilder::new() .request_timeout(Duration::from_millis(timeout)) .build(format!("http://{}", http_socket))?; - let auth_layer = AuthClientLayer::new(jwt_secret); + let auth_layer = AuthClientLayer::new(auth_rpc_jwtsecret); let auth_socket = SocketAddr::new(auth_addr, auth_port); let auth_client = HttpClientBuilder::new() .set_http_middleware(tower::ServiceBuilder::new().layer(auth_layer)) @@ -154,21 +160,17 @@ macro_rules! define_rpc_args { #[arg(long)] pub [<$prefix _auth_port>]: u16, - /// Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and - /// `--ws.api`. + /// Path to a JWT secret to use for the authenticated engine-API RPC server. + #[arg(long, value_name = "PATH", global = true)] + pub [<$prefix _auth_rpc_jwtsecret>]: JwtSecret, + + /// Hex encoded JWT secret to authenticate the regular RPC server(s) /// /// This is __not__ used for the authenticated engine-API RPC server, see - /// `--authrpc.jwtsecret`. - // TODO: + /// `authrpc.jwtsecret`. #[arg(long, value_name = "HEX", global = true)] - pub [<$prefix _jwtsecret>]: Option, + pub [<$prefix _rpc_jwtsecret>]: Option, - /// Path to a JWT secret to use for the authenticated engine-API RPC server. - /// - /// If no path is provided, a secret will be generated and stored in the datadir under - /// `//jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default. - #[arg(long, value_name = "PATH", global = true)] - pub [<$prefix _jwtsecret_path>]: Option, /// Filename for auth IPC socket/pipe within the datadir #[arg(long)] From 484c30e3c447946b6d4c8b03aba3a931e9759379 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 21:42:58 -0500 Subject: [PATCH 067/429] update use of auth rpc (flashbots/rollup-boost) --- src/main.rs | 2 -- src/rpc.rs | 18 +++++++----------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/main.rs b/src/main.rs index 87727f4d1c037..74eb3a42491ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -135,7 +135,6 @@ async fn main() -> eyre::Result<()> { let l2_client = ExecutionClient::new( args.l2_client.l2_http_addr, args.l2_client.l2_http_port, - args.l2_client.l2_rpc_jwtsecret, args.l2_client.l2_auth_addr, args.l2_client.l2_auth_port, args.l2_client.l2_auth_rpc_jwtsecret, @@ -145,7 +144,6 @@ async fn main() -> eyre::Result<()> { let builder_client = ExecutionClient::new( args.builder.builder_http_addr, args.builder.builder_http_port, - args.builder.builder_rpc_jwtsecret, args.builder.builder_auth_addr, args.builder.builder_auth_port, args.builder.builder_auth_rpc_jwtsecret, diff --git a/src/rpc.rs b/src/rpc.rs index 40ddd0d9b8ca3..dea0c38b77169 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -4,6 +4,7 @@ use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, }; +use jsonrpsee::core::client::ClientT; use jsonrpsee::core::{async_trait, ClientError, RegisterMethodError, RpcResult}; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; @@ -35,13 +36,11 @@ use clap::{ }; use std::path::PathBuf; -pub struct ExecutionClient< - C = HttpClient, - A = HttpClient>, -> { - pub client: C, +pub struct ExecutionClient { + // TODO: add support for optional auth rpc (eth api, miner api, etc.) + pub client: HttpClient, pub http_socket: SocketAddr, - pub auth_client: A, + pub auth_client: HttpClient>, pub auth_socket: SocketAddr, } @@ -49,20 +48,17 @@ impl ExecutionClient { pub fn new( http_addr: IpAddr, http_port: u16, - rpc_jwtsecret: Option, auth_addr: IpAddr, auth_port: u16, - auth_rpc_jwtsecret: JwtSecret, + auth_rpc_jwt_secret: JwtSecret, timeout: u64, ) -> Result { - // TODO: add optional auth layer for regular rpc - let http_socket = SocketAddr::new(http_addr, http_port); let client = HttpClientBuilder::new() .request_timeout(Duration::from_millis(timeout)) .build(format!("http://{}", http_socket))?; - let auth_layer = AuthClientLayer::new(auth_rpc_jwtsecret); + let auth_layer = AuthClientLayer::new(auth_rpc_jwt_secret); let auth_socket = SocketAddr::new(auth_addr, auth_port); let auth_client = HttpClientBuilder::new() .set_http_middleware(tower::ServiceBuilder::new().layer(auth_layer)) From 268bddf2139dd3e67c06c0c45010aca15081813b Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Wed, 29 Jan 2025 21:44:33 -0500 Subject: [PATCH 068/429] removed unused imports (flashbots/rollup-boost) --- src/main.rs | 4 ++-- src/rpc.rs | 32 +++++--------------------------- src/server.rs | 7 ++----- 3 files changed, 9 insertions(+), 34 deletions(-) diff --git a/src/main.rs b/src/main.rs index 74eb3a42491ca..eb8be50809ab0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use clap::{arg, ArgGroup, Parser}; +use clap::{arg, Parser}; use dotenv::dotenv; use http::{StatusCode, Uri}; use hyper::service::service_fn; @@ -21,9 +21,9 @@ use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use rpc::{BuilderArgs, ExecutionClient, L2ClientArgs}; use server::{HttpClientWrapper, RollupBoostServer}; +use std::net::SocketAddr; use std::sync::Arc; use std::time::Duration; -use std::{net::SocketAddr, path::PathBuf}; use tokio::net::TcpListener; use tracing::error; use tracing::{info, Level}; diff --git a/src/rpc.rs b/src/rpc.rs index dea0c38b77169..55f70edfd4bd5 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -1,40 +1,18 @@ -use crate::metrics::ServerMetrics; -use alloy_primitives::{Bytes, B256, U128, U64}; +use alloy_primitives::{Bytes, B256, U128}; use alloy_rpc_types_engine::{ - ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, - PayloadStatus, + ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, }; -use jsonrpsee::core::client::ClientT; -use jsonrpsee::core::{async_trait, ClientError, RegisterMethodError, RpcResult}; +use clap::{arg, ArgGroup, Parser}; +use jsonrpsee::core::RpcResult; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::proc_macros::rpc; -use jsonrpsee::types::error::INVALID_REQUEST_CODE; -use jsonrpsee::types::{ErrorCode, ErrorObject}; -use jsonrpsee::RpcModule; -use lru::LruCache; -use op_alloy_rpc_jsonrpsee::traits::{MinerApiExtClient, MinerApiExtServer}; use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; -use opentelemetry::global::{self, BoxedSpan, BoxedTracer}; -use opentelemetry::trace::{Span, TraceContextExt, Tracer}; -use opentelemetry::{Context, KeyValue}; use paste::paste; -use reth_optimism_payload_builder::{OpPayloadAttributes, OpPayloadBuilderAttributes}; -use reth_payload_primitives::PayloadBuilderAttributes; +use reth_optimism_payload_builder::OpPayloadAttributes; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use std::net::{IpAddr, SocketAddr}; -use std::num::NonZero; -use std::sync::Arc; use std::time::Duration; -use tokio::sync::Mutex; -use tracing::{debug, error, info}; - -use clap::{arg, ArgGroup, Parser}; -use clap::{ - builder::{PossibleValue, RangedU64ValueParser, TypedValueParser}, - Arg, Args, Command, -}; -use std::path::PathBuf; pub struct ExecutionClient { // TODO: add support for optional auth rpc (eth api, miner api, etc.) diff --git a/src/server.rs b/src/server.rs index 398cbf9679483..28c57cee414b5 100644 --- a/src/server.rs +++ b/src/server.rs @@ -10,8 +10,7 @@ use alloy_rpc_types_engine::{ }; use jsonrpsee::core::{async_trait, ClientError, RegisterMethodError, RpcResult}; use jsonrpsee::http_client::transport::HttpBackend; -use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; -use jsonrpsee::proc_macros::rpc; +use jsonrpsee::http_client::HttpClient; use jsonrpsee::types::error::INVALID_REQUEST_CODE; use jsonrpsee::types::{ErrorCode, ErrorObject}; use jsonrpsee::RpcModule; @@ -23,11 +22,9 @@ use opentelemetry::trace::{Span, TraceContextExt, Tracer}; use opentelemetry::{Context, KeyValue}; use reth_optimism_payload_builder::{OpPayloadAttributes, OpPayloadBuilderAttributes}; use reth_payload_primitives::PayloadBuilderAttributes; -use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; -use std::net::{IpAddr, SocketAddr}; +use reth_rpc_layer::AuthClientService; use std::num::NonZero; use std::sync::Arc; -use std::time::Duration; use tokio::sync::Mutex; use tracing::{debug, error, info}; From 99c49bcbff238567d3ebea0154d20fff140fb3c2 Mon Sep 17 00:00:00 2001 From: avalonche Date: Thu, 30 Jan 2025 20:21:33 +1100 Subject: [PATCH 069/429] README improvements (flashbots/rollup-boost) --- README.md | 103 +++++++++++++++++++++++++------- assets/workflow.svg | 3 - docs/high-availability-setup.md | 0 docs/local-devnet.md | 0 docs/running-rollup-boost.md | 0 5 files changed, 82 insertions(+), 24 deletions(-) delete mode 100644 assets/workflow.svg create mode 100644 docs/high-availability-setup.md create mode 100644 docs/local-devnet.md create mode 100644 docs/running-rollup-boost.md diff --git a/README.md b/README.md index d64fb655dc721..7deb4301e19dc 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ # Rollup Boost -This repo implements a proxy server for the Ethereum Engine API and is under active development. To read more about the design, check out the [design doc](https://github.com/ethereum-optimism/design-docs/pull/86). - +This is an implementation the block builder sidecar for Optimism Stack to enable external block production. To read more about the design, check out the [design doc](https://github.com/ethereum-optimism/design-docs/blob/main/protocol/external-block-production.md). ## Usage -Run the server using the following command: +Run the rollup-boost server using the following command: ``` cargo run -- [OPTIONS] @@ -33,28 +32,90 @@ You can also set the options using environment variables. See .env.example to us ### Example ``` -cargo run --jwt-token your_jwt_token --l2-url http://localhost:8545 --builder-url http://localhost:8546 +cargo run --jwt-token your_jwt_token --l2-url http://localhost:8551 --builder-url http://localhost:8546 ``` ## Core System Workflow -1. By default, `rollup-boost` forwards all JSON-RPC API calls from `proposer-op-node` to `proposer-op-geth`. -2. When `rollup-boost` receives an `engine_FCU` with attributes (initiating block building): - - It relays the call to `proposer-op-geth` as usual. - - If `builder-op-geth` is synced to the chain tip, the call is also multiplexed to it. - - The FCU call returns a PayloadID, which should be identical for both `proposer-op-geth` and `builder-op-geth`. -3. When `rollup-boost` receives an `engine_getPayload`: - - It first queries `proposer-op-geth` for a fallback block. - - In parallel, it queries `builder-op-geth`. -4. Upon receiving the `builder-op-geth` block: - - `rollup-boost` validates the block with `proposer-op-geth` using `engine_newPayload`. - - This validation ensures the block will be valid for `proposer-op-geth`, preventing network stalls due to invalid blocks. - - If the external block is valid, it is returned to the `proposer-op-node`. -5. As per its normal workflow, the `proposer-op-node` sends another `newPayload` request to the `rollup-boost` and another FCU(without) to update the state of its op-geth node. - - In this case, the `rollup-boost` just relays the data and does not introspect anything. - - Note that since we already tested `newPayload` on the proposer-op-geth in the previous step, this process should be cached. - -![Workflow Diagram](/assets/workflow.svg) +1. `rollup-boost` receives an `engine_FCU` with the attributes to initiate block building: + - It relays the call to proposer `op-geth` as usual and multiplexes the call to builder. + - The FCU call returns the proposer payload id and internally maps the builder payload id to proposer payload id in the case the payload ids are not the same. +2. When `rollup-boost` receives an `engine_getPayload`: + - It queries proposer `op-geth` for a fallback block. + - In parallel, it queries builder for a block. +3. Upon receiving the builder block: + - `rollup-boost` validates the block with proposer `op-geth` using `engine_newPayload`. + - This validation ensures the block will be valid for proposer `op-geth`, preventing network stalls due to invalid blocks. + - If the external block is valid, it is returned to the proposer `op-node`. Otherwise, `rollup-boost` will return the fallback block. +4. The proposer `op-node` sends a `engine_newPayload` request to `rollup-boost` and another `engine_FCU` without attributes to update chain state. + - `rollup-boost` just relays the calls to proposer `op-geth`. + - Note that since we already called `engine_newPayload` on the proposer `op-geth` in the previous step, the block should be cached and add minimal latency. + - The builder `op-node` will receive blocks via p2p gossip and keep the builder node in sync via the engine api. + +```mermaid +sequenceDiagram + box Proposer + participant op-node + participant rollup-boost + participant op-geth + end + box Builder + participant builder-op-node as op-node + participant builder-op-geth as builder + end + + Note over op-node, builder-op-geth: 1. Triggering Block Building + op-node->>rollup-boost: engine_FCU (with attrs) + rollup-boost->>op-geth: engine_FCU (with attrs) + rollup-boost->>builder-op-geth: engine_FCU (with attrs) + rollup-boost->>op-node: proposer payload id + + Note over op-node, builder-op-geth: 2. Get Local and Builder Blocks + op-node->>rollup-boost: engine_getPayload + rollup-boost->>op-geth: engine_getPayload + rollup-boost->>builder-op-geth: engine_getPayload + + Note over op-node, builder-op-geth: 3. Validating and Returning Builder Block + rollup-boost->>op-geth: engine_newPayload + op-geth->>rollup-boost: block validity + rollup-boost->>op-node: block payload + + Note over op-node, builder-op-geth: 4. Updating Chain State + op-node->>rollup-boost: engine_newPayload + rollup-boost->>op-geth: engine_newPayload + op-node->>rollup-boost: engine_FCU (without attrs) + rollup-boost->>op-geth: engine_FCU (without attrs) +``` + +## RPC Calls + +By default, `rollup-boost` will proxy all RPC calls from the proposer `op-node` to its local `op-geth` node. These are the list of RPC calls that are proxied to both the proposer and the builder execution engines: + +- `engine_forkchoiceUpdatedV3`: this call is only multiplexed to the builder if the call contains payload attributes and the no_tx_pool attribute is false. +- `engine_getPayloadV3`: this is used to get the builder block. +- `miner_*`: this allows the builder to be aware of changes in effective gas price, extra data, and [DA throttling requests](https://docs.optimism.io/builders/chain-operators/configuration/batcher) from the batcher. +- `eth_sendRawTransaction*`: this forwards transactions the proposer receives to the builder for block building. This call may not come from the proposer `op-node`, but directly from the rollup's rpc engine. + +### Boost Sync + +`rollup-boost` will sync the builder with the proposer `op-node` if the `--boost-sync` flag is set. After the builder is synced, boost sync improves the performance of keeping the builder in sync by removing the need to receive chain updates via p2p with the builder `op-node`. This entails additional engine api calls that are multiplexed to the builder from rollup-boost: + +- `engine_forkchoiceUpdatedV3`: this call will be multiplexed to the builder regardless of whether the call contains payload attributes or not. +- `engine_newPayloadV3`: ensures the builder has the latest block if the local payload was used. + +## Observability + +To check if the rollup-boost server is running, you can check the health endpoint: + +``` +curl http://localhost:8081/healthz +``` + +To enable metrics, you can set the `--metrics` flag. This will start a metrics server which will run on port 9090 by default. To see list of metrics, you can check the metrics endpoint: + +``` +curl http://localhost:9090/metrics +``` ## License The code in this project is free software under the [MIT License](/LICENSE). diff --git a/assets/workflow.svg b/assets/workflow.svg deleted file mode 100644 index 7ef3fb0a8368c..0000000000000 --- a/assets/workflow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - -BuilderProposerbuilder-Op-gethbuilder-Op-nodeSidecarOp-gethOp-nodebuilder-Op-gethbuilder-Op-nodeSidecarOp-gethOp-node12.par3.4.par5.eth_xxxeth_xxxFCU(with)FCU(with)PayloadIDFCU(with)PayloadIDPaylodIDWait for block timeGetPayloadV3GetPayloadV3ExecPayloadGetPayloadV3ExecPayloadNewPayloadV3ExecPayloadnewPayloadnewPayloadFCU(without)FCU(without) \ No newline at end of file diff --git a/docs/high-availability-setup.md b/docs/high-availability-setup.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/docs/local-devnet.md b/docs/local-devnet.md new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/docs/running-rollup-boost.md b/docs/running-rollup-boost.md new file mode 100644 index 0000000000000..e69de29bb2d1d From ca1f012816e20c2f2c7e386b7344cea5f14b0c8f Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 08:02:13 -0500 Subject: [PATCH 070/429] refactor, update execution client to take generic ClientT (flashbots/rollup-boost) --- src/{rpc.rs => client.rs} | 91 ++++++++++++++++----------------------- src/main.rs | 4 +- src/server.rs | 64 +++++++++++++++++++++++++-- 3 files changed, 100 insertions(+), 59 deletions(-) rename src/{rpc.rs => client.rs} (65%) diff --git a/src/rpc.rs b/src/client.rs similarity index 65% rename from src/rpc.rs rename to src/client.rs index 55f70edfd4bd5..3ab888e732b4f 100644 --- a/src/rpc.rs +++ b/src/client.rs @@ -3,6 +3,7 @@ use alloy_rpc_types_engine::{ ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, }; use clap::{arg, ArgGroup, Parser}; +use jsonrpsee::core::client::ClientT; use jsonrpsee::core::RpcResult; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; @@ -14,11 +15,13 @@ use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use std::net::{IpAddr, SocketAddr}; use std::time::Duration; -pub struct ExecutionClient { - // TODO: add support for optional auth rpc (eth api, miner api, etc.) - pub client: HttpClient, +pub struct ExecutionClient< + C: ClientT = HttpClient, + A: ClientT = HttpClient>, +> { + pub client: C, pub http_socket: SocketAddr, - pub auth_client: HttpClient>, + pub auth_client: A, pub auth_socket: SocketAddr, } @@ -52,55 +55,37 @@ impl ExecutionClient { } } -#[rpc(server, client, namespace = "engine")] -pub trait EngineApi { - #[method(name = "forkchoiceUpdatedV3")] - async fn fork_choice_updated_v3( - &self, - fork_choice_state: ForkchoiceState, - payload_attributes: Option, - ) -> RpcResult; - - #[method(name = "getPayloadV3")] - async fn get_payload_v3( - &self, - payload_id: PayloadId, - ) -> RpcResult; - - #[method(name = "newPayloadV3")] - async fn new_payload_v3( - &self, - payload: ExecutionPayloadV3, - versioned_hashes: Vec, - parent_beacon_block_root: B256, - ) -> RpcResult; -} +impl ExecutionClient>> { + pub fn new_with_auth( + http_addr: IpAddr, + http_port: u16, + rpc_jwt_secret: JwtSecret, + auth_addr: IpAddr, + auth_port: u16, + auth_rpc_jwt_secret: JwtSecret, + timeout: u64, + ) -> Result { + let rpc_auth_layer = AuthClientLayer::new(rpc_jwt_secret); + let http_socket = SocketAddr::new(http_addr, http_port); + let client = HttpClientBuilder::new() + .set_http_middleware(tower::ServiceBuilder::new().layer(rpc_auth_layer)) + .request_timeout(Duration::from_millis(timeout)) + .build(format!("http://{}", http_socket))?; -#[rpc(server, client, namespace = "eth")] -pub trait EthApi { - #[method(name = "sendRawTransaction")] - async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult; -} + let auth_layer = AuthClientLayer::new(auth_rpc_jwt_secret); + let auth_socket = SocketAddr::new(auth_addr, auth_port); + let auth_client = HttpClientBuilder::new() + .set_http_middleware(tower::ServiceBuilder::new().layer(auth_layer)) + .request_timeout(Duration::from_millis(timeout)) + .build(format!("http://{}", auth_socket))?; -/*TODO: Remove this in favor of the `MinerApi` from Reth once the - trait methods are updated to be async -*/ -/// Miner namespace rpc interface that can control miner/builder settings -#[rpc(server, client, namespace = "miner")] -pub trait MinerApi { - /// Sets the extra data string that is included when this miner mines a block. - /// - /// Returns an error if the extra data is too long. - #[method(name = "setExtra")] - async fn set_extra(&self, record: Bytes) -> RpcResult; - - /// Sets the minimum accepted gas price for the miner. - #[method(name = "setGasPrice")] - async fn set_gas_price(&self, gas_price: U128) -> RpcResult; - - /// Sets the gaslimit to target towards during mining. - #[method(name = "setGasLimit")] - async fn set_gas_limit(&self, gas_price: U128) -> RpcResult; + Ok(Self { + client, + http_socket, + auth_client, + auth_socket, + }) + } } /// Generates Clap argument structs with a prefix to create a unique namespace when specifing RPC client config via the CLI. @@ -135,14 +120,14 @@ macro_rules! define_rpc_args { pub [<$prefix _auth_port>]: u16, /// Path to a JWT secret to use for the authenticated engine-API RPC server. - #[arg(long, value_name = "PATH", global = true)] + #[arg(long, value_name = "PATH")] pub [<$prefix _auth_rpc_jwtsecret>]: JwtSecret, /// Hex encoded JWT secret to authenticate the regular RPC server(s) /// /// This is __not__ used for the authenticated engine-API RPC server, see /// `authrpc.jwtsecret`. - #[arg(long, value_name = "HEX", global = true)] + #[arg(long, value_name = "HEX")] pub [<$prefix _rpc_jwtsecret>]: Option, diff --git a/src/main.rs b/src/main.rs index eb8be50809ab0..37388c1501261 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use clap::{arg, Parser}; +use client::{BuilderArgs, ExecutionClient, L2ClientArgs}; use dotenv::dotenv; use http::{StatusCode, Uri}; use hyper::service::service_fn; @@ -18,7 +19,6 @@ use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; -use rpc::{BuilderArgs, ExecutionClient, L2ClientArgs}; use server::{HttpClientWrapper, RollupBoostServer}; use std::net::SocketAddr; @@ -29,9 +29,9 @@ use tracing::error; use tracing::{info, Level}; use tracing_subscriber::EnvFilter; +mod client; mod metrics; mod proxy; -mod rpc; mod server; #[derive(Parser, Debug)] diff --git a/src/server.rs b/src/server.rs index 28c57cee414b5..0133eeebfaba8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,8 +1,5 @@ +use crate::client::ExecutionClient; use crate::metrics::ServerMetrics; -use crate::rpc::{ - EngineApiClient, EngineApiServer, EthApiClient, EthApiServer, ExecutionClient, MinerApiClient, - MinerApiServer, -}; use alloy_primitives::{Bytes, B256, U128, U64}; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, @@ -28,6 +25,14 @@ use std::sync::Arc; use tokio::sync::Mutex; use tracing::{debug, error, info}; +use clap::{arg, ArgGroup, Parser}; +use jsonrpsee::core::client::ClientT; +use jsonrpsee::http_client::HttpClientBuilder; +use jsonrpsee::proc_macros::rpc; +use reth_rpc_layer::{AuthClientLayer, JwtSecret}; +use std::net::{IpAddr, SocketAddr}; +use std::time::Duration; + const CACHE_SIZE: usize = 100; struct PayloadTraceContext { @@ -147,6 +152,12 @@ impl TryInto> for RollupBoostServer { } } +#[rpc(server, client, namespace = "eth")] +pub trait EthApi { + #[method(name = "sendRawTransaction")] + async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult; +} + #[async_trait] impl EthApiServer for RollupBoostServer { async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult { @@ -185,6 +196,27 @@ impl EthApiServer for RollupBoostServer { } } +/*TODO: Remove this in favor of the `MinerApi` from Reth once the + trait methods are updated to be async +*/ +/// Miner namespace rpc interface that can control miner/builder settings +#[rpc(server, client, namespace = "miner")] +pub trait MinerApi { + /// Sets the extra data string that is included when this miner mines a block. + /// + /// Returns an error if the extra data is too long. + #[method(name = "setExtra")] + async fn set_extra(&self, record: Bytes) -> RpcResult; + + /// Sets the minimum accepted gas price for the miner. + #[method(name = "setGasPrice")] + async fn set_gas_price(&self, gas_price: U128) -> RpcResult; + + /// Sets the gaslimit to target towards during mining. + #[method(name = "setGasLimit")] + async fn set_gas_limit(&self, gas_price: U128) -> RpcResult; +} + #[async_trait] impl MinerApiServer for RollupBoostServer { async fn set_extra(&self, record: Bytes) -> RpcResult { @@ -315,6 +347,30 @@ impl MinerApiExtServer for RollupBoostServer { } } +#[rpc(server, client, namespace = "engine")] +pub trait EngineApi { + #[method(name = "forkchoiceUpdatedV3")] + async fn fork_choice_updated_v3( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + ) -> RpcResult; + + #[method(name = "getPayloadV3")] + async fn get_payload_v3( + &self, + payload_id: PayloadId, + ) -> RpcResult; + + #[method(name = "newPayloadV3")] + async fn new_payload_v3( + &self, + payload: ExecutionPayloadV3, + versioned_hashes: Vec, + parent_beacon_block_root: B256, + ) -> RpcResult; +} + #[async_trait] impl EngineApiServer for RollupBoostServer { async fn fork_choice_updated_v3( From 03c44e334137769afffe8ac2e44351708d02c67f Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 08:52:14 -0500 Subject: [PATCH 071/429] add comments (flashbots/rollup-boost) --- src/client.rs | 15 +++++++++++++-- src/main.rs | 34 +++++++++++++++++++--------------- src/server.rs | 22 +++++++++++++++------- 3 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/client.rs b/src/client.rs index 3ab888e732b4f..3dc6990b1caba 100644 --- a/src/client.rs +++ b/src/client.rs @@ -15,17 +15,27 @@ use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use std::net::{IpAddr, SocketAddr}; use std::time::Duration; +/// Client interface for interacting with an execution layer node. +/// +/// - **Engine API** calls are faciliated via the `auth_client` (requires JWT authentication). +/// - All other API calls including the **Eth & Miner APIs** are faciliated via the `client` (optional JWT authentication). +/// pub struct ExecutionClient< C: ClientT = HttpClient, A: ClientT = HttpClient>, > { + /// Handles requests to Eth, Miner, and other Execution Layer APIs (optional JWT authentication) pub client: C, + /// Address of the RPC server for execution layer API calls, excluding the Engine API pub http_socket: SocketAddr, + /// Handles requests to the authenticated Engine API (requires JWT authentication) pub auth_client: A, + /// Address of the RPC server for authenticated Engine API calls pub auth_socket: SocketAddr, } impl ExecutionClient { + /// Initializes a new [ExecutionClient] with JWT auth for the Engine API and without auth for general Execution Layer APIs. pub fn new( http_addr: IpAddr, http_port: u16, @@ -56,6 +66,7 @@ impl ExecutionClient { } impl ExecutionClient>> { + /// Initializes a new [ExecutionClient] with JWT auth for the Engine API and general Execution Layer APIs. pub fn new_with_auth( http_addr: IpAddr, http_port: u16, @@ -121,14 +132,14 @@ macro_rules! define_rpc_args { /// Path to a JWT secret to use for the authenticated engine-API RPC server. #[arg(long, value_name = "PATH")] - pub [<$prefix _auth_rpc_jwtsecret>]: JwtSecret, + pub [<$prefix _auth_rpc_jwt_secret>]: JwtSecret, /// Hex encoded JWT secret to authenticate the regular RPC server(s) /// /// This is __not__ used for the authenticated engine-API RPC server, see /// `authrpc.jwtsecret`. #[arg(long, value_name = "HEX")] - pub [<$prefix _rpc_jwtsecret>]: Option, + pub [<$prefix _rpc_jwt_secret>]: JwtSecret, /// Filename for auth IPC socket/pipe within the datadir diff --git a/src/main.rs b/src/main.rs index 37388c1501261..ea8c317d95d64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,22 +132,26 @@ async fn main() -> eyre::Result<()> { init_tracing(&args.otlp_endpoint); } - let l2_client = ExecutionClient::new( - args.l2_client.l2_http_addr, - args.l2_client.l2_http_port, - args.l2_client.l2_auth_addr, - args.l2_client.l2_auth_port, - args.l2_client.l2_auth_rpc_jwtsecret, - args.l2_client.l2_timeout, + let l2_client_args = args.l2_client; + let l2_client = ExecutionClient::new_with_auth( + l2_client_args.l2_http_addr, + l2_client_args.l2_http_port, + l2_client_args.l2_rpc_jwt_secret, + l2_client_args.l2_auth_addr, + l2_client_args.l2_auth_port, + l2_client_args.l2_auth_rpc_jwt_secret, + l2_client_args.l2_timeout, )?; - let builder_client = ExecutionClient::new( - args.builder.builder_http_addr, - args.builder.builder_http_port, - args.builder.builder_auth_addr, - args.builder.builder_auth_port, - args.builder.builder_auth_rpc_jwtsecret, - args.builder.builder_timeout, + let builder_args = args.builder; + let builder_client = ExecutionClient::new_with_auth( + builder_args.builder_http_addr, + builder_args.builder_http_port, + builder_args.builder_rpc_jwt_secret, + builder_args.builder_auth_addr, + builder_args.builder_auth_port, + builder_args.builder_auth_rpc_jwt_secret, + builder_args.builder_timeout, )?; let rollup_boost = RollupBoostServer::new(l2_client, builder_client, args.boost_sync, metrics); @@ -157,7 +161,7 @@ async fn main() -> eyre::Result<()> { // server setup info!("Starting server on :{}", args.rpc_port); let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new( - args.l2_client.l2_http_addr.to_string().parse::()?, + l2_client_args.l2_http_addr.to_string().parse::()?, )); let server = Server::builder() .set_http_middleware(service_builder) diff --git a/src/server.rs b/src/server.rs index 0133eeebfaba8..744b95b5a604d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -109,18 +109,22 @@ impl HttpClientWrapper { } #[derive(Clone)] -pub struct RollupBoostServer { - l2_client: Arc, - builder_client: Arc, +pub struct RollupBoostServer { + l2_client: Arc>, + builder_client: Arc>, boost_sync: bool, metrics: Option>, payload_trace_context: Arc, } -impl RollupBoostServer { +impl RollupBoostServer +where + C: ClientT, + A: ClientT, +{ pub fn new( - l2_client: ExecutionClient, - builder_client: ExecutionClient, + l2_client: ExecutionClient, + builder_client: ExecutionClient, boost_sync: bool, metrics: Option>, ) -> Self { @@ -134,7 +138,11 @@ impl RollupBoostServer { } } -impl TryInto> for RollupBoostServer { +impl TryInto> for RollupBoostServer +where + C: ClientT + Clone, + A: ClientT + Clone, +{ type Error = RegisterMethodError; fn try_into(self) -> Result, Self::Error> { From 8de514a06a499b4fc406872c0a97d581a888183f Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 09:15:30 -0500 Subject: [PATCH 072/429] update trait bounds (flashbots/rollup-boost) --- src/client.rs | 1 + src/server.rs | 76 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/client.rs b/src/client.rs index 3dc6990b1caba..d2a8e2f38a735 100644 --- a/src/client.rs +++ b/src/client.rs @@ -20,6 +20,7 @@ use std::time::Duration; /// - **Engine API** calls are faciliated via the `auth_client` (requires JWT authentication). /// - All other API calls including the **Eth & Miner APIs** are faciliated via the `client` (optional JWT authentication). /// +#[derive(Clone)] pub struct ExecutionClient< C: ClientT = HttpClient, A: ClientT = HttpClient>, diff --git a/src/server.rs b/src/server.rs index 744b95b5a604d..26b9516659b86 100644 --- a/src/server.rs +++ b/src/server.rs @@ -140,8 +140,25 @@ where impl TryInto> for RollupBoostServer where - C: ClientT + Clone, - A: ClientT + Clone, + C: EngineApiClient + + EthApiClient + + MinerApiClient + + MinerApiExtClient + + ClientT + + Clone + + Send + + Sync + + 'static, + + A: EngineApiClient + + EthApiClient + + MinerApiClient + + MinerApiExtClient + + ClientT + + Clone + + Send + + Sync + + 'static, { type Error = RegisterMethodError; @@ -167,7 +184,11 @@ pub trait EthApi { } #[async_trait] -impl EthApiServer for RollupBoostServer { +impl EthApiServer for RollupBoostServer +where + C: ClientT + Clone + Send + Sync + 'static, + A: ClientT + Clone + Send + Sync + 'static, +{ async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult { debug!( message = "received send_raw_transaction", @@ -226,7 +247,11 @@ pub trait MinerApi { } #[async_trait] -impl MinerApiServer for RollupBoostServer { +impl MinerApiServer for RollupBoostServer +where + C: ClientT + Clone + Send + Sync + 'static, + A: ClientT + Clone + Send + Sync + 'static, +{ async fn set_extra(&self, record: Bytes) -> RpcResult { debug!( message = "received miner_setExtra", @@ -314,7 +339,11 @@ impl MinerApiServer for RollupBoostServer { } #[async_trait] -impl MinerApiExtServer for RollupBoostServer { +impl MinerApiExtServer for RollupBoostServer +where + C: ClientT + Clone + Send + Sync + 'static, + A: ClientT + Clone + Send + Sync + 'static, +{ async fn set_max_da_size(&self, max_tx_size: U64, max_block_size: U64) -> RpcResult { debug!( target: "server::set_max_da_size", @@ -380,7 +409,11 @@ pub trait EngineApi { } #[async_trait] -impl EngineApiServer for RollupBoostServer { +impl EngineApiServer for RollupBoostServer +where + C: ClientT + Clone + Send + Sync + 'static, + A: ClientT + Clone + Send + Sync + 'static, +{ async fn fork_choice_updated_v3( &self, fork_choice_state: ForkchoiceState, @@ -642,6 +675,7 @@ impl EngineApiServer for RollupBoostServer { #[cfg(test)] mod tests { use std::net::SocketAddr; + use std::str::FromStr; use std::sync::Mutex; use crate::proxy::ProxyLayer; @@ -737,22 +771,28 @@ mod tests { let l2_client = HttpClientBuilder::new() .build(format!("http://{L2_ADDR}")) .unwrap(); + let builder_client = HttpClientBuilder::new() .build(format!("http://{BUILDER_ADDR}")) .unwrap(); - let rollup_boost_client = RollupBoostServer::new( - Arc::new(HttpClientWrapper::new( - l2_client, - format!("http://{L2_ADDR}"), - )), - Arc::new(HttpClientWrapper::new( - builder_client, - format!("http://{BUILDER_ADDR}"), - )), - boost_sync, - None, - ); + let l2_client = ExecutionClient { + client: l2_client.clone(), + http_socket: SocketAddr::from_str(L2_ADDR).unwrap(), + auth_client: l2_client, + auth_socket: SocketAddr::from_str(L2_ADDR).unwrap(), + }; + + let builder_client = ExecutionClient { + client: builder_client.clone(), + http_socket: SocketAddr::from_str(L2_ADDR).unwrap(), + auth_client: builder_client, + auth_socket: SocketAddr::from_str(L2_ADDR).unwrap(), + }; + + let rollup_boost_client = + RollupBoostServer::new(l2_client, builder_client, boost_sync, None); + let mut module: RpcModule<()> = RpcModule::new(()); module .merge(EngineApiServer::into_rpc(rollup_boost_client)) From 3d8e4311724ede64549621b7a0e18f30894c897b Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 09:23:38 -0500 Subject: [PATCH 073/429] remove unneeded httpclientwrapper (flashbots/rollup-boost) --- src/main.rs | 46 +--------------------------------------------- src/server.rs | 12 ------------ 2 files changed, 1 insertion(+), 57 deletions(-) diff --git a/src/main.rs b/src/main.rs index ea8c317d95d64..f94dde46ed6bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; -use server::{HttpClientWrapper, RollupBoostServer}; +use server::RollupBoostServer; use std::net::SocketAddr; use std::sync::Arc; @@ -175,22 +175,6 @@ async fn main() -> eyre::Result<()> { Ok(()) } -fn create_client( - url: &str, - jwt_secret: JwtSecret, - timeout: u64, -) -> eyre::Result>>> { - // Create a middleware that adds a new JWT token to every request. - let auth_layer = AuthClientLayer::new(jwt_secret); - let client_middleware = tower::ServiceBuilder::new().layer(auth_layer); - - let client = HttpClientBuilder::new() - .set_http_middleware(client_middleware) - .request_timeout(Duration::from_millis(timeout)) - .build(url)?; - Ok(HttpClientWrapper::new(client, url.to_string())) -} - fn init_tracing(endpoint: &str) { global::set_text_map_propagator(TraceContextPropagator::new()); let provider = opentelemetry_otlp::new_pipeline() @@ -279,34 +263,6 @@ mod tests { )); } - #[tokio::test] - async fn test_create_client() { - valid_jwt().await; - invalid_jwt().await; - } - - async fn valid_jwt() { - let secret = JwtSecret::from_hex(SECRET).unwrap(); - let url = format!("http://{}:{}", AUTH_ADDR, AUTH_PORT); - let client = create_client(url.as_str(), secret, 2000); - let response = send_request(client.unwrap().client).await; - assert!(response.is_ok()); - assert_eq!(response.unwrap(), "You are the dark lord"); - } - - async fn invalid_jwt() { - let secret = JwtSecret::random(); - let url = format!("http://{}:{}", AUTH_ADDR, AUTH_PORT); - let client = create_client(url.as_str(), secret, 2000); - let response = send_request(client.unwrap().client).await; - assert!(response.is_err()); - assert!(matches!( - response.unwrap_err(), - ClientError::Transport(e) - if matches!(e.downcast_ref::(), Some(TransportError::Rejected { status_code: 401 })) - )); - } - async fn send_request( client: HttpClient>, ) -> Result { diff --git a/src/server.rs b/src/server.rs index 26b9516659b86..b59ab0f7c72db 100644 --- a/src/server.rs +++ b/src/server.rs @@ -96,18 +96,6 @@ impl PayloadTraceContext { } } -#[derive(Clone)] -pub struct HttpClientWrapper>> { - pub client: C, - pub url: String, -} - -impl HttpClientWrapper { - pub fn new(client: C, url: String) -> Self { - Self { client, url } - } -} - #[derive(Clone)] pub struct RollupBoostServer { l2_client: Arc>, From bc3e7adfc1e4adfd2c4dfdc5f994f62e8d5937a4 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 10:52:27 -0500 Subject: [PATCH 074/429] add tests for rollup boost server (flashbots/rollup-boost) --- src/client.rs | 6 ++--- src/main.rs | 1 + src/server.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/client.rs b/src/client.rs index d2a8e2f38a735..2ffc2df8577bc 100644 --- a/src/client.rs +++ b/src/client.rs @@ -25,7 +25,7 @@ pub struct ExecutionClient< C: ClientT = HttpClient, A: ClientT = HttpClient>, > { - /// Handles requests to Eth, Miner, and other Execution Layer APIs (optional JWT authentication) + /// Handles requests to Eth, Miner, and other execution layer APIs (optional JWT authentication) pub client: C, /// Address of the RPC server for execution layer API calls, excluding the Engine API pub http_socket: SocketAddr, @@ -36,7 +36,7 @@ pub struct ExecutionClient< } impl ExecutionClient { - /// Initializes a new [ExecutionClient] with JWT auth for the Engine API and without auth for general Execution Layer APIs. + /// Initializes a new [ExecutionClient] with JWT auth for the Engine API and without auth for general execution layer APIs. pub fn new( http_addr: IpAddr, http_port: u16, @@ -67,7 +67,7 @@ impl ExecutionClient { } impl ExecutionClient>> { - /// Initializes a new [ExecutionClient] with JWT auth for the Engine API and general Execution Layer APIs. + /// Initializes a new [ExecutionClient] with JWT auth for the Engine API and general execution layer APIs. pub fn new_with_auth( http_addr: IpAddr, http_port: u16, diff --git a/src/main.rs b/src/main.rs index f94dde46ed6bc..1210a673f9e97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -263,6 +263,7 @@ mod tests { )); } + // TODO: move these tests async fn send_request( client: HttpClient>, ) -> Result { diff --git a/src/server.rs b/src/server.rs index b59ab0f7c72db..b950a0dd56edc 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1004,4 +1004,68 @@ mod tests { .unwrap(); server.start(module) } + + // TODO: update this so that the backend stores the requests that we can assert against + pub type MockExecutionClient = + ExecutionClient, HttpClient>; + + impl MockExecutionClient { + pub fn new() -> Self { + todo!() + } + } + + #[tokio::test] + async fn test_send_raw_transaction() -> eyre::Result<()> { + let l2_client = MockExecutionClient::new(); + let builder_client = MockExecutionClient::new(); + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); + + let bytes = Bytes::from(FixedBytes::from([1u8; 32]).to_vec()); + rollup_boost.send_raw_transaction(bytes).await?; + + // TODO: assert that clients recieved the request + + Ok(()) + } + + #[tokio::test] + async fn test_set_gas_limit() -> eyre::Result<()> { + let l2_client = MockExecutionClient::new(); + let builder_client = MockExecutionClient::new(); + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); + + // TODO: + Ok(()) + } + + #[tokio::test] + async fn test_set_gas_price() -> eyre::Result<()> { + let l2_client = MockExecutionClient::new(); + let builder_client = MockExecutionClient::new(); + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); + // TODO: + + Ok(()) + } + + #[tokio::test] + async fn test_set_extra() -> eyre::Result<()> { + let l2_client = MockExecutionClient::new(); + let builder_client = MockExecutionClient::new(); + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); + // TODO: + + Ok(()) + } + + #[tokio::test] + async fn test_set_max_da_size() -> eyre::Result<()> { + let l2_client = MockExecutionClient::new(); + let builder_client = MockExecutionClient::new(); + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); + // TODO: + + Ok(()) + } } From 1b74ee7c08d69c03639503e32670590268d92b2e Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 12:06:15 -0500 Subject: [PATCH 075/429] tests wip (flashbots/rollup-boost) --- src/server.rs | 55 +++++++++++---------------------------------------- 1 file changed, 11 insertions(+), 44 deletions(-) diff --git a/src/server.rs b/src/server.rs index b950a0dd56edc..cc2aab3510421 100644 --- a/src/server.rs +++ b/src/server.rs @@ -662,6 +662,8 @@ where #[cfg(test)] mod tests { + use core::panic; + use std::default; use std::net::SocketAddr; use std::str::FromStr; use std::sync::Mutex; @@ -677,9 +679,13 @@ mod tests { BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, PayloadStatusEnum, }; use http::Uri; + use jsonrpsee::core::client::BatchResponse; + use jsonrpsee::core::params::BatchRequestBuilder; + use jsonrpsee::core::traits::ToRpcParams; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::server::{Server, ServerBuilder, ServerHandle}; use jsonrpsee::RpcModule; + use serde::de::DeserializeOwned; use std::sync::Arc; const L2_ADDR: &str = "0.0.0.0:8554"; @@ -1005,67 +1011,28 @@ mod tests { server.start(module) } - // TODO: update this so that the backend stores the requests that we can assert against - pub type MockExecutionClient = - ExecutionClient, HttpClient>; - - impl MockExecutionClient { - pub fn new() -> Self { - todo!() - } - } - #[tokio::test] async fn test_send_raw_transaction() -> eyre::Result<()> { - let l2_client = MockExecutionClient::new(); - let builder_client = MockExecutionClient::new(); - let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); - - let bytes = Bytes::from(FixedBytes::from([1u8; 32]).to_vec()); - rollup_boost.send_raw_transaction(bytes).await?; - - // TODO: assert that clients recieved the request - - Ok(()) + todo!(); } #[tokio::test] async fn test_set_gas_limit() -> eyre::Result<()> { - let l2_client = MockExecutionClient::new(); - let builder_client = MockExecutionClient::new(); - let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); - - // TODO: - Ok(()) + todo!(); } #[tokio::test] async fn test_set_gas_price() -> eyre::Result<()> { - let l2_client = MockExecutionClient::new(); - let builder_client = MockExecutionClient::new(); - let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); - // TODO: - - Ok(()) + todo!(); } #[tokio::test] async fn test_set_extra() -> eyre::Result<()> { - let l2_client = MockExecutionClient::new(); - let builder_client = MockExecutionClient::new(); - let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); - // TODO: - - Ok(()) + todo!(); } #[tokio::test] async fn test_set_max_da_size() -> eyre::Result<()> { - let l2_client = MockExecutionClient::new(); - let builder_client = MockExecutionClient::new(); - let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); - // TODO: - - Ok(()) + todo!(); } } From b3660cb5f57feda80d7096ce9ab87f654da4d092 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 12:07:37 -0500 Subject: [PATCH 076/429] remove unused deps (flashbots/rollup-boost) --- src/server.rs | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/src/server.rs b/src/server.rs index cc2aab3510421..b8b85c465ad87 100644 --- a/src/server.rs +++ b/src/server.rs @@ -662,31 +662,20 @@ where #[cfg(test)] mod tests { - use core::panic; - use std::default; - use std::net::SocketAddr; - use std::str::FromStr; - use std::sync::Mutex; - - use crate::proxy::ProxyLayer; use super::*; - use alloy_primitives::hex; - use alloy_primitives::U64; use alloy_primitives::{FixedBytes, U256}; use alloy_rpc_types_engine::{ BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, PayloadStatusEnum, }; - use http::Uri; - use jsonrpsee::core::client::BatchResponse; - use jsonrpsee::core::params::BatchRequestBuilder; - use jsonrpsee::core::traits::ToRpcParams; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; - use jsonrpsee::server::{Server, ServerBuilder, ServerHandle}; + use jsonrpsee::server::{ServerBuilder, ServerHandle}; use jsonrpsee::RpcModule; - use serde::de::DeserializeOwned; + use std::net::SocketAddr; + use std::str::FromStr; use std::sync::Arc; + use std::sync::Mutex; const L2_ADDR: &str = "0.0.0.0:8554"; const BUILDER_ADDR: &str = "0.0.0.0:8555"; From 5a87ecc37be96e9ef190cd5abbaef07352748c1b Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 12:09:53 -0500 Subject: [PATCH 077/429] clippy (flashbots/rollup-boost) --- src/client.rs | 8 -------- src/main.rs | 8 ++------ src/server.rs | 15 ++++----------- 3 files changed, 6 insertions(+), 25 deletions(-) diff --git a/src/client.rs b/src/client.rs index 2ffc2df8577bc..f4e7739db83d7 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,16 +1,8 @@ -use alloy_primitives::{Bytes, B256, U128}; -use alloy_rpc_types_engine::{ - ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, -}; use clap::{arg, ArgGroup, Parser}; use jsonrpsee::core::client::ClientT; -use jsonrpsee::core::RpcResult; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; -use jsonrpsee::proc_macros::rpc; -use op_alloy_rpc_types_engine::OpExecutionPayloadEnvelopeV3; use paste::paste; -use reth_optimism_payload_builder::OpPayloadAttributes; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use std::net::{IpAddr, SocketAddr}; use std::time::Duration; diff --git a/src/main.rs b/src/main.rs index 1210a673f9e97..2da5373c618e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,7 @@ use http::{StatusCode, Uri}; use hyper::service::service_fn; use hyper::{server::conn::http1, Request, Response}; use hyper_util::rt::TokioIo; -use jsonrpsee::http_client::transport::HttpBackend; -use jsonrpsee::http_client::{HttpBody, HttpClient, HttpClientBuilder}; +use jsonrpsee::http_client::HttpBody; use jsonrpsee::server::Server; use jsonrpsee::RpcModule; use metrics::ServerMetrics; @@ -18,12 +17,10 @@ use opentelemetry_sdk::propagation::TraceContextPropagator; use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; -use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use server::RollupBoostServer; use std::net::SocketAddr; use std::sync::Arc; -use std::time::Duration; use tokio::net::TcpListener; use tracing::error; use tracing::{info, Level}; @@ -169,7 +166,6 @@ async fn main() -> eyre::Result<()> { .await?; let handle = server.start(module); - // TODO: handle.stopped().await; Ok(()) @@ -236,7 +232,7 @@ async fn init_metrics_server(addr: SocketAddr, handle: PrometheusHandle) -> eyre mod tests { use assert_cmd::Command; use jsonrpsee::core::client::ClientT; - use jsonrpsee::http_client::transport::Error as TransportError; + use jsonrpsee::RpcModule; use jsonrpsee::{ core::ClientError, diff --git a/src/server.rs b/src/server.rs index b8b85c465ad87..1361370c4f998 100644 --- a/src/server.rs +++ b/src/server.rs @@ -6,8 +6,6 @@ use alloy_rpc_types_engine::{ PayloadStatus, }; use jsonrpsee::core::{async_trait, ClientError, RegisterMethodError, RpcResult}; -use jsonrpsee::http_client::transport::HttpBackend; -use jsonrpsee::http_client::HttpClient; use jsonrpsee::types::error::INVALID_REQUEST_CODE; use jsonrpsee::types::{ErrorCode, ErrorObject}; use jsonrpsee::RpcModule; @@ -19,19 +17,14 @@ use opentelemetry::trace::{Span, TraceContextExt, Tracer}; use opentelemetry::{Context, KeyValue}; use reth_optimism_payload_builder::{OpPayloadAttributes, OpPayloadBuilderAttributes}; use reth_payload_primitives::PayloadBuilderAttributes; -use reth_rpc_layer::AuthClientService; use std::num::NonZero; use std::sync::Arc; use tokio::sync::Mutex; use tracing::{debug, error, info}; -use clap::{arg, ArgGroup, Parser}; +use clap::Parser; use jsonrpsee::core::client::ClientT; -use jsonrpsee::http_client::HttpClientBuilder; use jsonrpsee::proc_macros::rpc; -use reth_rpc_layer::{AuthClientLayer, JwtSecret}; -use std::net::{IpAddr, SocketAddr}; -use std::time::Duration; const CACHE_SIZE: usize = 100; @@ -836,7 +829,7 @@ mod tests { let fcu_requests_builder_mu = fcu_requests_builder.lock().unwrap(); assert_eq!(fcu_requests_mu.len(), 1); assert_eq!(fcu_requests_builder_mu.len(), 0); - let req: &(ForkchoiceState, Option) = fcu_requests_mu.get(0).unwrap(); + let req: &(ForkchoiceState, Option) = fcu_requests_mu.first().unwrap(); assert_eq!(req.0, fcu); assert_eq!(req.1, None); @@ -863,7 +856,7 @@ mod tests { assert_eq!(new_payload_requests_mu.len(), 1); assert_eq!(new_payload_requests_builder_mu.len(), 0); let req: &(ExecutionPayloadV3, Vec>, B256) = - new_payload_requests_mu.get(0).unwrap(); + new_payload_requests_mu.first().unwrap(); assert_eq!( req.0, test_harness @@ -893,7 +886,7 @@ mod tests { assert_eq!(get_payload_requests_builder_mu.len(), 1); assert_eq!(get_payload_requests_mu.len(), 1); assert_eq!(new_payload_requests_mu.len(), 2); - let req: &PayloadId = get_payload_requests_mu.get(0).unwrap(); + let req: &PayloadId = get_payload_requests_mu.first().unwrap(); assert_eq!(*req, PayloadId::new([0, 0, 0, 0, 0, 0, 0, 1])); test_harness.cleanup().await; From b6eafb3299840b2e4cf5724df586cd35c8a91e56 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 11:40:52 -0700 Subject: [PATCH 078/429] fix: CLI (flashbots/rollup-boost) --- src/client.rs | 48 +++++++++++++++++++++++------------------------- src/main.rs | 21 ++++++++++++--------- src/server.rs | 1 - 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/client.rs b/src/client.rs index f4e7739db83d7..7c6c09eeaa498 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,10 +1,11 @@ -use clap::{arg, ArgGroup, Parser}; +use clap::{arg, Parser}; use jsonrpsee::core::client::ClientT; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use paste::paste; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use std::net::{IpAddr, SocketAddr}; +use std::path::PathBuf; use std::time::Duration; /// Client interface for interacting with an execution layer node. @@ -34,14 +35,16 @@ impl ExecutionClient { http_port: u16, auth_addr: IpAddr, auth_port: u16, - auth_rpc_jwt_secret: JwtSecret, + auth_rpc_jwt_secret: PathBuf, timeout: u64, - ) -> Result { + ) -> eyre::Result { let http_socket = SocketAddr::new(http_addr, http_port); let client = HttpClientBuilder::new() .request_timeout(Duration::from_millis(timeout)) .build(format!("http://{}", http_socket))?; + let jwt = std::fs::read_to_string(auth_rpc_jwt_secret)?; + let auth_rpc_jwt_secret = JwtSecret::from_hex(jwt)?; let auth_layer = AuthClientLayer::new(auth_rpc_jwt_secret); let auth_socket = SocketAddr::new(auth_addr, auth_port); let auth_client = HttpClientBuilder::new() @@ -63,12 +66,14 @@ impl ExecutionClient>> { pub fn new_with_auth( http_addr: IpAddr, http_port: u16, - rpc_jwt_secret: JwtSecret, + rpc_jwt_secret: PathBuf, auth_addr: IpAddr, auth_port: u16, - auth_rpc_jwt_secret: JwtSecret, + auth_rpc_jwt_secret: PathBuf, timeout: u64, - ) -> Result { + ) -> eyre::Result { + let jwt = std::fs::read_to_string(rpc_jwt_secret)?; + let rpc_jwt_secret = JwtSecret::from_hex(jwt)?; let rpc_auth_layer = AuthClientLayer::new(rpc_jwt_secret); let http_socket = SocketAddr::new(http_addr, http_port); let client = HttpClientBuilder::new() @@ -76,6 +81,8 @@ impl ExecutionClient>> { .request_timeout(Duration::from_millis(timeout)) .build(format!("http://{}", http_socket))?; + let jwt = std::fs::read_to_string(auth_rpc_jwt_secret)?; + let auth_rpc_jwt_secret = JwtSecret::from_hex(jwt)?; let auth_layer = AuthClientLayer::new(auth_rpc_jwt_secret); let auth_socket = SocketAddr::new(auth_addr, auth_port); let auth_client = HttpClientBuilder::new() @@ -98,49 +105,40 @@ macro_rules! define_rpc_args { $( paste! { #[derive(Parser, Debug, Clone, PartialEq, Eq)] - #[clap(group(ArgGroup::new(concat!(stringify!($prefix), "_jwt")) - .required(true) - .multiple(false) - .args(&[ - concat!(stringify!($prefix), "_jwtsecret"), - concat!(stringify!($prefix), "_jwtsecret_path") - ]) - ))] pub struct $name { /// Http server address - #[arg(long)] + #[arg(long = concat!(stringify!($prefix), ".http.addr"), default_value = "127.0.0.1")] pub [<$prefix _http_addr>]: IpAddr, /// Http server port - #[arg(long)] + #[arg(long = concat!(stringify!($prefix), ".http.port"), default_value_t = 8545)] pub [<$prefix _http_port>]: u16, /// Auth server address - #[arg(long)] + #[arg(long = concat!(stringify!($prefix), ".auth.addr"), default_value = "127.0.0.1")] pub [<$prefix _auth_addr>]: IpAddr, /// Auth server port - #[arg(long)] + #[arg(long = concat!(stringify!($prefix), ".auth.port"), default_value_t = 8551)] pub [<$prefix _auth_port>]: u16, /// Path to a JWT secret to use for the authenticated engine-API RPC server. - #[arg(long, value_name = "PATH")] - pub [<$prefix _auth_rpc_jwt_secret>]: JwtSecret, + #[arg(long = concat!(stringify!($prefix), ".authrpc.jwtsecret"), value_name = "PATH", required = true)] + pub [<$prefix _auth_jwtsecret>]: PathBuf, /// Hex encoded JWT secret to authenticate the regular RPC server(s) /// /// This is __not__ used for the authenticated engine-API RPC server, see /// `authrpc.jwtsecret`. - #[arg(long, value_name = "HEX")] - pub [<$prefix _rpc_jwt_secret>]: JwtSecret, - + #[arg(long = concat!(stringify!($prefix), "rpc.jwtsecret"), value_name = "PATH", required = false)] + pub [<$prefix _rpc_jwtsecret>]: Option, /// Filename for auth IPC socket/pipe within the datadir - #[arg(long)] + #[arg(long = concat!(stringify!($prefix), ".auth.ipc.path"), value_name = "PATH")] pub [<$prefix _auth_ipc_path>]: Option, /// Timeout for http calls in milliseconds - #[arg(long)] + #[arg(long = concat!(stringify!($prefix), ".timeout"), default_value_t = 1000)] pub [<$prefix _timeout>]: u64, } } diff --git a/src/main.rs b/src/main.rs index 2da5373c618e8..1b52ed866c2a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -130,24 +130,23 @@ async fn main() -> eyre::Result<()> { } let l2_client_args = args.l2_client; - let l2_client = ExecutionClient::new_with_auth( + // TODO: add support for optional JWT gated rpc (eth api, miner api, etc.) based on rpc_jwtsecret Some/None + let l2_client = ExecutionClient::new( l2_client_args.l2_http_addr, l2_client_args.l2_http_port, - l2_client_args.l2_rpc_jwt_secret, l2_client_args.l2_auth_addr, l2_client_args.l2_auth_port, - l2_client_args.l2_auth_rpc_jwt_secret, + l2_client_args.l2_auth_jwtsecret, l2_client_args.l2_timeout, )?; let builder_args = args.builder; - let builder_client = ExecutionClient::new_with_auth( + let builder_client = ExecutionClient::new( builder_args.builder_http_addr, builder_args.builder_http_port, - builder_args.builder_rpc_jwt_secret, builder_args.builder_auth_addr, builder_args.builder_auth_port, - builder_args.builder_auth_rpc_jwt_secret, + builder_args.builder_auth_jwtsecret, builder_args.builder_timeout, )?; @@ -157,9 +156,13 @@ async fn main() -> eyre::Result<()> { // server setup info!("Starting server on :{}", args.rpc_port); - let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new( - l2_client_args.l2_http_addr.to_string().parse::()?, - )); + // TODO: Both the Auth and Non-Auth URI's should be passed to the ProxyLayer + let l2_auth_uri = format!( + "http://{}:{}", + l2_client_args.l2_auth_addr, l2_client_args.l2_auth_port + ); + let service_builder = + tower::ServiceBuilder::new().layer(ProxyLayer::new(l2_auth_uri.parse::()?)); let server = Server::builder() .set_http_middleware(service_builder) .build(format!("{}:{}", args.rpc_host, args.rpc_port).parse::()?) diff --git a/src/server.rs b/src/server.rs index 1361370c4f998..e52a994208fda 100644 --- a/src/server.rs +++ b/src/server.rs @@ -22,7 +22,6 @@ use std::sync::Arc; use tokio::sync::Mutex; use tracing::{debug, error, info}; -use clap::Parser; use jsonrpsee::core::client::ClientT; use jsonrpsee::proc_macros::rpc; From 0434d52225c6843452e1c413d541f4a5c3eed981 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 11:50:44 -0700 Subject: [PATCH 079/429] chore: add ExecutionClientError (flashbots/rollup-boost) --- src/client.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/client.rs b/src/client.rs index 7c6c09eeaa498..7fd655c5b093e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -7,6 +7,17 @@ use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; use std::time::Duration; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum ExecutionClientError { + #[error(transparent)] + HttpClient(#[from] jsonrpsee::core::client::Error), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Jwt(#[from] reth_rpc_layer::JwtError), +} /// Client interface for interacting with an execution layer node. /// @@ -37,7 +48,7 @@ impl ExecutionClient { auth_port: u16, auth_rpc_jwt_secret: PathBuf, timeout: u64, - ) -> eyre::Result { + ) -> Result { let http_socket = SocketAddr::new(http_addr, http_port); let client = HttpClientBuilder::new() .request_timeout(Duration::from_millis(timeout)) @@ -71,7 +82,7 @@ impl ExecutionClient>> { auth_port: u16, auth_rpc_jwt_secret: PathBuf, timeout: u64, - ) -> eyre::Result { + ) -> Result { let jwt = std::fs::read_to_string(rpc_jwt_secret)?; let rpc_jwt_secret = JwtSecret::from_hex(jwt)?; let rpc_auth_layer = AuthClientLayer::new(rpc_jwt_secret); From e2620228476b21995a4a1481580a350cb43e10cb Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 11:51:38 -0700 Subject: [PATCH 080/429] fix: rpc jwt (flashbots/rollup-boost) --- src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index 7fd655c5b093e..33765742a9a5a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -141,7 +141,7 @@ macro_rules! define_rpc_args { /// /// This is __not__ used for the authenticated engine-API RPC server, see /// `authrpc.jwtsecret`. - #[arg(long = concat!(stringify!($prefix), "rpc.jwtsecret"), value_name = "PATH", required = false)] + #[arg(long = concat!(stringify!($prefix), ".rpc.jwtsecret"), value_name = "PATH", required = false)] pub [<$prefix _rpc_jwtsecret>]: Option, /// Filename for auth IPC socket/pipe within the datadir From fc83306b526357408257f88588f4c82014c916ce Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 14:09:16 -0500 Subject: [PATCH 081/429] simplify execution client (flashbots/rollup-boost) --- src/client.rs | 7 ++--- src/main.rs | 4 ++- src/server.rs | 74 +++++++++++++++++---------------------------------- 3 files changed, 30 insertions(+), 55 deletions(-) diff --git a/src/client.rs b/src/client.rs index f4e7739db83d7..de0f05c996c12 100644 --- a/src/client.rs +++ b/src/client.rs @@ -13,16 +13,13 @@ use std::time::Duration; /// - All other API calls including the **Eth & Miner APIs** are faciliated via the `client` (optional JWT authentication). /// #[derive(Clone)] -pub struct ExecutionClient< - C: ClientT = HttpClient, - A: ClientT = HttpClient>, -> { +pub struct ExecutionClient> { /// Handles requests to Eth, Miner, and other execution layer APIs (optional JWT authentication) pub client: C, /// Address of the RPC server for execution layer API calls, excluding the Engine API pub http_socket: SocketAddr, /// Handles requests to the authenticated Engine API (requires JWT authentication) - pub auth_client: A, + pub auth_client: HttpClient>, /// Address of the RPC server for authenticated Engine API calls pub auth_socket: SocketAddr, } diff --git a/src/main.rs b/src/main.rs index 2da5373c618e8..ba0ebcdb5acce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -233,6 +233,8 @@ mod tests { use assert_cmd::Command; use jsonrpsee::core::client::ClientT; + use jsonrpsee::http_client::transport::HttpBackend; + use jsonrpsee::http_client::HttpClient; use jsonrpsee::RpcModule; use jsonrpsee::{ core::ClientError, @@ -240,7 +242,7 @@ mod tests { server::{ServerBuilder, ServerHandle}, }; use predicates::prelude::*; - use reth_rpc_layer::{AuthLayer, JwtAuthValidator}; + use reth_rpc_layer::{AuthClientService, AuthLayer, JwtAuthValidator, JwtSecret}; use std::result::Result; use super::*; diff --git a/src/server.rs b/src/server.rs index 1361370c4f998..658552930db99 100644 --- a/src/server.rs +++ b/src/server.rs @@ -90,22 +90,21 @@ impl PayloadTraceContext { } #[derive(Clone)] -pub struct RollupBoostServer { - l2_client: Arc>, - builder_client: Arc>, +pub struct RollupBoostServer { + l2_client: Arc>, + builder_client: Arc>, boost_sync: bool, metrics: Option>, payload_trace_context: Arc, } -impl RollupBoostServer +impl RollupBoostServer where C: ClientT, - A: ClientT, { pub fn new( - l2_client: ExecutionClient, - builder_client: ExecutionClient, + l2_client: ExecutionClient, + builder_client: ExecutionClient, boost_sync: bool, metrics: Option>, ) -> Self { @@ -119,7 +118,7 @@ where } } -impl TryInto> for RollupBoostServer +impl TryInto> for RollupBoostServer where C: EngineApiClient + EthApiClient @@ -130,16 +129,6 @@ where + Send + Sync + 'static, - - A: EngineApiClient - + EthApiClient - + MinerApiClient - + MinerApiExtClient - + ClientT - + Clone - + Send - + Sync - + 'static, { type Error = RegisterMethodError; @@ -165,10 +154,9 @@ pub trait EthApi { } #[async_trait] -impl EthApiServer for RollupBoostServer +impl EthApiServer for RollupBoostServer where C: ClientT + Clone + Send + Sync + 'static, - A: ClientT + Clone + Send + Sync + 'static, { async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult { debug!( @@ -228,10 +216,9 @@ pub trait MinerApi { } #[async_trait] -impl MinerApiServer for RollupBoostServer +impl MinerApiServer for RollupBoostServer where C: ClientT + Clone + Send + Sync + 'static, - A: ClientT + Clone + Send + Sync + 'static, { async fn set_extra(&self, record: Bytes) -> RpcResult { debug!( @@ -320,10 +307,9 @@ where } #[async_trait] -impl MinerApiExtServer for RollupBoostServer +impl MinerApiExtServer for RollupBoostServer where C: ClientT + Clone + Send + Sync + 'static, - A: ClientT + Clone + Send + Sync + 'static, { async fn set_max_da_size(&self, max_tx_size: U64, max_block_size: U64) -> RpcResult { debug!( @@ -390,10 +376,9 @@ pub trait EngineApi { } #[async_trait] -impl EngineApiServer for RollupBoostServer +impl EngineApiServer for RollupBoostServer where C: ClientT + Clone + Send + Sync + 'static, - A: ClientT + Clone + Send + Sync + 'static, { async fn fork_choice_updated_v3( &self, @@ -665,13 +650,17 @@ mod tests { use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; use jsonrpsee::server::{ServerBuilder, ServerHandle}; use jsonrpsee::RpcModule; - use std::net::SocketAddr; + use reth_rpc_layer::JwtSecret; + use std::net::{IpAddr, SocketAddr}; use std::str::FromStr; use std::sync::Arc; use std::sync::Mutex; + const HOST: &str = "0.0.0.0"; + const L2_PORT: u16 = 8554; const L2_ADDR: &str = "0.0.0.0:8554"; - const BUILDER_ADDR: &str = "0.0.0.0:8555"; + const BUILDER_PORT: u16 = 8555; + const BUILDER_ADDR: &str = "0.0.0.0.8555"; const SERVER_ADDR: &str = "0.0.0.0:8556"; #[derive(Debug, Clone)] @@ -744,27 +733,14 @@ mod tests { l2_mock: Option, builder_mock: Option, ) -> Self { - let l2_client = HttpClientBuilder::new() - .build(format!("http://{L2_ADDR}")) - .unwrap(); - - let builder_client = HttpClientBuilder::new() - .build(format!("http://{BUILDER_ADDR}")) - .unwrap(); - - let l2_client = ExecutionClient { - client: l2_client.clone(), - http_socket: SocketAddr::from_str(L2_ADDR).unwrap(), - auth_client: l2_client, - auth_socket: SocketAddr::from_str(L2_ADDR).unwrap(), - }; - - let builder_client = ExecutionClient { - client: builder_client.clone(), - http_socket: SocketAddr::from_str(L2_ADDR).unwrap(), - auth_client: builder_client, - auth_socket: SocketAddr::from_str(L2_ADDR).unwrap(), - }; + let host = IpAddr::from_str(HOST).unwrap(); + + let jwt_secret = JwtSecret::random(); + let l2_client = + ExecutionClient::new(host, L2_PORT, host, L2_PORT, jwt_secret, 2000).unwrap(); + let builder_client = + ExecutionClient::new(host, BUILDER_PORT, host, BUILDER_PORT, jwt_secret, 2000) + .unwrap(); let rollup_boost_client = RollupBoostServer::new(l2_client, builder_client, boost_sync, None); From 12dbfa65262f491918f6b0979acd4ed0e42df99f Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 14:50:48 -0500 Subject: [PATCH 082/429] wip (flashbots/rollup-boost) --- src/client.rs | 13 ++++---- src/proxy.rs | 82 +++++++++++++++++++++++++++++++++++---------------- src/server.rs | 15 ++++++---- 3 files changed, 73 insertions(+), 37 deletions(-) diff --git a/src/client.rs b/src/client.rs index 8bcc6efc8c8e6..56ecb242b026b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -6,6 +6,7 @@ use paste::paste; use reth_rpc_layer::{AuthClientLayer, AuthClientService, JwtSecret}; use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; +use std::sync::Arc; use std::time::Duration; use thiserror::Error; @@ -27,11 +28,11 @@ pub enum ExecutionClientError { #[derive(Clone)] pub struct ExecutionClient> { /// Handles requests to Eth, Miner, and other execution layer APIs (optional JWT authentication) - pub client: C, + pub client: Arc, /// Address of the RPC server for execution layer API calls, excluding the Engine API pub http_socket: SocketAddr, /// Handles requests to the authenticated Engine API (requires JWT authentication) - pub auth_client: HttpClient>, + pub auth_client: Arc>>, /// Address of the RPC server for authenticated Engine API calls pub auth_socket: SocketAddr, } @@ -61,9 +62,9 @@ impl ExecutionClient { .build(format!("http://{}", auth_socket))?; Ok(Self { - client, + client: Arc::new(client), http_socket, - auth_client, + auth_client: Arc::new(auth_client), auth_socket, }) } @@ -99,9 +100,9 @@ impl ExecutionClient>> { .build(format!("http://{}", auth_socket))?; Ok(Self { - client, + client: Arc::new(client), http_socket, - auth_client, + auth_client: Arc::new(auth_client), auth_socket, }) } diff --git a/src/proxy.rs b/src/proxy.rs index d08a2f0fecba3..27aef61ee6e6e 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -2,8 +2,12 @@ use http::Uri; use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client; use hyper_util::rt::TokioExecutor; +use jsonrpsee::core::client::ClientT; use jsonrpsee::core::{http_helpers, BoxError}; -use jsonrpsee::http_client::{HttpBody, HttpRequest, HttpResponse}; +use jsonrpsee::http_client::transport::HttpBackend; +use jsonrpsee::http_client::{HttpBody, HttpClient, HttpRequest, HttpResponse}; +use reth_rpc_layer::AuthClientService; +use std::sync::Arc; use std::task::{Context, Poll}; use std::{future::Future, pin::Pin}; use tower::{Layer, Service}; @@ -13,12 +17,12 @@ const MULTIPLEX_METHODS: [&str; 3] = ["engine_", "eth_sendRawTransaction", "mine #[derive(Debug, Clone)] pub struct ProxyLayer { - target_url: Uri, + l2_auth_client: Arc>>, } impl ProxyLayer { - pub fn new(target_url: Uri) -> Self { - ProxyLayer { target_url } + pub fn new(l2_auth_client: Arc>>) -> Self { + ProxyLayer { l2_auth_client } } } @@ -28,8 +32,7 @@ impl Layer for ProxyLayer { fn layer(&self, inner: S) -> Self::Service { ProxyService { inner, - client: Client::builder(TokioExecutor::new()).build_http(), - target_url: self.target_url.clone(), + l2_auth_client: self.l2_auth_client.clone(), } } } @@ -37,8 +40,7 @@ impl Layer for ProxyLayer { #[derive(Clone)] pub struct ProxyService { inner: S, - client: Client, - target_url: Uri, + l2_auth_client: Arc>>, } impl Service> for ProxyService @@ -62,14 +64,14 @@ where return Box::pin(async { Ok(Self::Response::new(HttpBody::from("OK"))) }); } - let target_url = self.target_url.clone(); - let client = self.client.clone(); + let l2_auth_client = self.l2_auth_client.clone(); let mut inner = self.inner.clone(); #[derive(serde::Deserialize, Debug)] struct RpcRequest<'a> { #[serde(borrow)] method: &'a str, + params: Option, } let fut = async move { @@ -78,7 +80,7 @@ where let (body, _is_single) = http_helpers::read_body(&parts.headers, body, u32::MAX).await?; // Deserialize the bytes to find the method - let method: RpcRequest = serde_json::from_slice(&body)?; + let rpc_request: RpcRequest = serde_json::from_slice(&body)?; // Create a new body from the bytes let new_body = HttpBody::from(body.clone()); @@ -88,29 +90,41 @@ where debug!( message = "received json rpc request for", - method = method.method + method = rpc_request.method ); if MULTIPLEX_METHODS .iter() - .any(|&m| method.method.starts_with(m)) + .any(|&m| rpc_request.method.starts_with(m)) { - info!(target: "proxy::call", message = "proxying request to rollup-boost server", ?method); + info!(target: "proxy::call", message = "proxying request to rollup-boost server", "method" = ?rpc_request.method); // let rpc server handle engine rpc requests let res = inner.call(req).await.map_err(|e| e.into())?; Ok(res) } else { - info!(target: "proxy::call", message = "forwarding request to l2 client", ?method); + info!(target: "proxy::call", message = "forwarding request to l2 client", "method" = ?rpc_request.method); - // Modify the URI - *req.uri_mut() = target_url; + // l2_auth_client.call(req); + l2_auth_client + .request( + rpc_request.method, + rpc_request.params.unwrap_or(serde_json::Value::Null), + ) + .await; - // Forward the request - let res = client - .request(req) - .await - .map(|res| res.map(HttpBody::new))?; - Ok(res) + // let res = l2_auth_client.request(req).await.map_err(|e| e.into()); + + // // Modify the URI + // *req.uri_mut() = target_url; + + // // Forward the request + // let res = client + // .request(req) + // .await + // .map(|res| res.map(HttpBody::new))?; + // Ok(res) + + todo!(); } }; Box::pin(fut) @@ -119,17 +133,21 @@ where #[cfg(test)] mod tests { - use std::net::SocketAddr; + use std::{ + net::{IpAddr, SocketAddr}, + str::FromStr, + }; use http_body_util::BodyExt; use jsonrpsee::{ core::{client::ClientT, ClientError}, - http_client::HttpClient, + http_client::{HttpClient, HttpClientBuilder}, rpc_params, server::{ServerBuilder, ServerHandle}, types::{ErrorCode, ErrorObject}, RpcModule, }; + use reth_rpc_layer::{AuthClientLayer, JwtSecret}; use super::*; @@ -240,7 +258,19 @@ mod tests { /// Spawn a new RPC server with a proxy layer. async fn spawn_proxy_server() -> ServerHandle { let addr = format!("{ADDR}:{PORT}"); - let proxy_layer = ProxyLayer::new(format!("http://{ADDR}:{PROXY_PORT}").parse().unwrap()); + + let jwt = JwtSecret::random(); + let auth_layer = AuthClientLayer::new(jwt); + let l2_auth_client = HttpClientBuilder::new() + .set_http_middleware(tower::ServiceBuilder::new().layer(auth_layer)) + .build(format!( + "http://{}", + SocketAddr::new(IpAddr::from_str(ADDR).unwrap(), PORT as u16) + )) + .expect("Could not build auth client"); + + let proxy_layer = ProxyLayer::new(Arc::new(l2_auth_client)); + // Create a layered server let server = ServerBuilder::default() .set_http_middleware(tower::ServiceBuilder::new().layer(proxy_layer)) diff --git a/src/server.rs b/src/server.rs index 6dfb94920ab42..61fae37a0ae4e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -90,8 +90,8 @@ impl PayloadTraceContext { #[derive(Clone)] pub struct RollupBoostServer { - l2_client: Arc>, - builder_client: Arc>, + l2_client: ExecutionClient, + builder_client: ExecutionClient, boost_sync: bool, metrics: Option>, payload_trace_context: Arc, @@ -108,8 +108,8 @@ where metrics: Option>, ) -> Self { Self { - l2_client: Arc::new(l2_client), - builder_client: Arc::new(builder_client), + l2_client, + builder_client, boost_sync, metrics, payload_trace_context: Arc::new(PayloadTraceContext::new()), @@ -651,6 +651,7 @@ mod tests { use jsonrpsee::RpcModule; use reth_rpc_layer::JwtSecret; use std::net::{IpAddr, SocketAddr}; + use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; use std::sync::Mutex; @@ -735,8 +736,12 @@ mod tests { let host = IpAddr::from_str(HOST).unwrap(); let jwt_secret = JwtSecret::random(); + // NOTE: update, placeholder for now + let jwt_secret = PathBuf::default(); let l2_client = - ExecutionClient::new(host, L2_PORT, host, L2_PORT, jwt_secret, 2000).unwrap(); + ExecutionClient::new(host, L2_PORT, host, L2_PORT, jwt_secret.clone(), 2000) + .unwrap(); + let builder_client = ExecutionClient::new(host, BUILDER_PORT, host, BUILDER_PORT, jwt_secret, 2000) .unwrap(); From f2dc514d2a987fd2592ea855c684127ca2a2b9cf Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 15:17:10 -0500 Subject: [PATCH 083/429] pass l2 auth client to proxy layer (flashbots/rollup-boost) --- src/main.rs | 9 ++------- src/proxy.rs | 22 ++-------------------- src/server.rs | 10 +++++----- 3 files changed, 9 insertions(+), 32 deletions(-) diff --git a/src/main.rs b/src/main.rs index bf8540698e086..cb797e40e326c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -152,17 +152,12 @@ async fn main() -> eyre::Result<()> { let rollup_boost = RollupBoostServer::new(l2_client, builder_client, args.boost_sync, metrics); + let l2_auth_client = rollup_boost.l2_client.auth_client.clone(); let module: RpcModule<()> = rollup_boost.try_into()?; // server setup info!("Starting server on :{}", args.rpc_port); - // TODO: Both the Auth and Non-Auth URI's should be passed to the ProxyLayer - let l2_auth_uri = format!( - "http://{}:{}", - l2_client_args.l2_auth_addr, l2_client_args.l2_auth_port - ); - let service_builder = - tower::ServiceBuilder::new().layer(ProxyLayer::new(l2_auth_uri.parse::()?)); + let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new(l2_auth_client)); let server = Server::builder() .set_http_middleware(service_builder) .build(format!("{}:{}", args.rpc_host, args.rpc_port).parse::()?) diff --git a/src/proxy.rs b/src/proxy.rs index 27aef61ee6e6e..937091e08eb6e 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -3,6 +3,7 @@ use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client; use hyper_util::rt::TokioExecutor; use jsonrpsee::core::client::ClientT; +use jsonrpsee::core::traits::ToRpcParams; use jsonrpsee::core::{http_helpers, BoxError}; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::{HttpBody, HttpClient, HttpRequest, HttpResponse}; @@ -71,7 +72,6 @@ where struct RpcRequest<'a> { #[serde(borrow)] method: &'a str, - params: Option, } let fut = async move { @@ -104,25 +104,7 @@ where } else { info!(target: "proxy::call", message = "forwarding request to l2 client", "method" = ?rpc_request.method); - // l2_auth_client.call(req); - l2_auth_client - .request( - rpc_request.method, - rpc_request.params.unwrap_or(serde_json::Value::Null), - ) - .await; - - // let res = l2_auth_client.request(req).await.map_err(|e| e.into()); - - // // Modify the URI - // *req.uri_mut() = target_url; - - // // Forward the request - // let res = client - // .request(req) - // .await - // .map(|res| res.map(HttpBody::new))?; - // Ok(res) + // TODO: forward to l2_auth_client todo!(); } diff --git a/src/server.rs b/src/server.rs index 61fae37a0ae4e..bc7c2894410fb 100644 --- a/src/server.rs +++ b/src/server.rs @@ -90,11 +90,11 @@ impl PayloadTraceContext { #[derive(Clone)] pub struct RollupBoostServer { - l2_client: ExecutionClient, - builder_client: ExecutionClient, - boost_sync: bool, - metrics: Option>, - payload_trace_context: Arc, + pub l2_client: ExecutionClient, + pub builder_client: ExecutionClient, + pub boost_sync: bool, + pub metrics: Option>, + pub payload_trace_context: Arc, } impl RollupBoostServer From 60f62832d9e597c3f7b04b7dbe4f409bb43b9d64 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 14:27:47 -0700 Subject: [PATCH 084/429] feat: instert jwt token into headers (flashbots/rollup-boost) --- src/main.rs | 11 ++++++++--- src/proxy.rs | 37 ++++++++++++++++++++++++++----------- src/server.rs | 2 +- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index cb797e40e326c..609ba2fa79429 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use opentelemetry_sdk::propagation::TraceContextPropagator; use opentelemetry_sdk::trace::Config; use opentelemetry_sdk::Resource; use proxy::ProxyLayer; +use reth_rpc_layer::JwtSecret; use server::RollupBoostServer; use std::net::SocketAddr; @@ -136,7 +137,7 @@ async fn main() -> eyre::Result<()> { l2_client_args.l2_http_port, l2_client_args.l2_auth_addr, l2_client_args.l2_auth_port, - l2_client_args.l2_auth_jwtsecret, + l2_client_args.l2_auth_jwtsecret.clone(), l2_client_args.l2_timeout, )?; @@ -152,12 +153,16 @@ async fn main() -> eyre::Result<()> { let rollup_boost = RollupBoostServer::new(l2_client, builder_client, args.boost_sync, metrics); - let l2_auth_client = rollup_boost.l2_client.auth_client.clone(); let module: RpcModule<()> = rollup_boost.try_into()?; // server setup info!("Starting server on :{}", args.rpc_port); - let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new(l2_auth_client)); + let auth_rpc_uri = format!("http://{}:{}", l2_client_args.l2_auth_addr, l2_client_args.l2_auth_port).parse::()?; + + let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new( + auth_rpc_uri, + JwtSecret::from_file(&l2_client_args.l2_auth_jwtsecret)? + )); let server = Server::builder() .set_http_middleware(service_builder) .build(format!("{}:{}", args.rpc_host, args.rpc_port).parse::()?) diff --git a/src/proxy.rs b/src/proxy.rs index 937091e08eb6e..f48a508bf3c19 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -1,3 +1,4 @@ +use http::header::AUTHORIZATION; use http::Uri; use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client; @@ -7,7 +8,7 @@ use jsonrpsee::core::traits::ToRpcParams; use jsonrpsee::core::{http_helpers, BoxError}; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::{HttpBody, HttpClient, HttpRequest, HttpResponse}; -use reth_rpc_layer::AuthClientService; +use reth_rpc_layer::{secret_to_bearer_header, AuthClientService, JwtSecret}; use std::sync::Arc; use std::task::{Context, Poll}; use std::{future::Future, pin::Pin}; @@ -18,12 +19,13 @@ const MULTIPLEX_METHODS: [&str; 3] = ["engine_", "eth_sendRawTransaction", "mine #[derive(Debug, Clone)] pub struct ProxyLayer { - l2_auth_client: Arc>>, + target_url: Uri, + secret: JwtSecret, } impl ProxyLayer { - pub fn new(l2_auth_client: Arc>>) -> Self { - ProxyLayer { l2_auth_client } + pub fn new(target_url: Uri, secret: JwtSecret) -> Self { + ProxyLayer { target_url, secret } } } @@ -33,7 +35,9 @@ impl Layer for ProxyLayer { fn layer(&self, inner: S) -> Self::Service { ProxyService { inner, - l2_auth_client: self.l2_auth_client.clone(), + client: Client::builder(TokioExecutor::new()).build_http(), + target_url: self.target_url.clone(), + secret: self.secret.clone(), } } } @@ -41,7 +45,9 @@ impl Layer for ProxyLayer { #[derive(Clone)] pub struct ProxyService { inner: S, - l2_auth_client: Arc>>, + client: Client, + target_url: Uri, + secret: JwtSecret, } impl Service> for ProxyService @@ -65,8 +71,10 @@ where return Box::pin(async { Ok(Self::Response::new(HttpBody::from("OK"))) }); } - let l2_auth_client = self.l2_auth_client.clone(); + let client = self.client.clone(); let mut inner = self.inner.clone(); + let target_url = self.target_url.clone(); + let secret = self.secret.clone(); #[derive(serde::Deserialize, Debug)] struct RpcRequest<'a> { @@ -103,10 +111,17 @@ where Ok(res) } else { info!(target: "proxy::call", message = "forwarding request to l2 client", "method" = ?rpc_request.method); - - // TODO: forward to l2_auth_client - - todo!(); + // Modify the URI + *req.uri_mut() = target_url.clone(); + // Insert JWT Authorization headers + req.headers_mut().insert(AUTHORIZATION, secret_to_bearer_header(&secret)); + + // Forward the request + let res = client + .request(req) + .await + .map(|res| res.map(HttpBody::new))?; + Ok(res) } }; Box::pin(fut) diff --git a/src/server.rs b/src/server.rs index bc7c2894410fb..7a524acff84c4 100644 --- a/src/server.rs +++ b/src/server.rs @@ -27,7 +27,7 @@ use jsonrpsee::proc_macros::rpc; const CACHE_SIZE: usize = 100; -struct PayloadTraceContext { +pub struct PayloadTraceContext { tracer: Arc, block_hash_to_payload_ids: Arc>>>, payload_id_to_span: Arc>>>, From 7740b1931268bd5a96c07fb55233fee056043ef5 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 15:25:07 -0700 Subject: [PATCH 085/429] feat: update JWT options in CLI (flashbots/rollup-boost) --- src/client.rs | 41 +++++++++++++++++++++++++++-------------- src/main.rs | 33 ++++++++++++++++++++++++++------- src/proxy.rs | 36 +++++++++++++++--------------------- src/server.rs | 5 +---- 4 files changed, 69 insertions(+), 46 deletions(-) diff --git a/src/client.rs b/src/client.rs index 56ecb242b026b..b42d034a0ec92 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,3 +1,4 @@ +use clap::ArgGroup; use clap::{arg, Parser}; use jsonrpsee::core::client::ClientT; use jsonrpsee::http_client::transport::HttpBackend; @@ -44,16 +45,13 @@ impl ExecutionClient { http_port: u16, auth_addr: IpAddr, auth_port: u16, - auth_rpc_jwt_secret: PathBuf, + auth_rpc_jwt_secret: JwtSecret, timeout: u64, ) -> Result { let http_socket = SocketAddr::new(http_addr, http_port); let client = HttpClientBuilder::new() .request_timeout(Duration::from_millis(timeout)) .build(format!("http://{}", http_socket))?; - - let jwt = std::fs::read_to_string(auth_rpc_jwt_secret)?; - let auth_rpc_jwt_secret = JwtSecret::from_hex(jwt)?; let auth_layer = AuthClientLayer::new(auth_rpc_jwt_secret); let auth_socket = SocketAddr::new(auth_addr, auth_port); let auth_client = HttpClientBuilder::new() @@ -72,7 +70,7 @@ impl ExecutionClient { impl ExecutionClient>> { /// Initializes a new [ExecutionClient] with JWT auth for the Engine API and general execution layer APIs. - pub fn new_with_auth( + pub fn _new_with_auth( http_addr: IpAddr, http_port: u16, rpc_jwt_secret: PathBuf, @@ -114,40 +112,55 @@ macro_rules! define_rpc_args { $( paste! { #[derive(Parser, Debug, Clone, PartialEq, Eq)] + #[clap(group(ArgGroup::new(concat!(stringify!($prefix), "_jwt")).required(true).multiple(false).args(&[ + concat!(stringify!($prefix), "_auth_jwtsecret_path"), + concat!(stringify!($prefix), "_auth_jwtsecret")]))) + ] pub struct $name { /// Http server address - #[arg(long = concat!(stringify!($prefix), ".http.addr"), default_value = "127.0.0.1")] + #[arg(long = concat!(stringify!($prefix), ".http.addr"), env, default_value = "127.0.0.1")] pub [<$prefix _http_addr>]: IpAddr, /// Http server port - #[arg(long = concat!(stringify!($prefix), ".http.port"), default_value_t = 8545)] + #[arg(long = concat!(stringify!($prefix), ".http.port"), env, default_value_t = 8545)] pub [<$prefix _http_port>]: u16, /// Auth server address - #[arg(long = concat!(stringify!($prefix), ".auth.addr"), default_value = "127.0.0.1")] + #[arg(long = concat!(stringify!($prefix), ".auth.addr"), env, default_value = "127.0.0.1")] pub [<$prefix _auth_addr>]: IpAddr, /// Auth server port - #[arg(long = concat!(stringify!($prefix), ".auth.port"), default_value_t = 8551)] + #[arg(long = concat!(stringify!($prefix), ".auth.port"), env, default_value_t = 8551)] pub [<$prefix _auth_port>]: u16, /// Path to a JWT secret to use for the authenticated engine-API RPC server. - #[arg(long = concat!(stringify!($prefix), ".authrpc.jwtsecret"), value_name = "PATH", required = true)] - pub [<$prefix _auth_jwtsecret>]: PathBuf, + #[arg(long = concat!(stringify!($prefix), ".authrpc.jwtsecret.path"), env, value_name = "PATH")] + pub [<$prefix _auth_jwtsecret_path>]: Option, + + /// Hex encoded JWT secret to use for the authenticated engine-API RPC server. + #[arg(long = concat!(stringify!($prefix), ".authrpc.jwtsecret"), env, value_name = "HEX")] + pub [<$prefix _auth_jwtsecret>]: Option, + + /// Path to a JWT secret to authenticate the regular RPC server(s) + /// + /// This is __not__ used for the authenticated engine-API RPC server, see + /// `authrpc.jwtsecret`. + #[arg(long = concat!(stringify!($prefix), ".rpc.jwtsecret.path"), env, value_name = "PATH", required = false, conflicts_with = concat!(stringify!($prefix), "_rpc_jwtsecret"))] + pub [<$prefix _rpc_jwtsecret_path>]: Option, /// Hex encoded JWT secret to authenticate the regular RPC server(s) /// /// This is __not__ used for the authenticated engine-API RPC server, see /// `authrpc.jwtsecret`. - #[arg(long = concat!(stringify!($prefix), ".rpc.jwtsecret"), value_name = "PATH", required = false)] + #[arg(long = concat!(stringify!($prefix), ".rpc.jwtsecret"), env, value_name = "HEX", required = false, conflicts_with = concat!(stringify!($prefix), "_rpc_jwtsecret_path"))] pub [<$prefix _rpc_jwtsecret>]: Option, /// Filename for auth IPC socket/pipe within the datadir - #[arg(long = concat!(stringify!($prefix), ".auth.ipc.path"), value_name = "PATH")] + #[arg(long = concat!(stringify!($prefix), ".auth.ipc.path"), env, value_name = "PATH")] pub [<$prefix _auth_ipc_path>]: Option, /// Timeout for http calls in milliseconds - #[arg(long = concat!(stringify!($prefix), ".timeout"), default_value_t = 1000)] + #[arg(long = concat!(stringify!($prefix), ".timeout"), env, default_value_t = 1000)] pub [<$prefix _timeout>]: u64, } } diff --git a/src/main.rs b/src/main.rs index 609ba2fa79429..0acd960d0539b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -131,23 +131,41 @@ async fn main() -> eyre::Result<()> { } let l2_client_args = args.l2_client; + + let l2_jwt = match ( + l2_client_args.l2_auth_jwtsecret.as_ref(), + l2_client_args.l2_auth_jwtsecret_path.as_ref(), + ) { + (Some(secret), None) => *secret, + (None, Some(path)) => JwtSecret::from_file(path)?, + _ => return Err(eyre::eyre!("Missing JWT secret")), + }; // TODO: add support for optional JWT gated rpc (eth api, miner api, etc.) based on rpc_jwtsecret Some/None let l2_client = ExecutionClient::new( l2_client_args.l2_http_addr, l2_client_args.l2_http_port, l2_client_args.l2_auth_addr, l2_client_args.l2_auth_port, - l2_client_args.l2_auth_jwtsecret.clone(), + l2_jwt, l2_client_args.l2_timeout, )?; let builder_args = args.builder; + let builder_jwt = match ( + builder_args.builder_auth_jwtsecret.as_ref(), + builder_args.builder_auth_jwtsecret_path.as_ref(), + ) { + (Some(secret), None) => *secret, + (None, Some(path)) => JwtSecret::from_file(path)?, + _ => return Err(eyre::eyre!("Missing JWT secret")), + }; + let builder_client = ExecutionClient::new( builder_args.builder_http_addr, builder_args.builder_http_port, builder_args.builder_auth_addr, builder_args.builder_auth_port, - builder_args.builder_auth_jwtsecret, + builder_jwt, builder_args.builder_timeout, )?; @@ -157,12 +175,13 @@ async fn main() -> eyre::Result<()> { // server setup info!("Starting server on :{}", args.rpc_port); - let auth_rpc_uri = format!("http://{}:{}", l2_client_args.l2_auth_addr, l2_client_args.l2_auth_port).parse::()?; + let auth_rpc_uri = format!( + "http://{}:{}", + l2_client_args.l2_auth_addr, l2_client_args.l2_auth_port + ) + .parse::()?; - let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new( - auth_rpc_uri, - JwtSecret::from_file(&l2_client_args.l2_auth_jwtsecret)? - )); + let service_builder = tower::ServiceBuilder::new().layer(ProxyLayer::new(auth_rpc_uri, l2_jwt)); let server = Server::builder() .set_http_middleware(service_builder) .build(format!("{}:{}", args.rpc_host, args.rpc_port).parse::()?) diff --git a/src/proxy.rs b/src/proxy.rs index f48a508bf3c19..3b018cf064e91 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -3,13 +3,9 @@ use http::Uri; use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client; use hyper_util::rt::TokioExecutor; -use jsonrpsee::core::client::ClientT; -use jsonrpsee::core::traits::ToRpcParams; use jsonrpsee::core::{http_helpers, BoxError}; -use jsonrpsee::http_client::transport::HttpBackend; -use jsonrpsee::http_client::{HttpBody, HttpClient, HttpRequest, HttpResponse}; -use reth_rpc_layer::{secret_to_bearer_header, AuthClientService, JwtSecret}; -use std::sync::Arc; +use jsonrpsee::http_client::{HttpBody, HttpRequest, HttpResponse}; +use reth_rpc_layer::{secret_to_bearer_header, JwtSecret}; use std::task::{Context, Poll}; use std::{future::Future, pin::Pin}; use tower::{Layer, Service}; @@ -45,7 +41,7 @@ impl Layer for ProxyLayer { #[derive(Clone)] pub struct ProxyService { inner: S, - client: Client, + client: Client, target_url: Uri, secret: JwtSecret, } @@ -111,10 +107,11 @@ where Ok(res) } else { info!(target: "proxy::call", message = "forwarding request to l2 client", "method" = ?rpc_request.method); - // Modify the URI + // Modify the URI *req.uri_mut() = target_url.clone(); // Insert JWT Authorization headers - req.headers_mut().insert(AUTHORIZATION, secret_to_bearer_header(&secret)); + req.headers_mut() + .insert(AUTHORIZATION, secret_to_bearer_header(&secret)); // Forward the request let res = client @@ -138,13 +135,13 @@ mod tests { use http_body_util::BodyExt; use jsonrpsee::{ core::{client::ClientT, ClientError}, - http_client::{HttpClient, HttpClientBuilder}, + http_client::HttpClient, rpc_params, server::{ServerBuilder, ServerHandle}, types::{ErrorCode, ErrorObject}, RpcModule, }; - use reth_rpc_layer::{AuthClientLayer, JwtSecret}; + use reth_rpc_layer::JwtSecret; use super::*; @@ -257,16 +254,13 @@ mod tests { let addr = format!("{ADDR}:{PORT}"); let jwt = JwtSecret::random(); - let auth_layer = AuthClientLayer::new(jwt); - let l2_auth_client = HttpClientBuilder::new() - .set_http_middleware(tower::ServiceBuilder::new().layer(auth_layer)) - .build(format!( - "http://{}", - SocketAddr::new(IpAddr::from_str(ADDR).unwrap(), PORT as u16) - )) - .expect("Could not build auth client"); - - let proxy_layer = ProxyLayer::new(Arc::new(l2_auth_client)); + let l2_auth_uri = format!( + "http://{}", + SocketAddr::new(IpAddr::from_str(ADDR).unwrap(), PORT as u16) + ) + .parse::() + .unwrap(); + let proxy_layer = ProxyLayer::new(l2_auth_uri, jwt); // Create a layered server let server = ServerBuilder::default() diff --git a/src/server.rs b/src/server.rs index 7a524acff84c4..8af14d7216d13 100644 --- a/src/server.rs +++ b/src/server.rs @@ -646,12 +646,11 @@ mod tests { use alloy_rpc_types_engine::{ BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, PayloadStatusEnum, }; - use jsonrpsee::http_client::{HttpClient, HttpClientBuilder}; + use jsonrpsee::http_client::HttpClient; use jsonrpsee::server::{ServerBuilder, ServerHandle}; use jsonrpsee::RpcModule; use reth_rpc_layer::JwtSecret; use std::net::{IpAddr, SocketAddr}; - use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; use std::sync::Mutex; @@ -736,8 +735,6 @@ mod tests { let host = IpAddr::from_str(HOST).unwrap(); let jwt_secret = JwtSecret::random(); - // NOTE: update, placeholder for now - let jwt_secret = PathBuf::default(); let l2_client = ExecutionClient::new(host, L2_PORT, host, L2_PORT, jwt_secret.clone(), 2000) .unwrap(); From e10320221a8e43d2fc6a5c3bac209942c176ffd2 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 15:28:10 -0700 Subject: [PATCH 086/429] fix: new_with_auth (flashbots/rollup-boost) --- src/client.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/client.rs b/src/client.rs index b42d034a0ec92..35e547105672a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -73,14 +73,12 @@ impl ExecutionClient>> { pub fn _new_with_auth( http_addr: IpAddr, http_port: u16, - rpc_jwt_secret: PathBuf, + rpc_jwt_secret: JwtSecret, auth_addr: IpAddr, auth_port: u16, - auth_rpc_jwt_secret: PathBuf, + auth_rpc_jwt_secret: JwtSecret, timeout: u64, ) -> Result { - let jwt = std::fs::read_to_string(rpc_jwt_secret)?; - let rpc_jwt_secret = JwtSecret::from_hex(jwt)?; let rpc_auth_layer = AuthClientLayer::new(rpc_jwt_secret); let http_socket = SocketAddr::new(http_addr, http_port); let client = HttpClientBuilder::new() @@ -88,8 +86,6 @@ impl ExecutionClient>> { .request_timeout(Duration::from_millis(timeout)) .build(format!("http://{}", http_socket))?; - let jwt = std::fs::read_to_string(auth_rpc_jwt_secret)?; - let auth_rpc_jwt_secret = JwtSecret::from_hex(jwt)?; let auth_layer = AuthClientLayer::new(auth_rpc_jwt_secret); let auth_socket = SocketAddr::new(auth_addr, auth_port); let auth_client = HttpClientBuilder::new() From 7bf6997748f3c8b7ce87342d7e287127b9990cb4 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 15:46:35 -0700 Subject: [PATCH 087/429] fix: cli (flashbots/rollup-boost) --- src/client.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/client.rs b/src/client.rs index 35e547105672a..5348d30e5c236 100644 --- a/src/client.rs +++ b/src/client.rs @@ -108,10 +108,14 @@ macro_rules! define_rpc_args { $( paste! { #[derive(Parser, Debug, Clone, PartialEq, Eq)] - #[clap(group(ArgGroup::new(concat!(stringify!($prefix), "_jwt")).required(true).multiple(false).args(&[ + #[clap(group(ArgGroup::new(concat!(stringify!($prefix), "_auth_jwt")).required(true).multiple(false).args(&[ concat!(stringify!($prefix), "_auth_jwtsecret_path"), concat!(stringify!($prefix), "_auth_jwtsecret")]))) ] + #[clap(group(ArgGroup::new(concat!(stringify!($prefix), "_rpc_jwt")).required(false).multiple(false).args(&[ + concat!(stringify!($prefix), "_rpc_jwtsecret_path"), + concat!(stringify!($prefix), "_rpc_jwtsecret")]))) + ] pub struct $name { /// Http server address #[arg(long = concat!(stringify!($prefix), ".http.addr"), env, default_value = "127.0.0.1")] @@ -141,14 +145,14 @@ macro_rules! define_rpc_args { /// /// This is __not__ used for the authenticated engine-API RPC server, see /// `authrpc.jwtsecret`. - #[arg(long = concat!(stringify!($prefix), ".rpc.jwtsecret.path"), env, value_name = "PATH", required = false, conflicts_with = concat!(stringify!($prefix), "_rpc_jwtsecret"))] + #[arg(long = concat!(stringify!($prefix), ".rpc.jwtsecret.path"), env, value_name = "PATH")] pub [<$prefix _rpc_jwtsecret_path>]: Option, /// Hex encoded JWT secret to authenticate the regular RPC server(s) /// /// This is __not__ used for the authenticated engine-API RPC server, see /// `authrpc.jwtsecret`. - #[arg(long = concat!(stringify!($prefix), ".rpc.jwtsecret"), env, value_name = "HEX", required = false, conflicts_with = concat!(stringify!($prefix), "_rpc_jwtsecret_path"))] + #[arg(long = concat!(stringify!($prefix), ".rpc.jwtsecret"), env, value_name = "HEX")] pub [<$prefix _rpc_jwtsecret>]: Option, /// Filename for auth IPC socket/pipe within the datadir From 8e7bbfc548fae11063aa16b50f9f62ce77c1b22f Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 15:50:13 -0700 Subject: [PATCH 088/429] chore: nits (flashbots/rollup-boost) --- src/main.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0acd960d0539b..b174dd484a9db 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,13 +132,12 @@ async fn main() -> eyre::Result<()> { let l2_client_args = args.l2_client; - let l2_jwt = match ( - l2_client_args.l2_auth_jwtsecret.as_ref(), - l2_client_args.l2_auth_jwtsecret_path.as_ref(), - ) { - (Some(secret), None) => *secret, - (None, Some(path)) => JwtSecret::from_file(path)?, - _ => return Err(eyre::eyre!("Missing JWT secret")), + let l2_jwt = if let Some(secret) = l2_client_args.l2_auth_jwtsecret { + secret + } else if let Some(path) = l2_client_args.l2_auth_jwtsecret_path.as_ref() { + JwtSecret::from_file(path)? + } else { + return Err(eyre::eyre!("Missing L2 JWT secret")); }; // TODO: add support for optional JWT gated rpc (eth api, miner api, etc.) based on rpc_jwtsecret Some/None let l2_client = ExecutionClient::new( @@ -151,13 +150,12 @@ async fn main() -> eyre::Result<()> { )?; let builder_args = args.builder; - let builder_jwt = match ( - builder_args.builder_auth_jwtsecret.as_ref(), - builder_args.builder_auth_jwtsecret_path.as_ref(), - ) { - (Some(secret), None) => *secret, - (None, Some(path)) => JwtSecret::from_file(path)?, - _ => return Err(eyre::eyre!("Missing JWT secret")), + let builder_jwt = if let Some(secret) = builder_args.builder_auth_jwtsecret { + secret + } else if let Some(path) = builder_args.builder_auth_jwtsecret_path.as_ref() { + JwtSecret::from_file(path)? + } else { + return Err(eyre::eyre!("Missing Builder JWT secret")); }; let builder_client = ExecutionClient::new( From f0693291af53b5d413d08addab0924ae9d3fb619 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 15:52:03 -0700 Subject: [PATCH 089/429] add comment (flashbots/rollup-boost) --- src/client.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client.rs b/src/client.rs index 5348d30e5c236..ae18f43e6ad81 100644 --- a/src/client.rs +++ b/src/client.rs @@ -156,6 +156,8 @@ macro_rules! define_rpc_args { pub [<$prefix _rpc_jwtsecret>]: Option, /// Filename for auth IPC socket/pipe within the datadir + /// + /// NOTE: This is unimplemented currently #[arg(long = concat!(stringify!($prefix), ".auth.ipc.path"), env, value_name = "PATH")] pub [<$prefix _auth_ipc_path>]: Option, From f3fe79232d8cbd4ae2faf3ba27186721aac20ea9 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 15:52:32 -0700 Subject: [PATCH 090/429] update comments (flashbots/rollup-boost) --- src/client.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.rs b/src/client.rs index ae18f43e6ad81..dfb75632daec8 100644 --- a/src/client.rs +++ b/src/client.rs @@ -155,7 +155,7 @@ macro_rules! define_rpc_args { #[arg(long = concat!(stringify!($prefix), ".rpc.jwtsecret"), env, value_name = "HEX")] pub [<$prefix _rpc_jwtsecret>]: Option, - /// Filename for auth IPC socket/pipe within the datadir + /// Filename for auth IPC socket/pipe /// /// NOTE: This is unimplemented currently #[arg(long = concat!(stringify!($prefix), ".auth.ipc.path"), env, value_name = "PATH")] From ed4a32333cb6e3b9d9fee813ccfbcfe4de44d618 Mon Sep 17 00:00:00 2001 From: avalonche Date: Fri, 31 Jan 2025 09:54:02 +1100 Subject: [PATCH 091/429] add tracing (flashbots/rollup-boost) --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7deb4301e19dc..3c43265e8556e 100644 --- a/README.md +++ b/README.md @@ -111,12 +111,24 @@ To check if the rollup-boost server is running, you can check the health endpoin curl http://localhost:8081/healthz ``` -To enable metrics, you can set the `--metrics` flag. This will start a metrics server which will run on port 9090 by default. To see list of metrics, you can check the metrics endpoint: +### Metrics + +To enable metrics, you can set the `--metrics` flag. This will start a metrics server which will run on port 9090 by default. To see the list of metrics, you can checkout [metrics.rs](./src/metrics.rs) and ping the metrics endpoint: ``` curl http://localhost:9090/metrics ``` +To check that rollup-boost is sending requests to get blocks from the builder, you can check the `builder_get_payload_v3` metric which is incremented when a `engine_getPayloadV3` call is proxied to the builder. + +Additionally, execution engines such as op-rbuilder has rpc metrics exposed to check if `engine_getPayloadV3` requests have been received. To check if the builder blocks are landing on-chain, the builder can be configured to include a builder transaction in the block, which is captured as part of the builder metrics. To see more details about obserability in the op-builder, you can check op-rbuilder's [README](https://github.com/flashbots/rbuilder/tree/develop/crates/op-rbuilder). + +### Tracing + +Tracing is enabled by setting the `--tracing` flag. This will start exporting traces to the otlp endpoint specified in the `--otlp-endpoint` flag. This endpoint is set to `http://localhost:4317` by default. + +Traces use the payload id to track the block building lifecycle. A distributed tracing system such as [Jaeger](https://www.jaegertracing.io/) can be used to visualize when the proposer triggers block building via `engine_forkchoiceUpdatedV3` and retrieve the block with `engine_getPayloadV3`. + ## License The code in this project is free software under the [MIT License](/LICENSE). From 764a211807fd3ac4ac6c8547d2ec0fdeb838fa9c Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 19:47:06 -0500 Subject: [PATCH 092/429] update send raw transaction test (flashbots/rollup-boost) --- src/server.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/src/server.rs b/src/server.rs index 8af14d7216d13..d48128c531f89 100644 --- a/src/server.rs +++ b/src/server.rs @@ -646,14 +646,20 @@ mod tests { use alloy_rpc_types_engine::{ BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, PayloadStatusEnum, }; + use hyper::client::conn::http1; + use hyper::server::conn::http2; + use hyper::service::service_fn; + use hyper_util::rt::{TokioExecutor, TokioIo}; use jsonrpsee::http_client::HttpClient; use jsonrpsee::server::{ServerBuilder, ServerHandle}; use jsonrpsee::RpcModule; use reth_rpc_layer::JwtSecret; - use std::net::{IpAddr, SocketAddr}; + use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::str::FromStr; use std::sync::Arc; use std::sync::Mutex; + use tokio::net::TcpListener; + use tokio::task::JoinHandle; const HOST: &str = "0.0.0.0"; const L2_PORT: u16 = 8554; @@ -970,9 +976,93 @@ mod tests { server.start(module) } + struct MockHttpServer { + addr: SocketAddr, + requests: Arc>>, + join_handle: JoinHandle<()>, + } + + impl MockHttpServer { + async fn serve() -> eyre::Result { + let listener = TcpListener::bind("127.0.0.1:0").await?; + let addr = listener.local_addr()?; + let requests = Arc::new(Mutex::new(vec![])); + + let requests_clone = requests.clone(); + let handle = tokio::spawn(async move { + loop { + match listener.accept().await { + Ok((stream, _)) => { + let io = TokioIo::new(stream); + let requests = requests_clone.clone(); + + tokio::spawn(async move { + if let Err(err) = + hyper::server::conn::http2::Builder::new(TokioExecutor::new()) + .serve_connection( + io, + service_fn(move |req| { + Self::handle_request(req, requests.clone()) + }), + ) + .await + { + eprintln!("Error serving connection: {}", err); + } + }); + } + Err(e) => eprintln!("Error accepting connection: {}", e), + } + } + }); + + Ok(Self { + addr, + requests, + join_handle: handle, + }) + } + + async fn handle_request( + req: hyper::Request, + requests: Arc>>, + ) -> Result, hyper::Error> { + let path = req.uri().to_string(); + requests.lock().unwrap().push(path); + Ok(hyper::Response::new("OK".to_string())) + } + } + #[tokio::test] async fn test_send_raw_transaction() -> eyre::Result<()> { - todo!(); + let builder = MockHttpServer::serve().await?; + let l2 = MockHttpServer::serve().await?; + + let jwt = JwtSecret::random(); + let builder_client = ExecutionClient::new( + builder.addr.ip(), + builder.addr.port(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 0, + jwt, + 2000, + )?; + + let l2_client = ExecutionClient::new( + l2.addr.ip(), + l2.addr.port(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 0, + jwt, + 2000, + )?; + + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); + + let bytes = Bytes::from(hex::decode("0x1234")?); + rollup_boost.send_raw_transaction(bytes).await?; + + Ok(()) } #[tokio::test] From 167fa61f784d62e4b19291fb472781d3fcd6b42b Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 18:05:04 -0700 Subject: [PATCH 093/429] test: fix legacy tests (flashbots/rollup-boost) --- src/client.rs | 4 ++-- src/main.rs | 45 ++++++++++++++++++++++++++++++++++++++++++++- src/proxy.rs | 4 ++-- src/server.rs | 14 ++++++++++---- 4 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/client.rs b/src/client.rs index dfb75632daec8..07d401aa0cc37 100644 --- a/src/client.rs +++ b/src/client.rs @@ -155,8 +155,8 @@ macro_rules! define_rpc_args { #[arg(long = concat!(stringify!($prefix), ".rpc.jwtsecret"), env, value_name = "HEX")] pub [<$prefix _rpc_jwtsecret>]: Option, - /// Filename for auth IPC socket/pipe - /// + /// Filename for auth IPC socket/pipe + /// /// NOTE: This is unimplemented currently #[arg(long = concat!(stringify!($prefix), ".auth.ipc.path"), env, value_name = "PATH")] pub [<$prefix _auth_ipc_path>]: Option, diff --git a/src/main.rs b/src/main.rs index b174dd484a9db..733ad8777ed57 100644 --- a/src/main.rs +++ b/src/main.rs @@ -253,6 +253,7 @@ mod tests { use assert_cmd::Command; use jsonrpsee::core::client::ClientT; + use jsonrpsee::http_client::transport::Error as TransportError; use jsonrpsee::http_client::transport::HttpBackend; use jsonrpsee::http_client::HttpClient; use jsonrpsee::RpcModule; @@ -281,9 +282,51 @@ mod tests { )); } + #[tokio::test] + async fn test_create_client() { + valid_jwt().await; + invalid_jwt().await; + } + + async fn valid_jwt() { + let secret = JwtSecret::from_hex(SECRET).unwrap(); + let client = ExecutionClient::new( + AUTH_ADDR.parse().unwrap(), + 8545, + AUTH_ADDR.parse().unwrap(), + AUTH_PORT as u16, + secret, + 1000, + ) + .unwrap(); + let response = send_request(client.auth_client).await; + assert!(response.is_ok()); + assert_eq!(response.unwrap(), "You are the dark lord"); + } + + async fn invalid_jwt() { + let secret = JwtSecret::random(); + let client = ExecutionClient::new( + AUTH_ADDR.parse().unwrap(), + 8545, + AUTH_ADDR.parse().unwrap(), + AUTH_PORT as u16, + secret, + 1000, + ) + .unwrap(); + let response = send_request(client.auth_client).await; + assert!(response.is_err()); + assert!(matches!( + response.unwrap_err(), + ClientError::Transport(e) + if matches!(e.downcast_ref::(), Some(TransportError::Rejected { status_code: 401 })) + )); + } + // TODO: move these tests async fn send_request( - client: HttpClient>, + client: Arc>>, ) -> Result { let server = spawn_server().await; diff --git a/src/proxy.rs b/src/proxy.rs index 3b018cf064e91..e52c3b825e908 100644 --- a/src/proxy.rs +++ b/src/proxy.rs @@ -146,7 +146,7 @@ mod tests { use super::*; const PORT: u32 = 8552; - const ADDR: &str = "0.0.0.0"; + const ADDR: &str = "127.0.0.1"; const PROXY_PORT: u32 = 8553; #[tokio::test] @@ -256,7 +256,7 @@ mod tests { let jwt = JwtSecret::random(); let l2_auth_uri = format!( "http://{}", - SocketAddr::new(IpAddr::from_str(ADDR).unwrap(), PORT as u16) + SocketAddr::new(IpAddr::from_str(ADDR).unwrap(), PROXY_PORT as u16) ) .parse::() .unwrap(); diff --git a/src/server.rs b/src/server.rs index 8af14d7216d13..5ab2491304219 100644 --- a/src/server.rs +++ b/src/server.rs @@ -656,10 +656,10 @@ mod tests { use std::sync::Mutex; const HOST: &str = "0.0.0.0"; - const L2_PORT: u16 = 8554; - const L2_ADDR: &str = "0.0.0.0:8554"; - const BUILDER_PORT: u16 = 8555; - const BUILDER_ADDR: &str = "0.0.0.0.8555"; + const L2_PORT: u16 = 8545; + const L2_ADDR: &str = "127.0.0.1:8545"; + const BUILDER_PORT: u16 = 8544; + const BUILDER_ADDR: &str = "127.0.0.1:8544"; const SERVER_ADDR: &str = "0.0.0.0:8556"; #[derive(Debug, Clone)] @@ -939,6 +939,7 @@ mod tests { } async fn spawn_server(mock_engine_server: MockEngineServer, addr: &str) -> ServerHandle { + println!("Starting server at {}", addr); let server = ServerBuilder::default().build(addr).await.unwrap(); let mut module: RpcModule<()> = RpcModule::new(()); module @@ -971,26 +972,31 @@ mod tests { } #[tokio::test] + #[ignore] async fn test_send_raw_transaction() -> eyre::Result<()> { todo!(); } #[tokio::test] + #[ignore] async fn test_set_gas_limit() -> eyre::Result<()> { todo!(); } #[tokio::test] + #[ignore] async fn test_set_gas_price() -> eyre::Result<()> { todo!(); } #[tokio::test] + #[ignore] async fn test_set_extra() -> eyre::Result<()> { todo!(); } #[tokio::test] + #[ignore] async fn test_set_max_da_size() -> eyre::Result<()> { todo!(); } From 4343f2f942a51e14e30a3e1238a26757aa23b366 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 21:09:35 -0700 Subject: [PATCH 094/429] update tests (flashbots/rollup-boost) --- src/server.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/server.rs b/src/server.rs index 0d24741e570f9..ec74438972a13 100644 --- a/src/server.rs +++ b/src/server.rs @@ -752,10 +752,7 @@ mod tests { let rollup_boost_client = RollupBoostServer::new(l2_client, builder_client, boost_sync, None); - let mut module: RpcModule<()> = RpcModule::new(()); - module - .merge(EngineApiServer::into_rpc(rollup_boost_client)) - .unwrap(); + let module: RpcModule<()> = rollup_boost_client.try_into().unwrap(); let proxy_server = ServerBuilder::default() .build("0.0.0.0:8556".parse::().unwrap()) @@ -999,7 +996,7 @@ mod tests { tokio::spawn(async move { if let Err(err) = - hyper::server::conn::http2::Builder::new(TokioExecutor::new()) + hyper::server::conn::http1::Builder::new() .serve_connection( io, service_fn(move |req| { @@ -1035,8 +1032,8 @@ mod tests { } #[tokio::test] - #[ignore] async fn test_send_raw_transaction() -> eyre::Result<()> { + tracing_subscriber::fmt::init(); let builder = MockHttpServer::serve().await?; let l2 = MockHttpServer::serve().await?; From feef3854a9ec449d966f92800690f357af731814 Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Thu, 30 Jan 2025 23:58:19 -0500 Subject: [PATCH 095/429] wip tests (flashbots/rollup-boost) --- src/server.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/server.rs b/src/server.rs index ec74438972a13..45ac0036b80d3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -646,6 +646,7 @@ mod tests { use alloy_rpc_types_engine::{ BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, PayloadStatusEnum, }; + use http_body::Body; use hyper::client::conn::http1; use hyper::server::conn::http2; use hyper::service::service_fn; @@ -654,6 +655,7 @@ mod tests { use jsonrpsee::server::{ServerBuilder, ServerHandle}; use jsonrpsee::RpcModule; use reth_rpc_layer::JwtSecret; + use serde_json::json; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::str::FromStr; use std::sync::Arc; @@ -995,15 +997,14 @@ mod tests { let requests = requests_clone.clone(); tokio::spawn(async move { - if let Err(err) = - hyper::server::conn::http1::Builder::new() - .serve_connection( - io, - service_fn(move |req| { - Self::handle_request(req, requests.clone()) - }), - ) - .await + if let Err(err) = hyper::server::conn::http1::Builder::new() + .serve_connection( + io, + service_fn(move |req| { + Self::handle_request(req, requests.clone()) + }), + ) + .await { eprintln!("Error serving connection: {}", err); } @@ -1027,7 +1028,14 @@ mod tests { ) -> Result, hyper::Error> { let path = req.uri().to_string(); requests.lock().unwrap().push(path); - Ok(hyper::Response::new("OK".to_string())) + + let response = json!({ + "jsonrpc": "2.0", + "result": format!("{:?}", B256::default()), + "id": 0 + }); + + return Ok(hyper::Response::new(response.to_string())); } } From 7f4dfffbded49cf10fa072be127bb7f5703a871c Mon Sep 17 00:00:00 2001 From: 0xKitsune <0xkitsune@protonmail.com> Date: Fri, 31 Jan 2025 00:11:01 -0500 Subject: [PATCH 096/429] handle send raw tx endpoint (flashbots/rollup-boost) --- src/server.rs | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/server.rs b/src/server.rs index 45ac0036b80d3..ca9f408da4c9e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -647,6 +647,7 @@ mod tests { BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, PayloadStatusEnum, }; use http_body::Body; + use http_body_util::BodyExt; use hyper::client::conn::http1; use hyper::server::conn::http2; use hyper::service::service_fn; @@ -1029,11 +1030,44 @@ mod tests { let path = req.uri().to_string(); requests.lock().unwrap().push(path); - let response = json!({ - "jsonrpc": "2.0", - "result": format!("{:?}", B256::default()), - "id": 0 - }); + let body_bytes = match req.into_body().collect().await { + Ok(buf) => buf.to_bytes(), + Err(_) => { + let error_response = json!({ + "jsonrpc": "2.0", + "error": { "code": -32700, "message": "Failed to read request body" }, + "id": null + }); + return Ok(hyper::Response::new(error_response.to_string())); + } + }; + + let request_body: serde_json::Value = match serde_json::from_slice(&body_bytes) { + Ok(json) => json, + Err(_) => { + let error_response = json!({ + "jsonrpc": "2.0", + "error": { "code": -32700, "message": "Invalid JSON format" }, + "id": null + }); + return Ok(hyper::Response::new(error_response.to_string())); + } + }; + + let method = request_body["method"].as_str().unwrap_or_default(); + + let response = match method { + "eth_sendRawTransaction" => json!({ + "jsonrpc": "2.0", + "result": format!("{}", B256::default()), + "id": request_body["id"] + }), + _ => json!({ + "jsonrpc": "2.0", + "error": { "code": -32601, "message": "Method not found" }, + "id": request_body["id"] + }), + }; return Ok(hyper::Response::new(response.to_string())); } From 98d3699ff6f1c0211258f18692affca04e5693b9 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 22:21:26 -0700 Subject: [PATCH 097/429] test: add miner tests (flashbots/rollup-boost) --- src/server.rs | 149 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 132 insertions(+), 17 deletions(-) diff --git a/src/server.rs b/src/server.rs index ca9f408da4c9e..e8320ad5858a5 100644 --- a/src/server.rs +++ b/src/server.rs @@ -980,7 +980,7 @@ mod tests { struct MockHttpServer { addr: SocketAddr, requests: Arc>>, - join_handle: JoinHandle<()>, + _join_handle: JoinHandle<()>, } impl MockHttpServer { @@ -1019,7 +1019,7 @@ mod tests { Ok(Self { addr, requests, - join_handle: handle, + _join_handle: handle, }) } @@ -1062,11 +1062,22 @@ mod tests { "result": format!("{}", B256::default()), "id": request_body["id"] }), - _ => json!({ - "jsonrpc": "2.0", - "error": { "code": -32601, "message": "Method not found" }, - "id": request_body["id"] - }), + "miner_setMaxDASize" | "miner_setGasLimit" | "miner_setGasPrice" + | "miner_setExtra" => { + json!({ + "jsonrpc": "2.0", + "result": true, + "id": request_body["id"] + }) + } + _ => { + let error_response = json!({ + "jsonrpc": "2.0", + "error": { "code": -32601, "message": "Method not found" }, + "id": request_body["id"] + }); + return Ok(hyper::Response::new(error_response.to_string())); + } }; return Ok(hyper::Response::new(response.to_string())); @@ -1075,7 +1086,6 @@ mod tests { #[tokio::test] async fn test_send_raw_transaction() -> eyre::Result<()> { - tracing_subscriber::fmt::init(); let builder = MockHttpServer::serve().await?; let l2 = MockHttpServer::serve().await?; @@ -1102,31 +1112,136 @@ mod tests { let bytes = Bytes::from(hex::decode("0x1234")?); rollup_boost.send_raw_transaction(bytes).await?; - + assert!(builder.requests.lock().unwrap().len() == 1); + assert!(l2.requests.lock().unwrap().len() == 1); Ok(()) } #[tokio::test] - #[ignore] async fn test_set_gas_limit() -> eyre::Result<()> { - todo!(); + let builder = MockHttpServer::serve().await?; + let l2 = MockHttpServer::serve().await?; + + let jwt = JwtSecret::random(); + let builder_client = ExecutionClient::new( + builder.addr.ip(), + builder.addr.port(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 0, + jwt, + 2000, + )?; + + let l2_client = ExecutionClient::new( + l2.addr.ip(), + l2.addr.port(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 0, + jwt, + 2000, + )?; + + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); + + rollup_boost.set_gas_limit(U128::ZERO).await?; + assert!(builder.requests.lock().unwrap().len() == 1); + assert!(l2.requests.lock().unwrap().len() == 1); + Ok(()) } #[tokio::test] - #[ignore] async fn test_set_gas_price() -> eyre::Result<()> { - todo!(); + let builder = MockHttpServer::serve().await?; + let l2 = MockHttpServer::serve().await?; + + let jwt = JwtSecret::random(); + let builder_client = ExecutionClient::new( + builder.addr.ip(), + builder.addr.port(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 0, + jwt, + 2000, + )?; + + let l2_client = ExecutionClient::new( + l2.addr.ip(), + l2.addr.port(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 0, + jwt, + 2000, + )?; + + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); + + rollup_boost.set_gas_price(U128::ZERO).await?; + assert!(builder.requests.lock().unwrap().len() == 1); + assert!(l2.requests.lock().unwrap().len() == 1); + Ok(()) } #[tokio::test] - #[ignore] async fn test_set_extra() -> eyre::Result<()> { - todo!(); + let builder = MockHttpServer::serve().await?; + let l2 = MockHttpServer::serve().await?; + + let jwt = JwtSecret::random(); + let builder_client = ExecutionClient::new( + builder.addr.ip(), + builder.addr.port(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 0, + jwt, + 2000, + )?; + + let l2_client = ExecutionClient::new( + l2.addr.ip(), + l2.addr.port(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 0, + jwt, + 2000, + )?; + + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); + + rollup_boost.set_extra(Bytes::default()).await?; + assert!(builder.requests.lock().unwrap().len() == 1); + assert!(l2.requests.lock().unwrap().len() == 1); + Ok(()) } #[tokio::test] - #[ignore] async fn test_set_max_da_size() -> eyre::Result<()> { - todo!(); + let builder = MockHttpServer::serve().await?; + let l2 = MockHttpServer::serve().await?; + + let jwt = JwtSecret::random(); + let builder_client = ExecutionClient::new( + builder.addr.ip(), + builder.addr.port(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 0, + jwt, + 2000, + )?; + + let l2_client = ExecutionClient::new( + l2.addr.ip(), + l2.addr.port(), + IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), + 0, + jwt, + 2000, + )?; + + let rollup_boost = RollupBoostServer::new(l2_client, builder_client, false, None); + + rollup_boost.set_max_da_size(U64::ZERO, U64::ZERO).await?; + assert!(builder.requests.lock().unwrap().len() == 1); + assert!(l2.requests.lock().unwrap().len() == 1); + Ok(()) } } From b02d045516f04248db8b51ae6a905fcc09f04d18 Mon Sep 17 00:00:00 2001 From: 0xOsiris Date: Thu, 30 Jan 2025 22:22:58 -0700 Subject: [PATCH 098/429] chore: cleanup imports (flashbots/rollup-boost) --- src/server.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/server.rs b/src/server.rs index e8320ad5858a5..6b58567f0f067 100644 --- a/src/server.rs +++ b/src/server.rs @@ -646,12 +646,9 @@ mod tests { use alloy_rpc_types_engine::{ BlobsBundleV1, ExecutionPayloadV1, ExecutionPayloadV2, PayloadStatusEnum, }; - use http_body::Body; use http_body_util::BodyExt; - use hyper::client::conn::http1; - use hyper::server::conn::http2; use hyper::service::service_fn; - use hyper_util::rt::{TokioExecutor, TokioIo}; + use hyper_util::rt::TokioIo; use jsonrpsee::http_client::HttpClient; use jsonrpsee::server::{ServerBuilder, ServerHandle}; use jsonrpsee::RpcModule; From ad44dd9466c3e2c3578ddbfaaf2dab36f59f6f8d Mon Sep 17 00:00:00 2001 From: avalonche Date: Fri, 31 Jan 2025 19:52:26 +1100 Subject: [PATCH 099/429] add runbook (flashbots/rollup-boost) --- README.md | 29 +-------------- assets/rollup-boost-architecture.png | Bin 0 -> 254325 bytes assets/rollup-boost-op-conductor.png | Bin 0 -> 581993 bytes docs/high-availability-setup.md | 0 docs/local-devnet.md | 0 docs/running-rollup-boost.md | 53 +++++++++++++++++++++++++++ 6 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 assets/rollup-boost-architecture.png create mode 100644 assets/rollup-boost-op-conductor.png delete mode 100644 docs/high-availability-setup.md delete mode 100644 docs/local-devnet.md diff --git a/README.md b/README.md index 3c43265e8556e..36a46fa108df7 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ cargo run -- [OPTIONS] - `--rpc-port `: Port to run the server on (default: 8081) - `--tracing`: Enable tracing (default: false) - `--log-level `: Log level (default: info) +- `--log-format `: Log format (default: text) - `--metrics`: Enable metrics (default: false) - `--boost-sync`: Enable syncing the builder with the proposer op-node (default: false) @@ -32,7 +33,7 @@ You can also set the options using environment variables. See .env.example to us ### Example ``` -cargo run --jwt-token your_jwt_token --l2-url http://localhost:8551 --builder-url http://localhost:8546 +cargo run -- --jwt-token your_jwt_token --l2-url http://localhost:8551 --builder-url http://localhost:8546 ``` ## Core System Workflow @@ -103,32 +104,6 @@ By default, `rollup-boost` will proxy all RPC calls from the proposer `op-node` - `engine_forkchoiceUpdatedV3`: this call will be multiplexed to the builder regardless of whether the call contains payload attributes or not. - `engine_newPayloadV3`: ensures the builder has the latest block if the local payload was used. -## Observability - -To check if the rollup-boost server is running, you can check the health endpoint: - -``` -curl http://localhost:8081/healthz -``` - -### Metrics - -To enable metrics, you can set the `--metrics` flag. This will start a metrics server which will run on port 9090 by default. To see the list of metrics, you can checkout [metrics.rs](./src/metrics.rs) and ping the metrics endpoint: - -``` -curl http://localhost:9090/metrics -``` - -To check that rollup-boost is sending requests to get blocks from the builder, you can check the `builder_get_payload_v3` metric which is incremented when a `engine_getPayloadV3` call is proxied to the builder. - -Additionally, execution engines such as op-rbuilder has rpc metrics exposed to check if `engine_getPayloadV3` requests have been received. To check if the builder blocks are landing on-chain, the builder can be configured to include a builder transaction in the block, which is captured as part of the builder metrics. To see more details about obserability in the op-builder, you can check op-rbuilder's [README](https://github.com/flashbots/rbuilder/tree/develop/crates/op-rbuilder). - -### Tracing - -Tracing is enabled by setting the `--tracing` flag. This will start exporting traces to the otlp endpoint specified in the `--otlp-endpoint` flag. This endpoint is set to `http://localhost:4317` by default. - -Traces use the payload id to track the block building lifecycle. A distributed tracing system such as [Jaeger](https://www.jaegertracing.io/) can be used to visualize when the proposer triggers block building via `engine_forkchoiceUpdatedV3` and retrieve the block with `engine_getPayloadV3`. - ## License The code in this project is free software under the [MIT License](/LICENSE). diff --git a/assets/rollup-boost-architecture.png b/assets/rollup-boost-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..e4627333800eedcd8a9cede6b92c37891d4d15d4 GIT binary patch literal 254325 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 z%0D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 literal 0 HcmV?d00001 diff --git a/assets/rollup-boost-op-conductor.png b/assets/rollup-boost-op-conductor.png new file mode 100644 index 0000000000000000000000000000000000000000..6c6c45c8e635db52ab9126241c4471f990e4a586 GIT binary patch literal 581993 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�e<^`>qywNo%kxk;xn10vAktpN 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¥)pyFE}V<#a#i-M`?(%~AMW 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≤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