From 147c01ff5a0e91e4d22d6edaaa676a5005bd4a47 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 9 Jul 2025 13:11:20 -0700 Subject: [PATCH 01/45] wip --- Cargo.lock | 759 +++++++++++++++++- Cargo.toml | 8 + crates/flashblocks-p2p/.gitignore | 1 + crates/flashblocks-p2p/Cargo.toml | 35 + .../flashblocks-p2p/src/bin.bak/main.rs.bak | 52 ++ .../flashblocks-p2p/src/connection/handler.rs | 56 ++ crates/flashblocks-p2p/src/connection/mod.rs | 78 ++ crates/flashblocks-p2p/src/lib.rs | 2 + crates/flashblocks-p2p/src/lib.rs.bak | 187 +++++ crates/flashblocks-p2p/src/protocol/event.rs | 14 + .../flashblocks-p2p/src/protocol/handler.rs | 37 + crates/flashblocks-p2p/src/protocol/mod.rs | 3 + crates/flashblocks-p2p/src/protocol/proto.rs | 116 +++ 13 files changed, 1315 insertions(+), 33 deletions(-) create mode 100644 crates/flashblocks-p2p/.gitignore create mode 100644 crates/flashblocks-p2p/Cargo.toml create mode 100644 crates/flashblocks-p2p/src/bin.bak/main.rs.bak create mode 100644 crates/flashblocks-p2p/src/connection/handler.rs create mode 100644 crates/flashblocks-p2p/src/connection/mod.rs create mode 100644 crates/flashblocks-p2p/src/lib.rs create mode 100644 crates/flashblocks-p2p/src/lib.rs.bak create mode 100644 crates/flashblocks-p2p/src/protocol/event.rs create mode 100644 crates/flashblocks-p2p/src/protocol/handler.rs create mode 100644 crates/flashblocks-p2p/src/protocol/mod.rs create mode 100644 crates/flashblocks-p2p/src/protocol/proto.rs diff --git a/Cargo.lock b/Cargo.lock index 453515f6..847c705d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1804,6 +1804,145 @@ dependencies = [ "zeroize", ] +[[package]] +name = "boa_ast" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c340fe0f0b267787095cbe35240c6786ff19da63ec7b69367ba338eace8169b" +dependencies = [ + "bitflags 2.9.1", + "boa_interner", + "boa_macros", + "boa_string", + "indexmap 2.10.0", + "num-bigint", + "rustc-hash 2.1.1", +] + +[[package]] +name = "boa_engine" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f620c3f06f51e65c0504ddf04978be1b814ac6586f0b45f6019801ab5efd37f9" +dependencies = [ + "arrayvec", + "bitflags 2.9.1", + "boa_ast", + "boa_gc", + "boa_interner", + "boa_macros", + "boa_parser", + "boa_profiler", + "boa_string", + "bytemuck", + "cfg-if", + "dashmap 6.1.0", + "fast-float2", + "hashbrown 0.15.4", + "icu_normalizer 1.5.0", + "indexmap 2.10.0", + "intrusive-collections", + "itertools 0.13.0", + "num-bigint", + "num-integer", + "num-traits", + "num_enum", + "once_cell", + "pollster", + "portable-atomic", + "rand 0.8.5", + "regress", + "rustc-hash 2.1.1", + "ryu-js", + "serde", + "serde_json", + "sptr", + "static_assertions", + "tap", + "thin-vec", + "thiserror 2.0.12", + "time", +] + +[[package]] +name = "boa_gc" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2425c0b7720d42d73eaa6a883fbb77a5c920da8694964a3d79a67597ac55cce2" +dependencies = [ + "boa_macros", + "boa_profiler", + "boa_string", + "hashbrown 0.15.4", + "thin-vec", +] + +[[package]] +name = "boa_interner" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42407a3b724cfaecde8f7d4af566df4b56af32a2f11f0956f5570bb974e7f749" +dependencies = [ + "boa_gc", + "boa_macros", + "hashbrown 0.15.4", + "indexmap 2.10.0", + "once_cell", + "phf", + "rustc-hash 2.1.1", + "static_assertions", +] + +[[package]] +name = "boa_macros" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fd3f870829131332587f607a7ff909f1af5fc523fd1b192db55fbbdf52e8d3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + +[[package]] +name = "boa_parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cc142dac798cdc6e2dbccfddeb50f36d2523bb977a976e19bdb3ae19b740804" +dependencies = [ + "bitflags 2.9.1", + "boa_ast", + "boa_interner", + "boa_macros", + "boa_profiler", + "fast-float2", + "icu_properties 1.5.1", + "num-bigint", + "num-traits", + "regress", + "rustc-hash 2.1.1", +] + +[[package]] +name = "boa_profiler" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4064908e7cdf9b6317179e9b04dcb27f1510c1c144aeab4d0394014f37a0f922" + +[[package]] +name = "boa_string" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7debc13fbf7997bf38bf8e9b20f1ad5e2a7d27a900e1f6039fe244ce30f589b5" +dependencies = [ + "fast-float2", + "paste", + "rustc-hash 2.1.1", + "sptr", + "static_assertions", +] + [[package]] name = "bollard" version = "0.18.1" @@ -1928,6 +2067,20 @@ name = "bytemuck" version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] [[package]] name = "byteorder" @@ -2253,6 +2406,18 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console" +version = "0.15.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "const-hex" version = "1.14.1" @@ -2988,6 +3153,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -3173,6 +3344,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fast-float2" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55" + [[package]] name = "fastrand" version = "2.3.0" @@ -3251,6 +3428,24 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flashblocks-p2p" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "clap", + "eyre", + "futures", + "reth", + "reth-eth-wire", + "reth-ethereum", + "reth-network", + "rollup-boost", + "tokio", + "tokio-stream", + "tracing", +] + [[package]] name = "flashblocks-rpc" version = "0.1.0" @@ -4085,6 +4280,18 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke 0.7.5", + "zerofrom", + "zerovec 0.10.4", +] + [[package]] name = "icu_collections" version = "2.0.0" @@ -4093,9 +4300,9 @@ checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ "displaydoc", "potential_utf", - "yoke", + "yoke 0.8.0", "zerofrom", - "zerovec", + "zerovec 0.11.2", ] [[package]] @@ -4105,10 +4312,61 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", + "litemap 0.8.0", + "tinystr 0.8.1", + "writeable 0.6.1", + "zerovec 0.11.2", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap 0.7.5", + "tinystr 0.7.6", + "writeable 0.5.5", + "zerovec 0.10.4", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider 1.5.0", + "tinystr 0.7.6", + "zerovec 0.10.4", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections 1.5.0", + "icu_normalizer_data 1.5.1", + "icu_properties 1.5.1", + "icu_provider 1.5.0", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec 0.10.4", ] [[package]] @@ -4118,20 +4376,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", + "icu_collections 2.0.0", + "icu_normalizer_data 2.0.0", + "icu_properties 2.0.1", + "icu_provider 2.0.0", "smallvec", - "zerovec", + "zerovec 0.11.2", ] +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + [[package]] name = "icu_normalizer_data" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections 1.5.0", + "icu_locid_transform", + "icu_properties_data 1.5.1", + "icu_provider 1.5.0", + "tinystr 0.7.6", + "zerovec 0.10.4", +] + [[package]] name = "icu_properties" version = "2.0.1" @@ -4139,21 +4418,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ "displaydoc", - "icu_collections", + "icu_collections 2.0.0", "icu_locale_core", - "icu_properties_data", - "icu_provider", + "icu_properties_data 2.0.1", + "icu_provider 2.0.0", "potential_utf", "zerotrie", - "zerovec", + "zerovec 0.11.2", ] +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + [[package]] name = "icu_properties_data" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr 0.7.6", + "writeable 0.5.5", + "yoke 0.7.5", + "zerofrom", + "zerovec 0.10.4", +] + [[package]] name = "icu_provider" version = "2.0.0" @@ -4163,12 +4465,23 @@ dependencies = [ "displaydoc", "icu_locale_core", "stable_deref_trait", - "tinystr", - "writeable", - "yoke", + "tinystr 0.8.1", + "writeable 0.6.1", + "yoke 0.8.0", "zerofrom", "zerotrie", - "zerovec", + "zerovec 0.11.2", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] @@ -4194,8 +4507,8 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ - "icu_normalizer", - "icu_properties", + "icu_normalizer 2.0.0", + "icu_properties 2.0.1", ] [[package]] @@ -4349,6 +4662,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "intrusive-collections" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" +dependencies = [ + "memoffset", +] + [[package]] name = "ipconfig" version = "0.3.2" @@ -4990,6 +5312,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + [[package]] name = "litemap" version = "0.8.0" @@ -5131,6 +5459,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "metrics" version = "0.24.2" @@ -5502,6 +5839,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", + "serde", ] [[package]] @@ -6174,6 +6512,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + [[package]] name = "polyval" version = "0.6.2" @@ -6198,7 +6542,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ - "zerovec", + "zerovec 0.11.2", ] [[package]] @@ -6799,6 +7143,16 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "regress" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ef7fa9ed0256d64a688a3747d0fef7a88851c18a5e1d57f115f38ec2e09366" +dependencies = [ + "hashbrown 0.15.4", + "memchr", +] + [[package]] name = "reqwest" version = "0.12.21" @@ -6848,12 +7202,58 @@ dependencies = [ "webpki-roots 1.0.1", ] -[[package]] -name = "resolv-conf" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" - +[[package]] +name = "resolv-conf" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" + +[[package]] +name = "reth" +version = "1.5.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +dependencies = [ + "alloy-rpc-types", + "aquamarine", + "clap", + "eyre", + "reth-chainspec", + "reth-cli-runner", + "reth-cli-util", + "reth-consensus", + "reth-consensus-common", + "reth-db", + "reth-ethereum-cli", + "reth-ethereum-payload-builder", + "reth-ethereum-primitives", + "reth-evm", + "reth-network", + "reth-network-api", + "reth-node-api", + "reth-node-builder", + "reth-node-core", + "reth-node-ethereum", + "reth-node-metrics", + "reth-payload-builder", + "reth-payload-primitives", + "reth-primitives", + "reth-provider", + "reth-ress-protocol", + "reth-ress-provider", + "reth-revm", + "reth-rpc", + "reth-rpc-api", + "reth-rpc-builder", + "reth-rpc-convert", + "reth-rpc-eth-types", + "reth-rpc-server-types", + "reth-tasks", + "reth-tokio-util", + "reth-transaction-pool", + "tokio", + "tracing", +] + [[package]] name = "reth-basic-payload-builder" version = "1.5.0" @@ -7039,6 +7439,7 @@ dependencies = [ "secp256k1 0.30.0", "serde", "thiserror 2.0.12", + "tikv-jemallocator", ] [[package]] @@ -7688,6 +8089,103 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "reth-ethereum" +version = "1.5.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +dependencies = [ + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "reth-chainspec", + "reth-cli-util", + "reth-codecs", + "reth-consensus", + "reth-consensus-common", + "reth-db", + "reth-eth-wire", + "reth-ethereum-cli", + "reth-ethereum-consensus", + "reth-ethereum-primitives", + "reth-evm", + "reth-evm-ethereum", + "reth-network", + "reth-network-api", + "reth-node-api", + "reth-node-builder", + "reth-node-core", + "reth-node-ethereum", + "reth-primitives-traits", + "reth-provider", + "reth-revm", + "reth-rpc", + "reth-rpc-api", + "reth-rpc-builder", + "reth-rpc-eth-types", + "reth-storage-api", + "reth-tasks", + "reth-trie", + "reth-trie-db", +] + +[[package]] +name = "reth-ethereum-cli" +version = "1.5.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-rpc-types", + "backon", + "clap", + "eyre", + "futures", + "reth-basic-payload-builder", + "reth-chainspec", + "reth-cli", + "reth-cli-commands", + "reth-cli-runner", + "reth-cli-util", + "reth-config", + "reth-consensus", + "reth-db", + "reth-db-api", + "reth-downloaders", + "reth-errors", + "reth-ethereum-payload-builder", + "reth-ethereum-primitives", + "reth-evm", + "reth-execution-types", + "reth-exex", + "reth-fs-util", + "reth-network", + "reth-network-api", + "reth-network-p2p", + "reth-node-api", + "reth-node-builder", + "reth-node-core", + "reth-node-ethereum", + "reth-node-events", + "reth-node-metrics", + "reth-payload-builder", + "reth-primitives-traits", + "reth-provider", + "reth-prune", + "reth-revm", + "reth-stages", + "reth-static-file", + "reth-tasks", + "reth-tracing", + "reth-transaction-pool", + "reth-trie", + "reth-trie-db", + "serde_json", + "similar-asserts", + "tokio", + "tracing", +] + [[package]] name = "reth-ethereum-consensus" version = "1.5.0" @@ -7822,6 +8320,7 @@ dependencies = [ "alloy-eips", "alloy-evm", "alloy-primitives", + "derive_more", "reth-chainspec", "reth-ethereum-forks", "reth-ethereum-primitives", @@ -8394,6 +8893,7 @@ dependencies = [ "procfs", "reth-metrics", "reth-tasks", + "tikv-jemalloc-ctl", "tokio", "tower 0.5.2", "tracing", @@ -8971,6 +9471,52 @@ dependencies = [ "thiserror 2.0.12", ] +[[package]] +name = "reth-ress-protocol" +version = "1.5.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "alloy-rlp", + "futures", + "reth-eth-wire", + "reth-ethereum-primitives", + "reth-network", + "reth-network-api", + "reth-storage-errors", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "reth-ress-provider" +version = "1.5.0" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +dependencies = [ + "alloy-consensus", + "alloy-primitives", + "eyre", + "futures", + "parking_lot", + "reth-chain-state", + "reth-errors", + "reth-ethereum-primitives", + "reth-evm", + "reth-node-api", + "reth-primitives-traits", + "reth-ress-protocol", + "reth-revm", + "reth-storage-api", + "reth-tasks", + "reth-tokio-util", + "reth-trie", + "schnellru", + "tokio", + "tracing", +] + [[package]] name = "reth-revm" version = "1.5.0" @@ -9822,6 +10368,8 @@ dependencies = [ "alloy-rpc-types-trace", "alloy-sol-types", "anstyle", + "boa_engine", + "boa_gc", "colorchoice", "revm", "serde", @@ -10283,6 +10831,12 @@ version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" +[[package]] +name = "ryu-js" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" + [[package]] name = "same-file" version = "1.0.6" @@ -10721,6 +11275,27 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +dependencies = [ + "bstr", + "unicode-segmentation", +] + +[[package]] +name = "similar-asserts" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b441962c817e33508847a22bd82f03a30cff43642dc2fae8b050566121eb9a" +dependencies = [ + "console", + "serde", + "similar", +] + [[package]] name = "simple_asn1" version = "0.6.3" @@ -10818,6 +11393,12 @@ dependencies = [ "der", ] +[[package]] +name = "sptr" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -11089,6 +11670,12 @@ dependencies = [ "testcontainers", ] +[[package]] +name = "thin-vec" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" + [[package]] name = "thiserror" version = "1.0.69" @@ -11147,6 +11734,37 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "tikv-jemalloc-ctl" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21f216790c8df74ce3ab25b534e0718da5a1916719771d3fec23315c99e468b" +dependencies = [ + "libc", + "paste", + "tikv-jemalloc-sys", +] + +[[package]] +name = "tikv-jemalloc-sys" +version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "tikv-jemallocator" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cec5ff18518d81584f477e9bfdf957f5bb0979b0bac3af4ca30b5b3ae2d2865" +dependencies = [ + "libc", + "tikv-jemalloc-sys", +] + [[package]] name = "time" version = "0.3.41" @@ -11155,6 +11773,7 @@ checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", + "js-sys", "libc", "num-conv", "num_threads", @@ -11189,6 +11808,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec 0.10.4", +] + [[package]] name = "tinystr" version = "0.8.1" @@ -11196,7 +11825,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", - "zerovec", + "zerovec 0.11.2", ] [[package]] @@ -11822,6 +12451,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -12715,6 +13350,18 @@ dependencies = [ "bitflags 2.9.1", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "writeable" version = "0.6.1" @@ -12765,6 +13412,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive 0.7.5", + "zerofrom", +] + [[package]] name = "yoke" version = "0.8.0" @@ -12773,10 +13432,22 @@ checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" dependencies = [ "serde", "stable_deref_trait", - "yoke-derive", + "yoke-derive 0.8.0", "zerofrom", ] +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", + "synstructure", +] + [[package]] name = "yoke-derive" version = "0.8.0" @@ -12857,8 +13528,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ "displaydoc", - "yoke", + "yoke 0.8.0", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke 0.7.5", "zerofrom", + "zerovec-derive 0.10.3", ] [[package]] @@ -12867,9 +13549,20 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" dependencies = [ - "yoke", + "yoke 0.8.0", "zerofrom", - "zerovec-derive", + "zerovec-derive 0.11.1", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1817834d..3a8b30a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,13 @@ [workspace] resolver = "3" +edition = "2024" +license = "MIT" members = [ "crates/rollup-boost", "crates/websocket-proxy", "crates/flashblocks-rpc", + "crates/flashblocks-p2p", ] [workspace.dependencies] @@ -20,12 +23,17 @@ serde_json = "1.0.96" metrics = "0.24.0" metrics-derive = "0.1" tokio = { version = "1", features = ["full"] } +tokio-stream = "0.1.17" eyre = "0.6.12" url = "2.2.0" sha2 = { version = "0.10", default-features = false } # Reth deps reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-eth-wire = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-network = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } # Alloy libraries alloy-rpc-types-engine = "1.0.13" diff --git a/crates/flashblocks-p2p/.gitignore b/crates/flashblocks-p2p/.gitignore new file mode 100644 index 00000000..eb5a316c --- /dev/null +++ b/crates/flashblocks-p2p/.gitignore @@ -0,0 +1 @@ +target diff --git a/crates/flashblocks-p2p/Cargo.toml b/crates/flashblocks-p2p/Cargo.toml new file mode 100644 index 00000000..a8e2d95f --- /dev/null +++ b/crates/flashblocks-p2p/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "flashblocks-p2p" +version = "0.1.0" +edition = "2024" +license = "MIT" + +[dependencies] +reth.workspace = true +reth-ethereum = { workspace = true, features = ["node", "network", "cli"] } +reth-eth-wire = { workspace = true } +reth-network = { workspace = true } +# reth-node-builder.workspace = true +# reth-optimism-chainspec.workspace = true +# reth-optimism-node.workspace = true +# reth-optimism-primitives.workspace = true +# reth-optimism-payload-builder.workspace = true +# reth-optimism-rpc.workspace = true +# reth-optimism-forks.workspace = true +# reth-provider.workspace = true +# reth-trie-db.workspace = true +# reth-transaction-pool.workspace = true +# reth-node-api.workspace = true +# alloy-primitives.workspace = true +# op-alloy-consensus.workspace = true +# alloy-rpc-types-eth.workspace = true + +tokio = { workspace = true } +tokio-stream = { workspace = true } +eyre.workspace = true +futures = { workspace = true } +clap.workspace = true +tracing.workspace = true +alloy-primitives.workspace = true + +rollup-boost = { path = "../rollup-boost" } diff --git a/crates/flashblocks-p2p/src/bin.bak/main.rs.bak b/crates/flashblocks-p2p/src/bin.bak/main.rs.bak new file mode 100644 index 00000000..177432ca --- /dev/null +++ b/crates/flashblocks-p2p/src/bin.bak/main.rs.bak @@ -0,0 +1,52 @@ +#![allow(missing_docs, rustdoc::missing_crate_level_docs)] + +use clap::Parser; +use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay}; +use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; +use reth_optimism_node::{OpNode, args::RollupArgs}; +use tracing::info; + +#[derive(Debug, Clone, PartialEq, Eq, clap::Args)] +#[command(next_help_heading = "Rollup")] +struct FlashblocksRollupArgs { + #[command(flatten)] + rollup_args: RollupArgs, + + #[arg(long = "flashblocks.enabled", default_value = "false")] + flashblocks_enabled: bool, + + #[arg(long = "flashblocks.websocket-url", value_name = "WEBSOCKET_URL")] + websocket_url: url::Url, +} + +fn main() { + if let Err(err) = + Cli::::parse().run(async move |builder, args| { + let rollup_args = args.rollup_args; + let chain_spec = builder.config().chain.clone(); + + info!(target: "reth::cli", "Launching Flashblocks RPC overlay node"); + let handle = builder + .node(OpNode::new(rollup_args)) + .extend_rpc_modules(move |ctx| { + if args.flashblocks_enabled { + let mut flashblocks_overlay = + FlashblocksOverlay::new(args.websocket_url, chain_spec); + flashblocks_overlay.start()?; + + let eth_api = ctx.registry.eth_api().clone(); + let api_ext = FlashblocksApiExt::new(eth_api.clone(), flashblocks_overlay); + + ctx.modules.replace_configured(api_ext.into_rpc())?; + } + Ok(()) + }) + .launch_with_debug_capabilities() + .await?; + handle.node_exit_future.await + }) + { + tracing::error!("Error: {err:?}"); + std::process::exit(1); + } +} diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs new file mode 100644 index 00000000..f13735ba --- /dev/null +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -0,0 +1,56 @@ +use super::FlashblocksConnection; +use crate::protocol::{ + event::ProtocolEvent, handler::ProtocolState, proto::FlashblocksProtoMessage, +}; +use reth_ethereum::network::{ + api::{Direction, PeerId}, + eth_wire::{capability::SharedCapabilities, multiplex::ProtocolConnection, protocol::Protocol}, + protocol::{ConnectionHandler, OnNotSupported}, +}; +use tokio::sync::mpsc; +use tokio_stream::wrappers::UnboundedReceiverStream; + +/// The connection handler for the custom RLPx protocol. +pub(crate) struct FlashblocksConnectionHandler { + pub(crate) state: ProtocolState, +} + +impl ConnectionHandler for FlashblocksConnectionHandler { + type Connection = FlashblocksConnection; + + fn protocol(&self) -> Protocol { + FlashblocksProtoMessage::protocol() + } + + fn on_unsupported_by_peer( + self, + _supported: &SharedCapabilities, + _direction: Direction, + _peer_id: PeerId, + ) -> OnNotSupported { + OnNotSupported::KeepAlive + } + + fn into_connection( + self, + direction: Direction, + peer_id: PeerId, + conn: ProtocolConnection, + ) -> Self::Connection { + let (tx, rx) = mpsc::unbounded_channel(); + self.state + .events + .send(ProtocolEvent::Established { + direction, + peer_id, + to_connection: tx, + }) + .ok(); + FlashblocksConnection { + conn, + initial_ping: direction.is_outgoing().then(FlashblocksProtoMessage::ping), + commands: UnboundedReceiverStream::new(rx), + pending_pong: None, + } + } +} diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs new file mode 100644 index 00000000..a2692dc9 --- /dev/null +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -0,0 +1,78 @@ +use super::protocol::proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}; +use alloy_primitives::bytes::BytesMut; +use futures::{Stream, StreamExt}; +use reth_ethereum::network::eth_wire::multiplex::ProtocolConnection; +use rollup_boost::FlashblocksPayloadV1; +use std::{ + pin::Pin, + task::{Context, Poll, ready}, +}; +use tokio::sync::oneshot; +use tokio_stream::wrappers::UnboundedReceiverStream; + +pub(crate) mod handler; + +/// We define some custom commands that the subprotocol supports. +pub(crate) enum FlashblocksCommand { + /// Sends a message to the peer + NewFlashBlock { + msg: String, + /// The response will be sent to this channel. + response: oneshot::Sender, + }, +} + +pub(crate) struct FlashblocksConnection { + conn: ProtocolConnection, + initial_ping: Option, + commands: UnboundedReceiverStream, + pending_pong: Option>, +} + +impl Stream for FlashblocksConnection { + type Item = BytesMut; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.get_mut(); + if let Some(initial_ping) = this.initial_ping.take() { + return Poll::Ready(Some(initial_ping.encoded())); + } + + loop { + if let Poll::Ready(Some(cmd)) = this.commands.poll_next_unpin(cx) { + return match cmd { + FlashblocksCommand::NewFlashBlock { msg, response } => { + this.pending_pong = Some(response); + Poll::Ready(Some(FlashblocksProtoMessage::ping_message(msg).encoded())) + } + }; + } + + let Some(msg) = ready!(this.conn.poll_next_unpin(cx)) else { + return Poll::Ready(None); + }; + + let Some(msg) = FlashblocksProtoMessage::decode_message(&mut &msg[..]) else { + return Poll::Ready(None); + }; + + match msg.message { + FlashblocksProtoMessageKind::Ping => { + return Poll::Ready(Some(FlashblocksProtoMessage::pong().encoded())); + } + FlashblocksProtoMessageKind::Pong => {} + FlashblocksProtoMessageKind::PingMessage(msg) => { + return Poll::Ready(Some(FlashblocksProtoMessage::pong_message(msg).encoded())); + } + FlashblocksProtoMessageKind::PongMessage(msg) => { + if let Some(sender) = this.pending_pong.take() { + sender.send(msg).ok(); + } + continue; + } + } + + return Poll::Pending; + } + } +} diff --git a/crates/flashblocks-p2p/src/lib.rs b/crates/flashblocks-p2p/src/lib.rs new file mode 100644 index 00000000..6373fd3c --- /dev/null +++ b/crates/flashblocks-p2p/src/lib.rs @@ -0,0 +1,2 @@ +pub mod connection; +pub mod protocol; diff --git a/crates/flashblocks-p2p/src/lib.rs.bak b/crates/flashblocks-p2p/src/lib.rs.bak new file mode 100644 index 00000000..2a13bcbe --- /dev/null +++ b/crates/flashblocks-p2p/src/lib.rs.bak @@ -0,0 +1,187 @@ +use reth::{ + chainspec::{EthChainSpec as _, Hardforks}, + network::{ + types::BasicNetworkPrimitives, NetworkConfig, NetworkHandle, NetworkManager, + NetworkPrimitives, + }, +}; +use reth_eth_wire::{ + capability::SharedCapabilities, multiplex::ProtocolConnection, protocol::Protocol, +}; +/// OpNetworkBuilder impls NetworkBuilder -> NetworkHandle impls NetworkProtocols +/// +/// NetworkConfig has `extra_protocols` +/// +// #![cfg_attr(not(any(test, feature = "test")), warn(unused_crate_dependencies))] +// +// pub mod args; +// pub mod node; +// +// #[cfg(any(feature = "test", test))] +// pub mod test_utils; +use reth_network::protocol::{ConnectionHandler, IntoRlpxSubProtocol, ProtocolHandler}; +use reth_node_api::{PrimitivesTy, TxTy}; +use reth_node_builder::{ + components::{ + BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder, + NetworkBuilder, PayloadBuilderBuilder, PoolBuilder, PoolBuilderConfigOverrides, + TxPoolBuilder, + }, + node::{FullNodeTypes, NodeTypes}, + rpc::{ + EngineApiBuilder, EngineValidatorAddOn, EngineValidatorBuilder, EthApiBuilder, Identity, + RethRpcAddOns, RethRpcMiddleware, RethRpcServerHandles, RpcAddOns, RpcContext, RpcHandle, + }, + BuilderContext, DebugNode, Node, NodeAdapter, NodeComponentsBuilder, +}; +use reth_provider::{ + BlockReaderIdExt, ChainSpecProvider, ExecutionOutcome, ProviderError, StateProvider, + StateProviderFactory, +}; +use reth_transaction_pool::{ + blobstore::DiskFileBlobStore, EthPoolTransaction, PeerId, PoolPooledTx, PoolTransaction, + TransactionPool, TransactionValidationTaskExecutor, +}; +use tokio::sync::oneshot; +use tracing::info; + +#[derive(Clone, Debug, Default)] +pub struct FlashblocksProtocolHandler; + +#[derive(Clone, Debug, Default)] +pub struct FlashblocksConnectionHandler; + +/// The connection handler for the custom RLPx protocol. +pub struct FlashblocksConnection { + conn: ProtocolConnection, + initial_ping: Option, + commands: UnboundedReceiverStream, + pending_pong: Option>, +} + +impl ConnectionHandler for FlashblocksConnectionHandler { + type Connection; + + fn protocol(&self) -> Protocol { + todo!() + } + + fn on_unsupported_by_peer( + self, + supported: &SharedCapabilities, + direction: reth_network::Direction, + peer_id: PeerId, + ) -> reth_network::protocol::OnNotSupported { + todo!() + } + + fn into_connection( + self, + direction: reth_network::Direction, + peer_id: PeerId, + conn: ProtocolConnection, + ) -> Self::Connection { + todo!() + } +} + +impl ProtocolHandler for FlashblocksProtocolHandler { + type ConnectionHandler = FlashblocksConnectionHandler; + + fn on_incoming(&self, socket_addr: std::net::SocketAddr) -> Option { + todo!() + } + + fn on_outgoing( + &self, + socket_addr: std::net::SocketAddr, + peer_id: PeerId, + ) -> Option { + todo!() + } +} + +#[derive(Clone, Debug, Default)] +pub struct FlashblocksNetworkBuilder { + /// Disable transaction pool gossip + pub disable_txpool_gossip: bool, + /// Disable discovery v4 + pub disable_discovery_v4: bool, +} + +impl FlashblocksNetworkBuilder { + /// Returns the [`NetworkConfig`] that contains the settings to launch the p2p network. + /// + /// This applies the configured [`OpNetworkBuilder`] settings. + pub fn network_config( + &self, + ctx: &BuilderContext, + ) -> eyre::Result> + where + Node: FullNodeTypes>, + NetworkP: NetworkPrimitives, + { + let Self { + disable_txpool_gossip, + disable_discovery_v4, + .. + } = self.clone(); + let args = &ctx.config().network; + let network_builder = ctx + .network_config_builder()? + // apply discovery settings + .apply(|mut builder| { + let rlpx_socket = (args.addr, args.port).into(); + if disable_discovery_v4 || args.discovery.disable_discovery { + builder = builder.disable_discv4_discovery(); + } + if !args.discovery.disable_discovery { + builder = builder.discovery_v5( + args.discovery.discovery_v5_builder( + rlpx_socket, + ctx.config() + .network + .resolved_bootnodes() + .or_else(|| ctx.chain_spec().bootnodes()) + .unwrap_or_default(), + ), + ); + } + + builder + }); + + let mut network_config = ctx.build_network_config(network_builder); + + // When `sequencer_endpoint` is configured, the node will forward all transactions to a + // Sequencer node for execution and inclusion on L1, and disable its own txpool + // gossip to prevent other parties in the network from learning about them. + network_config.tx_gossip_disabled = disable_txpool_gossip; + + Ok(network_config) + } +} + +impl NetworkBuilder for FlashblocksNetworkBuilder +where + Node: FullNodeTypes>, + Pool: TransactionPool>> + + Unpin + + 'static, +{ + type Network = + NetworkHandle, PoolPooledTx>>; + + async fn build_network( + self, + ctx: &BuilderContext, + pool: Pool, + ) -> eyre::Result { + let network_config = self.network_config(ctx)?; + let network = NetworkManager::builder(network_config).await?; + let handle = ctx.start_network(network, pool); + // info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized"); + + Ok(handle) + } +} diff --git a/crates/flashblocks-p2p/src/protocol/event.rs b/crates/flashblocks-p2p/src/protocol/event.rs new file mode 100644 index 00000000..263e87c0 --- /dev/null +++ b/crates/flashblocks-p2p/src/protocol/event.rs @@ -0,0 +1,14 @@ +use crate::connection::FlashblocksCommand; +use reth_ethereum::network::{Direction, api::PeerId}; +use tokio::sync::mpsc; + +/// The events that can be emitted by our custom protocol. +#[derive(Debug)] +pub(crate) enum ProtocolEvent { + Established { + #[expect(dead_code)] + direction: Direction, + peer_id: PeerId, + to_connection: mpsc::UnboundedSender, + }, +} diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs new file mode 100644 index 00000000..73bfd4f1 --- /dev/null +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -0,0 +1,37 @@ +use super::event::ProtocolEvent; +use crate::connection::handler::FlashblocksConnectionHandler; +use reth_ethereum::network::{api::PeerId, protocol::ProtocolHandler}; +use std::net::SocketAddr; +use tokio::sync::mpsc; + +/// Protocol state is an helper struct to store the protocol events. +#[derive(Clone, Debug)] +pub(crate) struct ProtocolState { + pub(crate) events: mpsc::UnboundedSender, +} + +/// The protocol handler takes care of incoming and outgoing connections. +#[derive(Debug)] +pub(crate) struct FlashblocksProtoHandler { + pub state: ProtocolState, +} + +impl ProtocolHandler for FlashblocksProtoHandler { + type ConnectionHandler = FlashblocksConnectionHandler; + + fn on_incoming(&self, _socket_addr: SocketAddr) -> Option { + Some(FlashblocksConnectionHandler { + state: self.state.clone(), + }) + } + + fn on_outgoing( + &self, + _socket_addr: SocketAddr, + _peer_id: PeerId, + ) -> Option { + Some(FlashblocksConnectionHandler { + state: self.state.clone(), + }) + } +} diff --git a/crates/flashblocks-p2p/src/protocol/mod.rs b/crates/flashblocks-p2p/src/protocol/mod.rs new file mode 100644 index 00000000..8aba9a4e --- /dev/null +++ b/crates/flashblocks-p2p/src/protocol/mod.rs @@ -0,0 +1,3 @@ +pub(crate) mod event; +pub(crate) mod handler; +pub(crate) mod proto; diff --git a/crates/flashblocks-p2p/src/protocol/proto.rs b/crates/flashblocks-p2p/src/protocol/proto.rs new file mode 100644 index 00000000..b813612b --- /dev/null +++ b/crates/flashblocks-p2p/src/protocol/proto.rs @@ -0,0 +1,116 @@ +//! Simple RLPx Ping Pong protocol that also support sending messages, +//! following [RLPx specs](https://github.com/ethereum/devp2p/blob/master/rlpx.md) + +use alloy_primitives::bytes::{Buf, BufMut, BytesMut}; +use reth_ethereum::network::eth_wire::{Capability, protocol::Protocol}; + +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub(crate) enum FlashblocksProtoMessageId { + Ping = 0x00, + Pong = 0x01, + PingMessage = 0x02, + PongMessage = 0x03, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) enum FlashblocksProtoMessageKind { + Ping, + Pong, + PingMessage(String), + PongMessage(String), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) struct FlashblocksProtoMessage { + pub message_type: FlashblocksProtoMessageId, + pub message: FlashblocksProtoMessageKind, +} + +impl FlashblocksProtoMessage { + /// Returns the capability for the `custom_rlpx` protocol. + pub fn capability() -> Capability { + Capability::new_static("flashblocks", 1) + } + + /// Returns the protocol for the `custom_rlpx` protocol. + pub fn protocol() -> Protocol { + Protocol::new(Self::capability(), 4) + } + + /// Creates a ping message + pub fn ping_message(msg: impl Into) -> Self { + Self { + message_type: FlashblocksProtoMessageId::PingMessage, + message: FlashblocksProtoMessageKind::PingMessage(msg.into()), + } + } + /// Creates a pong message + pub fn pong_message(msg: impl Into) -> Self { + Self { + message_type: FlashblocksProtoMessageId::PongMessage, + message: FlashblocksProtoMessageKind::PongMessage(msg.into()), + } + } + + /// Creates a ping message + pub fn ping() -> Self { + Self { + message_type: FlashblocksProtoMessageId::Ping, + message: FlashblocksProtoMessageKind::Ping, + } + } + + /// Creates a pong message + pub fn pong() -> Self { + Self { + message_type: FlashblocksProtoMessageId::Pong, + message: FlashblocksProtoMessageKind::Pong, + } + } + + /// Creates a new `FlashblocksProtoMessage` with the given message ID and payload. + pub fn encoded(&self) -> BytesMut { + let mut buf = BytesMut::new(); + buf.put_u8(self.message_type as u8); + match &self.message { + FlashblocksProtoMessageKind::Ping | FlashblocksProtoMessageKind::Pong => {} + FlashblocksProtoMessageKind::PingMessage(msg) + | FlashblocksProtoMessageKind::PongMessage(msg) => { + buf.put(msg.as_bytes()); + } + } + buf + } + + /// Decodes a `FlashblocksProtoMessage` from the given message buffer. + pub fn decode_message(buf: &mut &[u8]) -> Option { + if buf.is_empty() { + return None; + } + let id = buf[0]; + buf.advance(1); + let message_type = match id { + 0x00 => FlashblocksProtoMessageId::Ping, + 0x01 => FlashblocksProtoMessageId::Pong, + 0x02 => FlashblocksProtoMessageId::PingMessage, + 0x03 => FlashblocksProtoMessageId::PongMessage, + _ => return None, + }; + let message = match message_type { + FlashblocksProtoMessageId::Ping => FlashblocksProtoMessageKind::Ping, + FlashblocksProtoMessageId::Pong => FlashblocksProtoMessageKind::Pong, + FlashblocksProtoMessageId::PingMessage => FlashblocksProtoMessageKind::PingMessage( + String::from_utf8_lossy(&buf[..]).into_owned(), + ), + FlashblocksProtoMessageId::PongMessage => FlashblocksProtoMessageKind::PongMessage( + String::from_utf8_lossy(&buf[..]).into_owned(), + ), + }; + + Some(Self { + message_type, + message, + }) + } +} From 14c42976a861fa993c130f7ae243c2af2021fbec Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 9 Jul 2025 14:41:48 -0700 Subject: [PATCH 02/45] wip --- Cargo.lock | 44 +++++++++- crates/flashblocks-p2p/Cargo.toml | 9 ++ .../flashblocks-p2p/src/connection/handler.rs | 4 +- crates/flashblocks-p2p/src/connection/mod.rs | 38 +++------ crates/flashblocks-p2p/src/protocol/auth.rs | 73 ++++++++++++++++ crates/flashblocks-p2p/src/protocol/error.rs | 9 ++ crates/flashblocks-p2p/src/protocol/mod.rs | 8 +- crates/flashblocks-p2p/src/protocol/proto.rs | 83 +++++++------------ .../src/flashblocks/primitives.rs | 6 +- 9 files changed, 182 insertions(+), 92 deletions(-) create mode 100644 crates/flashblocks-p2p/src/protocol/auth.rs create mode 100644 crates/flashblocks-p2p/src/protocol/error.rs diff --git a/Cargo.lock b/Cargo.lock index 847c705d..d2da3f59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1652,6 +1652,26 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", +] + [[package]] name = "bindgen" version = "0.69.5" @@ -3094,6 +3114,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" dependencies = [ "pkcs8", + "serde", "signature", ] @@ -3433,14 +3454,21 @@ name = "flashblocks-p2p" version = "0.1.0" dependencies = [ "alloy-primitives", + "bincode 2.0.1", + "blake3", "clap", + "ed25519-dalek", "eyre", "futures", + "rand_core 0.6.4", "reth", "reth-eth-wire", "reth-ethereum", "reth-network", "rollup-boost", + "serde", + "serde_json", + "thiserror 2.0.12", "tokio", "tokio-stream", "tracing", @@ -8667,7 +8695,7 @@ version = "1.5.0" source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" dependencies = [ "anyhow", - "bincode", + "bincode 1.3.3", "derive_more", "lz4_flex", "memmap2", @@ -9862,7 +9890,7 @@ dependencies = [ "alloy-consensus", "alloy-eips", "alloy-primitives", - "bincode", + "bincode 1.3.3", "blake3", "eyre", "futures-util", @@ -12433,6 +12461,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "url" version = "2.5.4" @@ -12539,6 +12573,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + [[package]] name = "visibility" version = "0.1.1" diff --git a/crates/flashblocks-p2p/Cargo.toml b/crates/flashblocks-p2p/Cargo.toml index a8e2d95f..65ca4ea7 100644 --- a/crates/flashblocks-p2p/Cargo.toml +++ b/crates/flashblocks-p2p/Cargo.toml @@ -24,6 +24,12 @@ reth-network = { workspace = true } # op-alloy-consensus.workspace = true # alloy-rpc-types-eth.workspace = true +ed25519-dalek = { version = "2", features = ["serde"] } +rand_core = "0.6" # for secure RNG +blake3 = "1" # fast hashing for payload IDs +serde = { version = "1", features = ["derive"] } +bincode = "2" # stable, deterministic encoding + tokio = { workspace = true } tokio-stream = { workspace = true } eyre.workspace = true @@ -31,5 +37,8 @@ futures = { workspace = true } clap.workspace = true tracing.workspace = true alloy-primitives.workspace = true +serde_json = "1.0" +thiserror = "2" + rollup-boost = { path = "../rollup-boost" } diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs index f13735ba..5f91bdff 100644 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -10,7 +10,7 @@ use reth_ethereum::network::{ use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; -/// The connection handler for the custom RLPx protocol. +/// The connection handler for the flashblocks RLPx protocol. pub(crate) struct FlashblocksConnectionHandler { pub(crate) state: ProtocolState, } @@ -48,9 +48,7 @@ impl ConnectionHandler for FlashblocksConnectionHandler { .ok(); FlashblocksConnection { conn, - initial_ping: direction.is_outgoing().then(FlashblocksProtoMessage::ping), commands: UnboundedReceiverStream::new(rx), - pending_pong: None, } } } diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index a2692dc9..6f0f3fd7 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -1,3 +1,5 @@ +use crate::protocol::auth::Authorized; + use super::protocol::proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}; use alloy_primitives::bytes::BytesMut; use futures::{Stream, StreamExt}; @@ -7,26 +9,22 @@ use std::{ pin::Pin, task::{Context, Poll, ready}, }; -use tokio::sync::oneshot; +use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; pub(crate) mod handler; /// We define some custom commands that the subprotocol supports. pub(crate) enum FlashblocksCommand { - /// Sends a message to the peer - NewFlashBlock { - msg: String, - /// The response will be sent to this channel. - response: oneshot::Sender, + /// Sends a flashblocks payload to the peer + FlashblocksPayloadV1 { + payload: Authorized, }, } pub(crate) struct FlashblocksConnection { conn: ProtocolConnection, - initial_ping: Option, commands: UnboundedReceiverStream, - pending_pong: Option>, } impl Stream for FlashblocksConnection { @@ -34,17 +32,13 @@ impl Stream for FlashblocksConnection { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); - if let Some(initial_ping) = this.initial_ping.take() { - return Poll::Ready(Some(initial_ping.encoded())); - } loop { if let Poll::Ready(Some(cmd)) = this.commands.poll_next_unpin(cx) { return match cmd { - FlashblocksCommand::NewFlashBlock { msg, response } => { - this.pending_pong = Some(response); - Poll::Ready(Some(FlashblocksProtoMessage::ping_message(msg).encoded())) - } + FlashblocksCommand::FlashblocksPayloadV1 { payload } => Poll::Ready(Some( + FlashblocksProtoMessage::flashblocks_payload(payload).encoded(), + )), }; } @@ -57,17 +51,9 @@ impl Stream for FlashblocksConnection { }; match msg.message { - FlashblocksProtoMessageKind::Ping => { - return Poll::Ready(Some(FlashblocksProtoMessage::pong().encoded())); - } - FlashblocksProtoMessageKind::Pong => {} - FlashblocksProtoMessageKind::PingMessage(msg) => { - return Poll::Ready(Some(FlashblocksProtoMessage::pong_message(msg).encoded())); - } - FlashblocksProtoMessageKind::PongMessage(msg) => { - if let Some(sender) = this.pending_pong.take() { - sender.send(msg).ok(); - } + FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload) => { + // Process the received payload (could emit an event here) + // For now, we just continue to the next message continue; } } diff --git a/crates/flashblocks-p2p/src/protocol/auth.rs b/crates/flashblocks-p2p/src/protocol/auth.rs new file mode 100644 index 00000000..bb54c568 --- /dev/null +++ b/crates/flashblocks-p2p/src/protocol/auth.rs @@ -0,0 +1,73 @@ +use blake3::hash as blake3_hash; +use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; +use reth::payload::PayloadId; +use serde::{Deserialize, Serialize}; +use serde_json; + +use crate::protocol::error::FlashblocksP2PError; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Authorized { + payload: T, + authorization: Authorization, + builder_sig: Signature, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Authorization { + payload_id: PayloadId, + builder_pub: VerifyingKey, + authorizer_sig: Signature, +} + +impl Authorization { + pub fn new( + authorizer_sk: &SigningKey, + builder_pub: VerifyingKey, + payload_id: PayloadId, + ) -> Self { + let mut msg = payload_id.0.to_vec(); + msg.extend_from_slice(builder_pub.as_bytes()); + let hash = blake3_hash(&msg); + let sig = authorizer_sk.sign(hash.as_bytes()); + + Self { + payload_id, + builder_pub, + authorizer_sig: sig, + } + } + + pub fn verify(&self, authorizer_pub: VerifyingKey) -> Result<(), FlashblocksP2PError> { + let mut msg = self.payload_id.0.to_vec(); + msg.extend_from_slice(self.builder_pub.as_bytes()); + let hash = blake3_hash(&msg); + authorizer_pub + .verify(hash.as_bytes(), &self.authorizer_sig) + .map_err(|_| FlashblocksP2PError::InvalidAuthorizerSig) + } +} + +impl Authorized { + pub fn new(builder_sk: &SigningKey, authorization: Authorization, payload: T) -> Self { + let hash = blake3_hash(&serde_json::to_vec(&payload).unwrap()); + let builder_sig = builder_sk.sign(hash.as_bytes()); + + Self { + payload, + authorization, + builder_sig, + } + } + + pub fn verify(&self, authorizer_pub: VerifyingKey) -> Result<(), FlashblocksP2PError> { + self.authorization.verify(authorizer_pub)?; + + let hash = blake3_hash(&serde_json::to_vec(&self.payload).unwrap()); + + self.authorization + .builder_pub + .verify(hash.as_bytes(), &self.builder_sig) + .map_err(|_| FlashblocksP2PError::InvalidBuilderSig) + } +} diff --git a/crates/flashblocks-p2p/src/protocol/error.rs b/crates/flashblocks-p2p/src/protocol/error.rs new file mode 100644 index 00000000..e7f86527 --- /dev/null +++ b/crates/flashblocks-p2p/src/protocol/error.rs @@ -0,0 +1,9 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum FlashblocksP2PError { + #[error("invalid authorizer signature")] + InvalidAuthorizerSig, + #[error("invalid builder signature")] + InvalidBuilderSig, +} diff --git a/crates/flashblocks-p2p/src/protocol/mod.rs b/crates/flashblocks-p2p/src/protocol/mod.rs index 8aba9a4e..dbdcd376 100644 --- a/crates/flashblocks-p2p/src/protocol/mod.rs +++ b/crates/flashblocks-p2p/src/protocol/mod.rs @@ -1,3 +1,5 @@ -pub(crate) mod event; -pub(crate) mod handler; -pub(crate) mod proto; +pub mod auth; +pub mod error; +pub mod event; +pub mod handler; +pub mod proto; diff --git a/crates/flashblocks-p2p/src/protocol/proto.rs b/crates/flashblocks-p2p/src/protocol/proto.rs index b813612b..21db1df9 100644 --- a/crates/flashblocks-p2p/src/protocol/proto.rs +++ b/crates/flashblocks-p2p/src/protocol/proto.rs @@ -1,24 +1,22 @@ -//! Simple RLPx Ping Pong protocol that also support sending messages, +//! Simple RLPx Flashblocks protocol for propagating FlashblocksPayloadV1 messages //! following [RLPx specs](https://github.com/ethereum/devp2p/blob/master/rlpx.md) use alloy_primitives::bytes::{Buf, BufMut, BytesMut}; use reth_ethereum::network::eth_wire::{Capability, protocol::Protocol}; +use rollup_boost::FlashblocksPayloadV1; +use serde_json; + +use crate::protocol::auth::Authorized; #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub(crate) enum FlashblocksProtoMessageId { - Ping = 0x00, - Pong = 0x01, - PingMessage = 0x02, - PongMessage = 0x03, + FlashblocksPayloadV1 = 0x00, } #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum FlashblocksProtoMessageKind { - Ping, - Pong, - PingMessage(String), - PongMessage(String), + FlashblocksPayloadV1(Authorized), } #[derive(Clone, Debug, PartialEq, Eq)] @@ -28,44 +26,21 @@ pub(crate) struct FlashblocksProtoMessage { } impl FlashblocksProtoMessage { - /// Returns the capability for the `custom_rlpx` protocol. + /// Returns the capability for the `flashblocks` protocol. pub fn capability() -> Capability { Capability::new_static("flashblocks", 1) } - /// Returns the protocol for the `custom_rlpx` protocol. + /// Returns the protocol for the `flashblocks` protocol. pub fn protocol() -> Protocol { - Protocol::new(Self::capability(), 4) - } - - /// Creates a ping message - pub fn ping_message(msg: impl Into) -> Self { - Self { - message_type: FlashblocksProtoMessageId::PingMessage, - message: FlashblocksProtoMessageKind::PingMessage(msg.into()), - } - } - /// Creates a pong message - pub fn pong_message(msg: impl Into) -> Self { - Self { - message_type: FlashblocksProtoMessageId::PongMessage, - message: FlashblocksProtoMessageKind::PongMessage(msg.into()), - } + Protocol::new(Self::capability(), 1) } - /// Creates a ping message - pub fn ping() -> Self { + /// Creates a flashblocks payload message + pub fn flashblocks_payload(payload: Authorized) -> Self { Self { - message_type: FlashblocksProtoMessageId::Ping, - message: FlashblocksProtoMessageKind::Ping, - } - } - - /// Creates a pong message - pub fn pong() -> Self { - Self { - message_type: FlashblocksProtoMessageId::Pong, - message: FlashblocksProtoMessageKind::Pong, + message_type: FlashblocksProtoMessageId::FlashblocksPayloadV1, + message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload), } } @@ -74,10 +49,10 @@ impl FlashblocksProtoMessage { let mut buf = BytesMut::new(); buf.put_u8(self.message_type as u8); match &self.message { - FlashblocksProtoMessageKind::Ping | FlashblocksProtoMessageKind::Pong => {} - FlashblocksProtoMessageKind::PingMessage(msg) - | FlashblocksProtoMessageKind::PongMessage(msg) => { - buf.put(msg.as_bytes()); + FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload) => { + // Serialize the payload as JSON for transmission + let json = serde_json::to_string(payload).unwrap_or_default(); + buf.put(json.as_bytes()); } } buf @@ -91,21 +66,19 @@ impl FlashblocksProtoMessage { let id = buf[0]; buf.advance(1); let message_type = match id { - 0x00 => FlashblocksProtoMessageId::Ping, - 0x01 => FlashblocksProtoMessageId::Pong, - 0x02 => FlashblocksProtoMessageId::PingMessage, - 0x03 => FlashblocksProtoMessageId::PongMessage, + 0x00 => FlashblocksProtoMessageId::FlashblocksPayloadV1, _ => return None, }; + let message = match message_type { - FlashblocksProtoMessageId::Ping => FlashblocksProtoMessageKind::Ping, - FlashblocksProtoMessageId::Pong => FlashblocksProtoMessageKind::Pong, - FlashblocksProtoMessageId::PingMessage => FlashblocksProtoMessageKind::PingMessage( - String::from_utf8_lossy(&buf[..]).into_owned(), - ), - FlashblocksProtoMessageId::PongMessage => FlashblocksProtoMessageKind::PongMessage( - String::from_utf8_lossy(&buf[..]).into_owned(), - ), + FlashblocksProtoMessageId::FlashblocksPayloadV1 => { + // Deserialize the JSON payload + let json_str = String::from_utf8_lossy(&buf[..]); + match serde_json::from_str::>(&json_str) { + Ok(payload) => FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload), + Err(_) => return None, + } + } }; Some(Self { diff --git a/crates/rollup-boost/src/flashblocks/primitives.rs b/crates/rollup-boost/src/flashblocks/primitives.rs index f6089a3a..c9263ad0 100644 --- a/crates/rollup-boost/src/flashblocks/primitives.rs +++ b/crates/rollup-boost/src/flashblocks/primitives.rs @@ -9,7 +9,7 @@ use serde_json::Value; /// such as state root, receipts, logs, and new transactions. Other immutable block fields /// like parent hash and block number are excluded since they remain constant throughout /// the block's construction. -#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize, Eq)] pub struct ExecutionPayloadFlashblockDeltaV1 { /// The state root of the block. pub state_root: B256, @@ -34,7 +34,7 @@ pub struct ExecutionPayloadFlashblockDeltaV1 { /// throughout block construction. This includes fundamental block properties like /// parent hash, block number, and other header fields that are determined at /// block creation and cannot be modified. -#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize, Eq)] pub struct ExecutionPayloadBaseV1 { /// Ecotone parent beacon block root pub parent_beacon_block_root: B256, @@ -59,7 +59,7 @@ pub struct ExecutionPayloadBaseV1 { pub base_fee_per_gas: U256, } -#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize, Eq)] pub struct FlashblocksPayloadV1 { /// The payload id of the flashblock pub payload_id: PayloadId, From 54af8b1a61bbbbca7c7204686e22d8970ba25ed4 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 9 Jul 2025 15:04:45 -0700 Subject: [PATCH 03/45] wip --- crates/flashblocks-p2p/src/connection/handler.rs | 7 ++++--- crates/flashblocks-p2p/src/connection/mod.rs | 12 ++++++------ crates/flashblocks-p2p/src/protocol/event.rs | 6 ++++-- crates/flashblocks-p2p/src/protocol/handler.rs | 8 ++++---- 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs index 5f91bdff..3da25d89 100644 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -1,6 +1,6 @@ use super::FlashblocksConnection; use crate::protocol::{ - event::ProtocolEvent, handler::ProtocolState, proto::FlashblocksProtoMessage, + event::FlashblocksP2PEvent, handler::FlashblocksP2PState, proto::FlashblocksProtoMessage, }; use reth_ethereum::network::{ api::{Direction, PeerId}, @@ -12,7 +12,7 @@ use tokio_stream::wrappers::UnboundedReceiverStream; /// The connection handler for the flashblocks RLPx protocol. pub(crate) struct FlashblocksConnectionHandler { - pub(crate) state: ProtocolState, + pub(crate) state: FlashblocksP2PState, } impl ConnectionHandler for FlashblocksConnectionHandler { @@ -40,7 +40,7 @@ impl ConnectionHandler for FlashblocksConnectionHandler { let (tx, rx) = mpsc::unbounded_channel(); self.state .events - .send(ProtocolEvent::Established { + .send(FlashblocksP2PEvent::Established { direction, peer_id, to_connection: tx, @@ -49,6 +49,7 @@ impl ConnectionHandler for FlashblocksConnectionHandler { FlashblocksConnection { conn, commands: UnboundedReceiverStream::new(rx), + state: self.state, } } } diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index 6f0f3fd7..3d54ab02 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -1,4 +1,4 @@ -use crate::protocol::auth::Authorized; +use crate::protocol::{auth::Authorized, event::FlashblocksP2PEvent, handler::FlashblocksP2PState}; use super::protocol::proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}; use alloy_primitives::bytes::BytesMut; @@ -9,7 +9,6 @@ use std::{ pin::Pin, task::{Context, Poll, ready}, }; -use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; pub(crate) mod handler; @@ -25,6 +24,7 @@ pub(crate) enum FlashblocksCommand { pub(crate) struct FlashblocksConnection { conn: ProtocolConnection, commands: UnboundedReceiverStream, + state: FlashblocksP2PState, } impl Stream for FlashblocksConnection { @@ -52,13 +52,13 @@ impl Stream for FlashblocksConnection { match msg.message { FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload) => { - // Process the received payload (could emit an event here) - // For now, we just continue to the next message + this.state + .events + .send(FlashblocksP2PEvent::FlashblocksPayloadV1(payload)) + .ok(); continue; } } - - return Poll::Pending; } } } diff --git a/crates/flashblocks-p2p/src/protocol/event.rs b/crates/flashblocks-p2p/src/protocol/event.rs index 263e87c0..c6002786 100644 --- a/crates/flashblocks-p2p/src/protocol/event.rs +++ b/crates/flashblocks-p2p/src/protocol/event.rs @@ -1,14 +1,16 @@ -use crate::connection::FlashblocksCommand; +use crate::{connection::FlashblocksCommand, protocol::auth::Authorized}; use reth_ethereum::network::{Direction, api::PeerId}; +use rollup_boost::FlashblocksPayloadV1; use tokio::sync::mpsc; /// The events that can be emitted by our custom protocol. #[derive(Debug)] -pub(crate) enum ProtocolEvent { +pub(crate) enum FlashblocksP2PEvent { Established { #[expect(dead_code)] direction: Direction, peer_id: PeerId, to_connection: mpsc::UnboundedSender, }, + FlashblocksPayloadV1(Authorized), } diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 73bfd4f1..b81af9c0 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -1,4 +1,4 @@ -use super::event::ProtocolEvent; +use super::event::FlashblocksP2PEvent; use crate::connection::handler::FlashblocksConnectionHandler; use reth_ethereum::network::{api::PeerId, protocol::ProtocolHandler}; use std::net::SocketAddr; @@ -6,14 +6,14 @@ use tokio::sync::mpsc; /// Protocol state is an helper struct to store the protocol events. #[derive(Clone, Debug)] -pub(crate) struct ProtocolState { - pub(crate) events: mpsc::UnboundedSender, +pub(crate) struct FlashblocksP2PState { + pub(crate) events: mpsc::UnboundedSender, } /// The protocol handler takes care of incoming and outgoing connections. #[derive(Debug)] pub(crate) struct FlashblocksProtoHandler { - pub state: ProtocolState, + pub state: FlashblocksP2PState, } impl ProtocolHandler for FlashblocksProtoHandler { From 9966ecbd57c8060ef0be35d33d2f387205e06bb3 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 9 Jul 2025 16:47:44 -0700 Subject: [PATCH 04/45] wip --- Cargo.lock | 3 + .../src/bin.bak/{main.rs.bak => main.rs} | 0 crates/flashblocks-p2p/src/protocol/auth.rs | 45 +------- crates/flashblocks-p2p/src/protocol/mod.rs | 1 - crates/rollup-boost/Cargo.toml | 4 + crates/rollup-boost/src/cli.rs | 101 ++++++++++-------- crates/rollup-boost/src/client/rpc.rs | 27 ++++- crates/rollup-boost/src/flashblocks/args.rs | 24 ++++- .../src/flashblocks}/error.rs | 0 crates/rollup-boost/src/flashblocks/mod.rs | 3 + .../rollup-boost/src/flashblocks/service.rs | 4 + crates/rollup-boost/src/health.rs | 10 ++ crates/rollup-boost/src/server.rs | 66 ++++++++++-- crates/rollup-boost/src/tests/common/mod.rs | 1 + 14 files changed, 188 insertions(+), 101 deletions(-) rename crates/flashblocks-p2p/src/bin.bak/{main.rs.bak => main.rs} (100%) rename crates/{flashblocks-p2p/src/protocol => rollup-boost/src/flashblocks}/error.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index d2da3f59..f7d9e5b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10577,12 +10577,15 @@ dependencies = [ "alloy-serde", "anyhow", "assert_cmd", + "blake3", "bytes", "clap", "ctor", "dotenvy", + "ed25519-dalek", "eyre", "futures", + "hex", "http", "http-body-util", "hyper", diff --git a/crates/flashblocks-p2p/src/bin.bak/main.rs.bak b/crates/flashblocks-p2p/src/bin.bak/main.rs similarity index 100% rename from crates/flashblocks-p2p/src/bin.bak/main.rs.bak rename to crates/flashblocks-p2p/src/bin.bak/main.rs diff --git a/crates/flashblocks-p2p/src/protocol/auth.rs b/crates/flashblocks-p2p/src/protocol/auth.rs index bb54c568..0afd9647 100644 --- a/crates/flashblocks-p2p/src/protocol/auth.rs +++ b/crates/flashblocks-p2p/src/protocol/auth.rs @@ -1,51 +1,14 @@ use blake3::hash as blake3_hash; use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; -use reth::payload::PayloadId; +use rollup_boost::{Authorization, FlashblocksP2PError}; use serde::{Deserialize, Serialize}; use serde_json; -use crate::protocol::error::FlashblocksP2PError; - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Authorized { - payload: T, - authorization: Authorization, - builder_sig: Signature, -} - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Authorization { - payload_id: PayloadId, - builder_pub: VerifyingKey, - authorizer_sig: Signature, -} - -impl Authorization { - pub fn new( - authorizer_sk: &SigningKey, - builder_pub: VerifyingKey, - payload_id: PayloadId, - ) -> Self { - let mut msg = payload_id.0.to_vec(); - msg.extend_from_slice(builder_pub.as_bytes()); - let hash = blake3_hash(&msg); - let sig = authorizer_sk.sign(hash.as_bytes()); - - Self { - payload_id, - builder_pub, - authorizer_sig: sig, - } - } - - pub fn verify(&self, authorizer_pub: VerifyingKey) -> Result<(), FlashblocksP2PError> { - let mut msg = self.payload_id.0.to_vec(); - msg.extend_from_slice(self.builder_pub.as_bytes()); - let hash = blake3_hash(&msg); - authorizer_pub - .verify(hash.as_bytes(), &self.authorizer_sig) - .map_err(|_| FlashblocksP2PError::InvalidAuthorizerSig) - } + pub payload: T, + pub authorization: Authorization, + pub builder_sig: Signature, } impl Authorized { diff --git a/crates/flashblocks-p2p/src/protocol/mod.rs b/crates/flashblocks-p2p/src/protocol/mod.rs index dbdcd376..a5ccda34 100644 --- a/crates/flashblocks-p2p/src/protocol/mod.rs +++ b/crates/flashblocks-p2p/src/protocol/mod.rs @@ -1,5 +1,4 @@ pub mod auth; -pub mod error; pub mod event; pub mod handler; pub mod proto; diff --git a/crates/rollup-boost/Cargo.toml b/crates/rollup-boost/Cargo.toml index 5f1bf069..2a973d6c 100644 --- a/crates/rollup-boost/Cargo.toml +++ b/crates/rollup-boost/Cargo.toml @@ -65,6 +65,10 @@ paste = "1.0.15" parking_lot = "0.12.3" tokio-util = { version = "0.7.13" } +ed25519-dalek = { version = "2", features = ["serde"] } +blake3 = "1" # fast hashing for payload IDs +hex = "0.4" + [dev-dependencies] rand = "0.9.0" time = { version = "0.3.36", features = ["macros", "formatting", "parsing"] } diff --git a/crates/rollup-boost/src/cli.rs b/crates/rollup-boost/src/cli.rs index 3eb4d3d6..1dd7e114 100644 --- a/crates/rollup-boost/src/cli.rs +++ b/crates/rollup-boost/src/cli.rs @@ -94,7 +94,7 @@ pub struct RollupBoostArgs { pub block_selection_policy: Option, #[clap(flatten)] - pub flashblocks: FlashblocksArgs, + pub flashblocks: Option, } impl RollupBoostArgs { @@ -112,12 +112,15 @@ impl RollupBoostArgs { } else { bail!("Missing L2 Client JWT secret"); }; + if let Some(flashblocks_args) = &self.flashblocks {} let l2_client = RpcClient::new( l2_client_args.l2_url.clone(), l2_auth_jwt, l2_client_args.l2_timeout, PayloadSource::L2, + None, + None, )?; let builder_args = self.builder; @@ -134,56 +137,62 @@ impl RollupBoostArgs { builder_auth_jwt, builder_args.builder_timeout, PayloadSource::Builder, + self.flashblocks + .as_ref() + .map(|fb| fb.flashblocks_authorization_sk.clone()), + self.flashblocks + .as_ref() + .map(|fb| fb.flashblocks_builder_vk.clone()), )?; let (probe_layer, probes) = ProbeLayer::new(); let execution_mode = Arc::new(Mutex::new(self.execution_mode)); - let (rpc_module, health_handle): (RpcModule<()>, _) = if self.flashblocks.flashblocks { - let flashblocks_args = self.flashblocks; - let inbound_url = flashblocks_args.flashblocks_builder_url; - let outbound_addr = SocketAddr::new( - IpAddr::from_str(&flashblocks_args.flashblocks_host)?, - flashblocks_args.flashblocks_port, - ); - - let builder_client = Arc::new(Flashblocks::run( - builder_client.clone(), - inbound_url, - outbound_addr, - flashblocks_args.flashblock_builder_ws_reconnect_ms, - )?); - - let rollup_boost = RollupBoostServer::new( - l2_client, - builder_client, - execution_mode.clone(), - self.block_selection_policy, - probes.clone(), - ); - - let health_handle = rollup_boost - .spawn_health_check(self.health_check_interval, self.max_unsafe_interval); - - // Spawn the debug server - rollup_boost.start_debug_server(debug_addr.as_str()).await?; - (rollup_boost.try_into()?, health_handle) - } else { - let rollup_boost = RollupBoostServer::new( - l2_client, - Arc::new(builder_client), - execution_mode.clone(), - self.block_selection_policy, - probes.clone(), - ); - - let health_handle = rollup_boost - .spawn_health_check(self.health_check_interval, self.max_unsafe_interval); - - // Spawn the debug server - rollup_boost.start_debug_server(debug_addr.as_str()).await?; - (rollup_boost.try_into()?, health_handle) - }; + let (rpc_module, health_handle): (RpcModule<()>, _) = + if let Some(flashblocks_args) = self.flashblocks { + let inbound_url = flashblocks_args.flashblocks_builder_url; + let outbound_addr = SocketAddr::new( + IpAddr::from_str(&flashblocks_args.flashblocks_host)?, + flashblocks_args.flashblocks_port, + ); + + let builder_client = Arc::new(Flashblocks::run( + builder_client.clone(), + inbound_url, + outbound_addr, + flashblocks_args.flashblock_builder_ws_reconnect_ms, + )?); + + let rollup_boost = RollupBoostServer::new( + l2_client, + builder_client, + execution_mode.clone(), + self.block_selection_policy, + probes.clone(), + ); + + let health_handle = rollup_boost + .spawn_health_check(self.health_check_interval, self.max_unsafe_interval); + + // Spawn the debug server + rollup_boost.start_debug_server(debug_addr.as_str()).await?; + (rollup_boost.try_into()?, health_handle) + } else { + let rollup_boost = RollupBoostServer::new( + l2_client, + Arc::new(builder_client), + execution_mode.clone(), + self.block_selection_policy, + probes.clone(), + ); + + let health_handle = rollup_boost + .spawn_health_check(self.health_check_interval, self.max_unsafe_interval); + + // Spawn the debug server + rollup_boost.start_debug_server(debug_addr.as_str()).await?; + (rollup_boost.try_into()?, health_handle) + }; // Build and start the server info!("Starting server on :{}", self.rpc_port); diff --git a/crates/rollup-boost/src/client/rpc.rs b/crates/rollup-boost/src/client/rpc.rs index 6633e643..d53a4b05 100644 --- a/crates/rollup-boost/src/client/rpc.rs +++ b/crates/rollup-boost/src/client/rpc.rs @@ -1,8 +1,8 @@ -use crate::EngineApiExt; use crate::client::auth::AuthLayer; use crate::payload::{NewPayload, OpExecutionPayloadEnvelope, PayloadSource, PayloadVersion}; use crate::server::EngineApiClient; use crate::version::{CARGO_PKG_VERSION, VERGEN_GIT_SHA}; +use crate::{Authorization, EngineApiExt}; use alloy_primitives::{B256, Bytes}; use alloy_rpc_types_engine::{ ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, JwtError, JwtSecret, PayloadId, @@ -10,6 +10,7 @@ use alloy_rpc_types_engine::{ }; use alloy_rpc_types_eth::{Block, BlockNumberOrTag}; use clap::{Parser, arg}; +use ed25519_dalek::{SigningKey, VerifyingKey}; use http::{HeaderMap, Uri}; use jsonrpsee::core::async_trait; use jsonrpsee::core::middleware::layer::RpcLogger; @@ -22,6 +23,7 @@ use op_alloy_rpc_types_engine::{ }; use opentelemetry::trace::SpanKind; use paste::paste; +use reth_optimism_payload_builder::payload_id_optimism; use std::path::PathBuf; use std::time::Duration; use thiserror::Error; @@ -109,6 +111,10 @@ pub struct RpcClient { auth_rpc: Uri, /// The source of the payload payload_source: PayloadSource, + /// Flashblocks Authorization Secret + flashblocks_authorization_sk: Option, + /// Flashblocks Authorization Secret + flashblocks_builder_pk: Option, } impl RpcClient { @@ -118,6 +124,8 @@ impl RpcClient { auth_rpc_jwt_secret: JwtSecret, timeout: u64, payload_source: PayloadSource, + flashblocks_authorization_sk: Option, + flashblocks_builder_pk: Option, ) -> Result { let version = format!("{CARGO_PKG_VERSION}-{VERGEN_GIT_SHA}"); let mut headers = HeaderMap::new(); @@ -134,6 +142,8 @@ impl RpcClient { auth_client, auth_rpc, payload_source, + flashblocks_authorization_sk, + flashblocks_builder_pk, }) } @@ -155,9 +165,20 @@ impl RpcClient { payload_attributes: Option, ) -> ClientResult { info!("Sending fork_choice_updated_v3 to {}", self.payload_source); + let authorization = match ( + &payload_attributes, + &self.flashblocks_authorization_sk, + &self.flashblocks_builder_pk, + ) { + (Some(attrs), Some(sk), Some(pk)) => { + let payload_id = payload_id_optimism(&fork_choice_state.head_block_hash, attrs, 3); + Some(Authorization::new(sk, pk.clone(), payload_id)) + } + _ => None, + }; let res = self .auth_client - .fork_choice_updated_v3(fork_choice_state, payload_attributes.clone()) + .fork_choice_updated_v3(fork_choice_state, payload_attributes.clone(), authorization) .await .set_code()?; @@ -457,7 +478,7 @@ pub mod tests { let port = get_available_port(); let secret = JwtSecret::from_hex(SECRET).unwrap(); let auth_rpc = Uri::from_str(&format!("http://{}:{}", AUTH_ADDR, port)).unwrap(); - let client = RpcClient::new(auth_rpc, secret, 1000, PayloadSource::L2).unwrap(); + let client = RpcClient::new(auth_rpc, secret, 1000, PayloadSource::L2, None, None).unwrap(); let response = send_request(client.auth_client, port).await; assert!(response.is_ok()); assert_eq!(response.unwrap(), "You are the dark lord"); diff --git a/crates/rollup-boost/src/flashblocks/args.rs b/crates/rollup-boost/src/flashblocks/args.rs index 1b5a7780..4097b747 100644 --- a/crates/rollup-boost/src/flashblocks/args.rs +++ b/crates/rollup-boost/src/flashblocks/args.rs @@ -1,10 +1,15 @@ use clap::Parser; +use ed25519_dalek::{SigningKey, VerifyingKey}; +use eyre::Context; use url::Url; +use hex::FromHex; + #[derive(Parser, Clone, Debug)] pub struct FlashblocksArgs { /// Enable Flashblocks client - #[arg(long, env, default_value = "false")] + /// TODO: validate input + #[arg(long, env)] pub flashblocks: bool, /// Flashblocks Builder WebSocket URL @@ -22,4 +27,21 @@ pub struct FlashblocksArgs { /// Time used for timeout if builder disconnected #[arg(long, env, default_value = "5000")] pub flashblock_builder_ws_reconnect_ms: u64, + + #[arg(long, env = "FLASHBLOCKS_AUTHORIZATION_SK", value_parser = parse_sk)] + pub flashblocks_authorization_sk: SigningKey, + + #[arg(long, env = "FLASHBLOCKS_BUILDER_VK", value_parser = parse_vk)] + pub flashblocks_builder_vk: VerifyingKey, +} + +fn parse_sk(s: &str) -> eyre::Result { + let bytes = + <[u8; 32]>::from_hex(s.trim()).context("failed parsing flashblocks_authorization_sk")?; + Ok(SigningKey::from_bytes(&bytes)) +} + +fn parse_vk(s: &str) -> eyre::Result { + let bytes = <[u8; 32]>::from_hex(s.trim()).context("failed parsing flashblocks_builder_vk")?; + Ok(VerifyingKey::from_bytes(&bytes)?) } diff --git a/crates/flashblocks-p2p/src/protocol/error.rs b/crates/rollup-boost/src/flashblocks/error.rs similarity index 100% rename from crates/flashblocks-p2p/src/protocol/error.rs rename to crates/rollup-boost/src/flashblocks/error.rs diff --git a/crates/rollup-boost/src/flashblocks/mod.rs b/crates/rollup-boost/src/flashblocks/mod.rs index 3cdd3e7e..e7cafcff 100644 --- a/crates/rollup-boost/src/flashblocks/mod.rs +++ b/crates/rollup-boost/src/flashblocks/mod.rs @@ -14,4 +14,7 @@ mod outbound; mod args; pub use args::*; +mod error; +pub use error::*; + mod metrics; diff --git a/crates/rollup-boost/src/flashblocks/service.rs b/crates/rollup-boost/src/flashblocks/service.rs index b2d12686..606fc674 100644 --- a/crates/rollup-boost/src/flashblocks/service.rs +++ b/crates/rollup-boost/src/flashblocks/service.rs @@ -385,6 +385,8 @@ mod tests { jwt_secret, 2000, PayloadSource::Builder, + None, + None, )?; let service = @@ -414,6 +416,8 @@ mod tests { jwt_secret, 2000, PayloadSource::Builder, + None, + None, )?; let service = diff --git a/crates/rollup-boost/src/health.rs b/crates/rollup-boost/src/health.rs index fed2e27c..730582a2 100644 --- a/crates/rollup-boost/src/health.rs +++ b/crates/rollup-boost/src/health.rs @@ -273,6 +273,8 @@ mod tests { JwtSecret::random(), 100, PayloadSource::Builder, + None, + None, )?); let health_handle = HealthHandle { @@ -304,6 +306,8 @@ mod tests { JwtSecret::random(), 100, PayloadSource::Builder, + None, + None, )?); let health_handle = HealthHandle { @@ -336,6 +340,8 @@ mod tests { JwtSecret::random(), 100, PayloadSource::Builder, + None, + None, )?); let health_handle = HealthHandle { @@ -368,6 +374,8 @@ mod tests { JwtSecret::random(), 100, PayloadSource::Builder, + None, + None, )?); let health_handle = HealthHandle { @@ -393,6 +401,8 @@ mod tests { JwtSecret::random(), 100, PayloadSource::Builder, + None, + None, )?); let health_handle = HealthHandle { diff --git a/crates/rollup-boost/src/server.rs b/crates/rollup-boost/src/server.rs index d6f315a2..9d3fb338 100644 --- a/crates/rollup-boost/src/server.rs +++ b/crates/rollup-boost/src/server.rs @@ -1,5 +1,5 @@ use crate::debug_api::ExecutionMode; -use crate::{BlockSelectionPolicy, EngineApiExt}; +use crate::{BlockSelectionPolicy, EngineApiExt, FlashblocksP2PError}; use crate::{ client::rpc::RpcClient, debug_api::DebugServer, @@ -16,6 +16,7 @@ use alloy_rpc_types_engine::{ PayloadStatus, }; use alloy_rpc_types_eth::{Block, BlockNumberOrTag}; +use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; use http_body_util::{BodyExt, Full}; use jsonrpsee::RpcModule; use jsonrpsee::core::BoxError; @@ -33,6 +34,7 @@ use op_alloy_rpc_types_engine::{ }; use opentelemetry::trace::SpanKind; use parking_lot::Mutex; +use serde::{Deserialize, Serialize}; use std::sync::Arc; use std::time::Duration; use tokio::task::JoinHandle; @@ -285,6 +287,41 @@ where } } +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Authorization { + pub payload_id: PayloadId, + pub builder_pub: VerifyingKey, + pub authorizer_sig: Signature, +} + +impl Authorization { + pub fn new( + authorizer_sk: &SigningKey, + builder_pub: VerifyingKey, + payload_id: PayloadId, + ) -> Self { + let mut msg = payload_id.0.to_vec(); + msg.extend_from_slice(builder_pub.as_bytes()); + let hash = blake3::hash(&msg); + let sig = authorizer_sk.sign(hash.as_bytes()); + + Self { + payload_id, + builder_pub, + authorizer_sig: sig, + } + } + + pub fn verify(&self, authorizer_pub: VerifyingKey) -> Result<(), FlashblocksP2PError> { + let mut msg = self.payload_id.0.to_vec(); + msg.extend_from_slice(self.builder_pub.as_bytes()); + let hash = blake3::hash(&msg); + authorizer_pub + .verify(hash.as_bytes(), &self.authorizer_sig) + .map_err(|_| FlashblocksP2PError::InvalidAuthorizerSig) + } +} + #[rpc(server, client)] pub trait EngineApi { #[method(name = "engine_forkchoiceUpdatedV3")] @@ -292,6 +329,7 @@ pub trait EngineApi { &self, fork_choice_state: ForkchoiceState, payload_attributes: Option, + flashblocks_authorization: Option, ) -> RpcResult; #[method(name = "engine_getPayloadV3")] @@ -346,6 +384,7 @@ where &self, fork_choice_state: ForkchoiceState, payload_attributes: Option, + _flashblocks_authorization: Option, ) -> RpcResult { // Send the FCU to the default l2 client let l2_fut = self @@ -652,8 +691,15 @@ pub mod tests { let (builder_server, builder_server_addr) = spawn_server(builder_mock.clone()).await; let l2_auth_rpc = Uri::from_str(&format!("http://{l2_server_addr}")).unwrap(); - let l2_client = - RpcClient::new(l2_auth_rpc.clone(), jwt_secret, 2000, PayloadSource::L2).unwrap(); + let l2_client = RpcClient::new( + l2_auth_rpc.clone(), + jwt_secret, + 2000, + PayloadSource::L2, + None, + None, + ) + .unwrap(); let builder_auth_rpc = Uri::from_str(&format!("http://{builder_server_addr}")).unwrap(); let builder_client = Arc::new( @@ -662,6 +708,8 @@ pub mod tests { jwt_secret, 2000, PayloadSource::Builder, + None, + None, ) .unwrap(), ); @@ -752,7 +800,7 @@ pub mod tests { }; let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, None) + .fork_choice_updated_v3(fcu, None, None) .await; assert!(fcu_response.is_ok()); let fcu_requests = test_harness.l2_mock.fcu_requests.clone(); @@ -933,7 +981,7 @@ pub mod tests { }; let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, None) + .fork_choice_updated_v3(fcu, None, None) .await; assert!(fcu_response.is_ok()); @@ -1006,7 +1054,7 @@ pub mod tests { }; let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, Some(payload_attributes.clone())) + .fork_choice_updated_v3(fcu, Some(payload_attributes.clone()), None) .await; assert!(fcu_response.is_ok()); @@ -1018,7 +1066,7 @@ pub mod tests { payload_attributes.no_tx_pool = Some(true); let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, Some(payload_attributes)) + .fork_choice_updated_v3(fcu, Some(payload_attributes), None) .await; assert!(fcu_response.is_ok()); @@ -1050,7 +1098,7 @@ pub mod tests { }; let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, None) + .fork_choice_updated_v3(fcu, None, None) .await; assert!(fcu_response.is_err()); @@ -1060,7 +1108,7 @@ pub mod tests { }; let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, Some(payload_attributes)) + .fork_choice_updated_v3(fcu, Some(payload_attributes), None) .await; assert!(fcu_response.is_err()); } diff --git a/crates/rollup-boost/src/tests/common/mod.rs b/crates/rollup-boost/src/tests/common/mod.rs index 7d6f2a13..e6ad81fd 100644 --- a/crates/rollup-boost/src/tests/common/mod.rs +++ b/crates/rollup-boost/src/tests/common/mod.rs @@ -142,6 +142,7 @@ impl EngineApi { finalized_block_hash: current_head, }, payload_attributes, + None, ) .await?) } From cddf97f00aba2306b313c265061d1b6ee54495d6 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 9 Jul 2025 17:53:36 -0700 Subject: [PATCH 05/45] wip --- crates/rollup-boost/src/client/rpc.rs | 29 ++++++++------ crates/rollup-boost/src/flashblocks/args.rs | 43 ++++++++++++++++----- crates/rollup-boost/src/server.rs | 27 ++++++++----- crates/rollup-boost/src/tests/common/mod.rs | 2 +- 4 files changed, 69 insertions(+), 32 deletions(-) diff --git a/crates/rollup-boost/src/client/rpc.rs b/crates/rollup-boost/src/client/rpc.rs index d53a4b05..d1ef6843 100644 --- a/crates/rollup-boost/src/client/rpc.rs +++ b/crates/rollup-boost/src/client/rpc.rs @@ -2,7 +2,7 @@ use crate::client::auth::AuthLayer; use crate::payload::{NewPayload, OpExecutionPayloadEnvelope, PayloadSource, PayloadVersion}; use crate::server::EngineApiClient; use crate::version::{CARGO_PKG_VERSION, VERGEN_GIT_SHA}; -use crate::{Authorization, EngineApiExt}; +use crate::{Authorization, EngineApiExt, FlashblocksEngineApiClient}; use alloy_primitives::{B256, Bytes}; use alloy_rpc_types_engine::{ ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, JwtError, JwtSecret, PayloadId, @@ -165,22 +165,29 @@ impl RpcClient { payload_attributes: Option, ) -> ClientResult { info!("Sending fork_choice_updated_v3 to {}", self.payload_source); - let authorization = match ( + let res = match ( &payload_attributes, &self.flashblocks_authorization_sk, &self.flashblocks_builder_pk, ) { (Some(attrs), Some(sk), Some(pk)) => { let payload_id = payload_id_optimism(&fork_choice_state.head_block_hash, attrs, 3); - Some(Authorization::new(sk, pk.clone(), payload_id)) + let authorization = Authorization::new(sk, pk.clone(), payload_id); + self.auth_client + .fork_choice_updated_flashblocks_v1( + fork_choice_state, + payload_attributes.clone(), + Some(authorization), + ) + .await + .set_code()? } - _ => None, + _ => self + .auth_client + .fork_choice_updated_v3(fork_choice_state, payload_attributes.clone()) + .await + .set_code()?, }; - let res = self - .auth_client - .fork_choice_updated_v3(fork_choice_state, payload_attributes.clone(), authorization) - .await - .set_code()?; if let Some(payload_id) = res.payload_id { tracing::Span::current().record("payload_id", payload_id.to_string()); @@ -468,9 +475,7 @@ pub mod tests { 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", - )); + cmd.assert().failure(); } #[tokio::test] diff --git a/crates/rollup-boost/src/flashblocks/args.rs b/crates/rollup-boost/src/flashblocks/args.rs index 4097b747..0bfdc080 100644 --- a/crates/rollup-boost/src/flashblocks/args.rs +++ b/crates/rollup-boost/src/flashblocks/args.rs @@ -1,37 +1,60 @@ -use clap::Parser; +use clap::{Args, Parser}; use ed25519_dalek::{SigningKey, VerifyingKey}; use eyre::Context; use url::Url; use hex::FromHex; -#[derive(Parser, Clone, Debug)] +#[derive(Args, Clone, Debug)] +#[group(requires = "flashblocks")] pub struct FlashblocksArgs { /// Enable Flashblocks client - /// TODO: validate input - #[arg(long, env)] + #[arg(long, env, required = false)] pub flashblocks: bool, /// Flashblocks Builder WebSocket URL - #[arg(long, env, default_value = "ws://127.0.0.1:1111")] + #[arg( + long, + env, + default_value = "ws://127.0.0.1:1111" + )] pub flashblocks_builder_url: Url, /// Flashblocks WebSocket host for outbound connections - #[arg(long, env, default_value = "127.0.0.1")] + #[arg( + long, + env, + default_value = "127.0.0.1" + )] pub flashblocks_host: String, /// Flashblocks WebSocket port for outbound connections - #[arg(long, env, default_value = "1112")] + #[arg( + long, + env, + default_value = "1112" + )] pub flashblocks_port: u16, /// Time used for timeout if builder disconnected - #[arg(long, env, default_value = "5000")] + #[arg( + long, + env, + default_value = "5000" + )] pub flashblock_builder_ws_reconnect_ms: u64, - #[arg(long, env = "FLASHBLOCKS_AUTHORIZATION_SK", value_parser = parse_sk)] + #[arg( + long, + env = "FLASHBLOCKS_AUTHORIZATION_SK", value_parser = parse_sk, + required = false, + )] pub flashblocks_authorization_sk: SigningKey, - #[arg(long, env = "FLASHBLOCKS_BUILDER_VK", value_parser = parse_vk)] + #[arg(long, + env = "FLASHBLOCKS_BUILDER_VK", value_parser = parse_vk, + required = false, + )] pub flashblocks_builder_vk: VerifyingKey, } diff --git a/crates/rollup-boost/src/server.rs b/crates/rollup-boost/src/server.rs index 9d3fb338..12dd91ab 100644 --- a/crates/rollup-boost/src/server.rs +++ b/crates/rollup-boost/src/server.rs @@ -322,6 +322,17 @@ impl Authorization { } } +#[rpc(server, client)] +pub trait FlashblocksEngineApi { + #[method(name = "engine_forkchoiceUpdatedFlashblocksV1")] + async fn fork_choice_updated_flashblocks_v1( + &self, + fork_choice_state: ForkchoiceState, + payload_attributes: Option, + flashblocks_authorization: Option, + ) -> RpcResult; +} + #[rpc(server, client)] pub trait EngineApi { #[method(name = "engine_forkchoiceUpdatedV3")] @@ -329,7 +340,6 @@ pub trait EngineApi { &self, fork_choice_state: ForkchoiceState, payload_attributes: Option, - flashblocks_authorization: Option, ) -> RpcResult; #[method(name = "engine_getPayloadV3")] @@ -384,7 +394,6 @@ where &self, fork_choice_state: ForkchoiceState, payload_attributes: Option, - _flashblocks_authorization: Option, ) -> RpcResult { // Send the FCU to the default l2 client let l2_fut = self @@ -800,7 +809,7 @@ pub mod tests { }; let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, None, None) + .fork_choice_updated_v3(fcu, None) .await; assert!(fcu_response.is_ok()); let fcu_requests = test_harness.l2_mock.fcu_requests.clone(); @@ -981,7 +990,7 @@ pub mod tests { }; let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, None, None) + .fork_choice_updated_v3(fcu, None) .await; assert!(fcu_response.is_ok()); @@ -1054,9 +1063,9 @@ pub mod tests { }; let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, Some(payload_attributes.clone()), None) + .fork_choice_updated_v3(fcu, Some(payload_attributes.clone())) .await; - assert!(fcu_response.is_ok()); + fcu_response.unwrap(); // no tx pool is false so should return the builder payload let get_payload_response = test_harness.rpc_client.get_payload_v3(payload_id).await; @@ -1066,7 +1075,7 @@ pub mod tests { payload_attributes.no_tx_pool = Some(true); let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, Some(payload_attributes), None) + .fork_choice_updated_v3(fcu, Some(payload_attributes)) .await; assert!(fcu_response.is_ok()); @@ -1098,7 +1107,7 @@ pub mod tests { }; let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, None, None) + .fork_choice_updated_v3(fcu, None) .await; assert!(fcu_response.is_err()); @@ -1108,7 +1117,7 @@ pub mod tests { }; let fcu_response = test_harness .rpc_client - .fork_choice_updated_v3(fcu, Some(payload_attributes), None) + .fork_choice_updated_v3(fcu, Some(payload_attributes)) .await; assert!(fcu_response.is_err()); } diff --git a/crates/rollup-boost/src/tests/common/mod.rs b/crates/rollup-boost/src/tests/common/mod.rs index e6ad81fd..f10e6a2b 100644 --- a/crates/rollup-boost/src/tests/common/mod.rs +++ b/crates/rollup-boost/src/tests/common/mod.rs @@ -142,7 +142,6 @@ impl EngineApi { finalized_block_hash: current_head, }, payload_attributes, - None, ) .await?) } @@ -360,6 +359,7 @@ impl RollupBoostTestHarnessBuilder { println!("proxy authrpc: {}", builder_url); // Start Rollup-boost instance + let mut rollup_boost = RollupBoostConfig::default(); rollup_boost.args.l2_client.l2_url = l2.auth_rpc().await?; rollup_boost.args.builder.builder_url = builder_url.try_into().unwrap(); From 89f5ee688c8f0133191e3b01c29aa8316fb91d06 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 9 Jul 2025 18:37:14 -0700 Subject: [PATCH 06/45] add capability to node --- Cargo.lock | 4 ++ Cargo.toml | 1 + crates/flashblocks-p2p/Cargo.toml | 1 + crates/flashblocks-p2p/src/bin.bak/main.rs | 52 ------------------- .../flashblocks-p2p/src/connection/handler.rs | 4 +- crates/flashblocks-p2p/src/connection/mod.rs | 4 +- crates/flashblocks-p2p/src/protocol/event.rs | 2 +- .../flashblocks-p2p/src/protocol/handler.rs | 6 +-- crates/flashblocks-rpc/Cargo.toml | 5 ++ crates/flashblocks-rpc/src/bin/main.rs | 14 +++++ 10 files changed, 33 insertions(+), 60 deletions(-) delete mode 100644 crates/flashblocks-p2p/src/bin.bak/main.rs diff --git a/Cargo.lock b/Cargo.lock index f7d9e5b0..7639dbf4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3491,6 +3491,7 @@ dependencies = [ "brotli", "clap", "eyre", + "flashblocks-p2p", "futures-util", "jsonrpsee 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)", "metrics", @@ -3500,6 +3501,9 @@ dependencies = [ "op-alloy-rpc-types", "reth-db", "reth-e2e-test-utils", + "reth-eth-wire", + "reth-ethereum", + "reth-network", "reth-node-api", "reth-node-builder", "reth-node-core", diff --git a/Cargo.toml b/Cargo.toml index 3a8b30a7..c00044b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ members = [ [workspace.dependencies] rollup-boost = { path = "crates/rollup-boost" } +flashblocks-p2p = { path = "crates/flashblocks-p2p" } tracing = "0.1.4" tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] } diff --git a/crates/flashblocks-p2p/Cargo.toml b/crates/flashblocks-p2p/Cargo.toml index 65ca4ea7..9ff5d59f 100644 --- a/crates/flashblocks-p2p/Cargo.toml +++ b/crates/flashblocks-p2p/Cargo.toml @@ -24,6 +24,7 @@ reth-network = { workspace = true } # op-alloy-consensus.workspace = true # alloy-rpc-types-eth.workspace = true + ed25519-dalek = { version = "2", features = ["serde"] } rand_core = "0.6" # for secure RNG blake3 = "1" # fast hashing for payload IDs diff --git a/crates/flashblocks-p2p/src/bin.bak/main.rs b/crates/flashblocks-p2p/src/bin.bak/main.rs deleted file mode 100644 index 177432ca..00000000 --- a/crates/flashblocks-p2p/src/bin.bak/main.rs +++ /dev/null @@ -1,52 +0,0 @@ -#![allow(missing_docs, rustdoc::missing_crate_level_docs)] - -use clap::Parser; -use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay}; -use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; -use reth_optimism_node::{OpNode, args::RollupArgs}; -use tracing::info; - -#[derive(Debug, Clone, PartialEq, Eq, clap::Args)] -#[command(next_help_heading = "Rollup")] -struct FlashblocksRollupArgs { - #[command(flatten)] - rollup_args: RollupArgs, - - #[arg(long = "flashblocks.enabled", default_value = "false")] - flashblocks_enabled: bool, - - #[arg(long = "flashblocks.websocket-url", value_name = "WEBSOCKET_URL")] - websocket_url: url::Url, -} - -fn main() { - if let Err(err) = - Cli::::parse().run(async move |builder, args| { - let rollup_args = args.rollup_args; - let chain_spec = builder.config().chain.clone(); - - info!(target: "reth::cli", "Launching Flashblocks RPC overlay node"); - let handle = builder - .node(OpNode::new(rollup_args)) - .extend_rpc_modules(move |ctx| { - if args.flashblocks_enabled { - let mut flashblocks_overlay = - FlashblocksOverlay::new(args.websocket_url, chain_spec); - flashblocks_overlay.start()?; - - let eth_api = ctx.registry.eth_api().clone(); - let api_ext = FlashblocksApiExt::new(eth_api.clone(), flashblocks_overlay); - - ctx.modules.replace_configured(api_ext.into_rpc())?; - } - Ok(()) - }) - .launch_with_debug_capabilities() - .await?; - handle.node_exit_future.await - }) - { - tracing::error!("Error: {err:?}"); - std::process::exit(1); - } -} diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs index 3da25d89..0c551974 100644 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -11,8 +11,8 @@ use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; /// The connection handler for the flashblocks RLPx protocol. -pub(crate) struct FlashblocksConnectionHandler { - pub(crate) state: FlashblocksP2PState, +pub struct FlashblocksConnectionHandler { + pub state: FlashblocksP2PState, } impl ConnectionHandler for FlashblocksConnectionHandler { diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index 3d54ab02..f9479320 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -14,14 +14,14 @@ use tokio_stream::wrappers::UnboundedReceiverStream; pub(crate) mod handler; /// We define some custom commands that the subprotocol supports. -pub(crate) enum FlashblocksCommand { +pub enum FlashblocksCommand { /// Sends a flashblocks payload to the peer FlashblocksPayloadV1 { payload: Authorized, }, } -pub(crate) struct FlashblocksConnection { +pub struct FlashblocksConnection { conn: ProtocolConnection, commands: UnboundedReceiverStream, state: FlashblocksP2PState, diff --git a/crates/flashblocks-p2p/src/protocol/event.rs b/crates/flashblocks-p2p/src/protocol/event.rs index c6002786..180d5c0e 100644 --- a/crates/flashblocks-p2p/src/protocol/event.rs +++ b/crates/flashblocks-p2p/src/protocol/event.rs @@ -5,7 +5,7 @@ use tokio::sync::mpsc; /// The events that can be emitted by our custom protocol. #[derive(Debug)] -pub(crate) enum FlashblocksP2PEvent { +pub enum FlashblocksP2PEvent { Established { #[expect(dead_code)] direction: Direction, diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index b81af9c0..1203a27b 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -6,13 +6,13 @@ use tokio::sync::mpsc; /// Protocol state is an helper struct to store the protocol events. #[derive(Clone, Debug)] -pub(crate) struct FlashblocksP2PState { - pub(crate) events: mpsc::UnboundedSender, +pub struct FlashblocksP2PState { + pub events: mpsc::UnboundedSender, } /// The protocol handler takes care of incoming and outgoing connections. #[derive(Debug)] -pub(crate) struct FlashblocksProtoHandler { +pub struct FlashblocksProtoHandler { pub state: FlashblocksP2PState, } diff --git a/crates/flashblocks-rpc/Cargo.toml b/crates/flashblocks-rpc/Cargo.toml index 55d15249..b7b1af0d 100644 --- a/crates/flashblocks-rpc/Cargo.toml +++ b/crates/flashblocks-rpc/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT" [dependencies] rollup-boost.workspace = true +flashblocks-p2p.workspace = true reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } @@ -29,6 +30,10 @@ reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-ethereum = { workspace = true, features = ["node", "network", "cli"] } +reth-eth-wire = { workspace = true } +reth-network = { workspace = true } + alloy-eips.workspace = true alloy-primitives.workspace = true alloy-json-rpc.workspace = true diff --git a/crates/flashblocks-rpc/src/bin/main.rs b/crates/flashblocks-rpc/src/bin/main.rs index 177432ca..b7f2bcf2 100644 --- a/crates/flashblocks-rpc/src/bin/main.rs +++ b/crates/flashblocks-rpc/src/bin/main.rs @@ -1,9 +1,12 @@ #![allow(missing_docs, rustdoc::missing_crate_level_docs)] use clap::Parser; +use flashblocks_p2p::protocol::handler::{FlashblocksP2PState, FlashblocksProtoHandler}; use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay}; +use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; use reth_optimism_node::{OpNode, args::RollupArgs}; +use tokio::sync::mpsc; use tracing::info; #[derive(Debug, Clone, PartialEq, Eq, clap::Args)] @@ -43,6 +46,17 @@ fn main() { }) .launch_with_debug_capabilities() .await?; + + let (tx, mut rx) = mpsc::unbounded_channel(); + + let custom_rlpx_handler = FlashblocksProtoHandler { + state: FlashblocksP2PState { events: tx }, + }; + + handle + .node + .network + .add_rlpx_sub_protocol(custom_rlpx_handler.into_rlpx_sub_protocol()); handle.node_exit_future.await }) { From c3ad860278e3b02c9a3605798e93e8a37d0b427d Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 9 Jul 2025 19:11:31 -0700 Subject: [PATCH 07/45] feat: add validation to node overlay --- Cargo.lock | 1 + crates/flashblocks-rpc/Cargo.toml | 1 + crates/flashblocks-rpc/src/bin/main.rs | 21 ++-- crates/flashblocks-rpc/src/flashblocks.rs | 104 +++++++------------- crates/rollup-boost/src/flashblocks/args.rs | 4 +- 5 files changed, 52 insertions(+), 79 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7639dbf4..a53bb020 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3490,6 +3490,7 @@ dependencies = [ "alloy-rpc-types-eth", "brotli", "clap", + "ed25519-dalek", "eyre", "flashblocks-p2p", "futures-util", diff --git a/crates/flashblocks-rpc/Cargo.toml b/crates/flashblocks-rpc/Cargo.toml index b7b1af0d..51525cdc 100644 --- a/crates/flashblocks-rpc/Cargo.toml +++ b/crates/flashblocks-rpc/Cargo.toml @@ -33,6 +33,7 @@ reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1 reth-ethereum = { workspace = true, features = ["node", "network", "cli"] } reth-eth-wire = { workspace = true } reth-network = { workspace = true } +ed25519-dalek = { version = "2", features = ["serde"] } alloy-eips.workspace = true alloy-primitives.workspace = true diff --git a/crates/flashblocks-rpc/src/bin/main.rs b/crates/flashblocks-rpc/src/bin/main.rs index b7f2bcf2..f41a9c85 100644 --- a/crates/flashblocks-rpc/src/bin/main.rs +++ b/crates/flashblocks-rpc/src/bin/main.rs @@ -1,11 +1,13 @@ #![allow(missing_docs, rustdoc::missing_crate_level_docs)] use clap::Parser; +use ed25519_dalek::VerifyingKey; use flashblocks_p2p::protocol::handler::{FlashblocksP2PState, FlashblocksProtoHandler}; -use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay}; +use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, FlashblocksOverlayBuilder}; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; use reth_optimism_node::{OpNode, args::RollupArgs}; +use rollup_boost::parse_vk; use tokio::sync::mpsc; use tracing::info; @@ -20,22 +22,30 @@ struct FlashblocksRollupArgs { #[arg(long = "flashblocks.websocket-url", value_name = "WEBSOCKET_URL")] websocket_url: url::Url, + + #[arg(long, + env = "FLASHBLOCKS_BUILDER_VK", value_parser = parse_vk, + required = false, + )] + pub flashblocks_builder_vk: VerifyingKey, } -fn main() { +pub fn main() { if let Err(err) = Cli::::parse().run(async move |builder, args| { let rollup_args = args.rollup_args; let chain_spec = builder.config().chain.clone(); + let (tx, events) = mpsc::unbounded_channel(); + + let flashblocks_overlay_builder = + FlashblocksOverlayBuilder::new(chain_spec, args.flashblocks_builder_vk, events); + let flashblocks_overlay = flashblocks_overlay_builder.start()?; info!(target: "reth::cli", "Launching Flashblocks RPC overlay node"); let handle = builder .node(OpNode::new(rollup_args)) .extend_rpc_modules(move |ctx| { if args.flashblocks_enabled { - let mut flashblocks_overlay = - FlashblocksOverlay::new(args.websocket_url, chain_spec); - flashblocks_overlay.start()?; let eth_api = ctx.registry.eth_api().clone(); let api_ext = FlashblocksApiExt::new(eth_api.clone(), flashblocks_overlay); @@ -47,7 +57,6 @@ fn main() { .launch_with_debug_capabilities() .await?; - let (tx, mut rx) = mpsc::unbounded_channel(); let custom_rlpx_handler = FlashblocksProtoHandler { state: FlashblocksP2PState { events: tx }, diff --git a/crates/flashblocks-rpc/src/flashblocks.rs b/crates/flashblocks-rpc/src/flashblocks.rs index 689d7ea5..0312fc26 100644 --- a/crates/flashblocks-rpc/src/flashblocks.rs +++ b/crates/flashblocks-rpc/src/flashblocks.rs @@ -1,6 +1,7 @@ use crate::{FlashblocksApi, cache::FlashblocksCache}; use alloy_primitives::{Address, TxHash, U256}; -use futures_util::StreamExt; +use ed25519_dalek::VerifyingKey; +use flashblocks_p2p::protocol::event::FlashblocksP2PEvent; use jsonrpsee::core::async_trait; use op_alloy_network::Optimism; use reth_optimism_chainspec::OpChainSpec; @@ -8,97 +9,58 @@ use reth_rpc_eth_api::{RpcBlock, RpcReceipt}; use rollup_boost::FlashblocksPayloadV1; use std::{io::Read, sync::Arc}; use tokio::sync::mpsc; -use tokio_tungstenite::{connect_async, tungstenite::Message}; use tracing::{debug, error, info}; -use url::Url; + +pub struct FlashblocksOverlayBuilder { + events: mpsc::UnboundedReceiver, + flashblocks_authorizor: VerifyingKey, + cache: FlashblocksCache, +} #[derive(Clone)] pub struct FlashblocksOverlay { - url: Url, cache: FlashblocksCache, } -impl FlashblocksOverlay { - pub fn new(url: Url, chain_spec: Arc) -> Self { +impl FlashblocksOverlayBuilder { + pub fn new( + chain_spec: Arc, + flashblocks_authorizor: VerifyingKey, + events: mpsc::UnboundedReceiver, + ) -> Self { Self { - url, + events, + flashblocks_authorizor, cache: FlashblocksCache::new(chain_spec), } } - pub fn start(&mut self) -> eyre::Result<()> { - let url = self.url.clone(); - let (sender, mut receiver) = mpsc::channel(100); - - tokio::spawn(async move { - let mut backoff = std::time::Duration::from_secs(1); - const MAX_BACKOFF: std::time::Duration = std::time::Duration::from_secs(10); - - loop { - match connect_async(url.as_str()).await { - Ok((ws_stream, _)) => { - info!("WebSocket connection established"); - let (_write, mut read) = ws_stream.split(); - - while let Some(msg) = read.next().await { - debug!("Received message: {:?}", msg); - - match msg { - Ok(Message::Binary(bytes)) => match try_decode_message(&bytes) { - Ok(payload) => { - info!("Received payload: {:?}", payload); - - let _ = sender - .send(InternalMessage::NewPayload(payload)) - .await - .map_err(|e| { - error!("failed to send payload to channel: {}", e); - }); - } - Err(e) => { - error!("failed to parse fb message: {}", e); - } - }, - Ok(Message::Close(e)) => { - error!("WebSocket connection closed: {:?}", e); - break; - } - Err(e) => { - error!("WebSocket connection error: {}", e); - break; - } - _ => {} - } - } - } - Err(e) => { - error!( - "WebSocket connection error, retrying in {:?}: {}", - backoff, e - ); - tokio::time::sleep(backoff).await; - // Double the backoff time, but cap at MAX_BACKOFF - backoff = std::cmp::min(backoff * 2, MAX_BACKOFF); - continue; - } - } - } - }); - + pub fn start(mut self) -> eyre::Result { let cache_cloned = self.cache.clone(); + let overlay = FlashblocksOverlay { + cache: self.cache.clone(), + }; tokio::spawn(async move { - while let Some(message) = receiver.recv().await { + while let Some(message) = self.events.recv().await { match message { - InternalMessage::NewPayload(payload) => { - if let Err(e) = cache_cloned.process_payload(payload) { - error!("failed to process payload: {}", e); + FlashblocksP2PEvent::Established { .. } => todo!(), + FlashblocksP2PEvent::FlashblocksPayloadV1(authorized) => { + match authorized.verify(self.flashblocks_authorizor) { + Ok(_) => { + if let Err(e) = cache_cloned.process_payload(authorized.payload) { + error!("failed to process payload: {}", e); + } + } + Err(e) => { + error!("{e:?}"); + } } } } } }); - Ok(()) + Ok(overlay) } pub fn process_payload(&self, payload: FlashblocksPayloadV1) -> eyre::Result<()> { diff --git a/crates/rollup-boost/src/flashblocks/args.rs b/crates/rollup-boost/src/flashblocks/args.rs index 0bfdc080..913662b8 100644 --- a/crates/rollup-boost/src/flashblocks/args.rs +++ b/crates/rollup-boost/src/flashblocks/args.rs @@ -58,13 +58,13 @@ pub struct FlashblocksArgs { pub flashblocks_builder_vk: VerifyingKey, } -fn parse_sk(s: &str) -> eyre::Result { +pub fn parse_sk(s: &str) -> eyre::Result { let bytes = <[u8; 32]>::from_hex(s.trim()).context("failed parsing flashblocks_authorization_sk")?; Ok(SigningKey::from_bytes(&bytes)) } -fn parse_vk(s: &str) -> eyre::Result { +pub fn parse_vk(s: &str) -> eyre::Result { let bytes = <[u8; 32]>::from_hex(s.trim()).context("failed parsing flashblocks_builder_vk")?; Ok(VerifyingKey::from_bytes(&bytes)?) } From 36201bacbd84358b90b73c27f17a85834e7258aa Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 10 Jul 2025 08:44:01 -0700 Subject: [PATCH 08/45] FlashblocksNetworkBuilder --- Cargo.lock | 5 +- Cargo.toml | 4 + crates/flashblocks-p2p/Cargo.toml | 5 +- .../flashblocks-p2p/src/connection/handler.rs | 9 +- crates/flashblocks-p2p/src/connection/mod.rs | 9 +- crates/flashblocks-p2p/src/lib.rs | 1 + crates/flashblocks-p2p/src/lib.rs.bak | 187 ------------------ crates/flashblocks-p2p/src/net/mod.rs | 56 ++++++ .../flashblocks-p2p/src/protocol/handler.rs | 31 ++- crates/flashblocks-rpc/src/bin/main.rs | 3 +- crates/flashblocks-rpc/src/tests/mod.rs | 8 +- 11 files changed, 113 insertions(+), 205 deletions(-) delete mode 100644 crates/flashblocks-p2p/src/lib.rs.bak create mode 100644 crates/flashblocks-p2p/src/net/mod.rs diff --git a/Cargo.lock b/Cargo.lock index a53bb020..35e032a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3460,11 +3460,14 @@ dependencies = [ "ed25519-dalek", "eyre", "futures", - "rand_core 0.6.4", "reth", "reth-eth-wire", "reth-ethereum", "reth-network", + "reth-node-api", + "reth-node-builder", + "reth-provider", + "reth-transaction-pool", "rollup-boost", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index c00044b2..39433297 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,10 @@ reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-eth-wire = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-network = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } # Alloy libraries alloy-rpc-types-engine = "1.0.13" diff --git a/crates/flashblocks-p2p/Cargo.toml b/crates/flashblocks-p2p/Cargo.toml index 9ff5d59f..1645cbcf 100644 --- a/crates/flashblocks-p2p/Cargo.toml +++ b/crates/flashblocks-p2p/Cargo.toml @@ -9,6 +9,10 @@ reth.workspace = true reth-ethereum = { workspace = true, features = ["node", "network", "cli"] } reth-eth-wire = { workspace = true } reth-network = { workspace = true } +reth-node-api = { workspace = true } +reth-node-builder = { workspace = true } +reth-provider = { workspace = true } +reth-transaction-pool = { workspace = true } # reth-node-builder.workspace = true # reth-optimism-chainspec.workspace = true # reth-optimism-node.workspace = true @@ -26,7 +30,6 @@ reth-network = { workspace = true } ed25519-dalek = { version = "2", features = ["serde"] } -rand_core = "0.6" # for secure RNG blake3 = "1" # fast hashing for payload IDs serde = { version = "1", features = ["derive"] } bincode = "2" # stable, deterministic encoding diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs index 0c551974..48e264f0 100644 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -11,12 +11,13 @@ use tokio::sync::mpsc; use tokio_stream::wrappers::UnboundedReceiverStream; /// The connection handler for the flashblocks RLPx protocol. -pub struct FlashblocksConnectionHandler { +pub struct FlashblocksConnectionHandler { pub state: FlashblocksP2PState, + pub network_handle: N, } -impl ConnectionHandler for FlashblocksConnectionHandler { - type Connection = FlashblocksConnection; +impl ConnectionHandler for FlashblocksConnectionHandler { + type Connection = FlashblocksConnection; fn protocol(&self) -> Protocol { FlashblocksProtoMessage::protocol() @@ -48,8 +49,10 @@ impl ConnectionHandler for FlashblocksConnectionHandler { .ok(); FlashblocksConnection { conn, + peer_id, commands: UnboundedReceiverStream::new(rx), state: self.state, + network_handle: self.network_handle, } } } diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index f9479320..70434dff 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -3,7 +3,7 @@ use crate::protocol::{auth::Authorized, event::FlashblocksP2PEvent, handler::Fla use super::protocol::proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}; use alloy_primitives::bytes::BytesMut; use futures::{Stream, StreamExt}; -use reth_ethereum::network::eth_wire::multiplex::ProtocolConnection; +use reth_ethereum::network::{api::PeerId, eth_wire::multiplex::ProtocolConnection}; use rollup_boost::FlashblocksPayloadV1; use std::{ pin::Pin, @@ -21,13 +21,15 @@ pub enum FlashblocksCommand { }, } -pub struct FlashblocksConnection { +pub struct FlashblocksConnection { conn: ProtocolConnection, + peer_id: PeerId, commands: UnboundedReceiverStream, state: FlashblocksP2PState, + network_handle: N, } -impl Stream for FlashblocksConnection { +impl Stream for FlashblocksConnection { type Item = BytesMut; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -56,7 +58,6 @@ impl Stream for FlashblocksConnection { .events .send(FlashblocksP2PEvent::FlashblocksPayloadV1(payload)) .ok(); - continue; } } } diff --git a/crates/flashblocks-p2p/src/lib.rs b/crates/flashblocks-p2p/src/lib.rs index 6373fd3c..9bdbbc9d 100644 --- a/crates/flashblocks-p2p/src/lib.rs +++ b/crates/flashblocks-p2p/src/lib.rs @@ -1,2 +1,3 @@ pub mod connection; +pub mod net; pub mod protocol; diff --git a/crates/flashblocks-p2p/src/lib.rs.bak b/crates/flashblocks-p2p/src/lib.rs.bak deleted file mode 100644 index 2a13bcbe..00000000 --- a/crates/flashblocks-p2p/src/lib.rs.bak +++ /dev/null @@ -1,187 +0,0 @@ -use reth::{ - chainspec::{EthChainSpec as _, Hardforks}, - network::{ - types::BasicNetworkPrimitives, NetworkConfig, NetworkHandle, NetworkManager, - NetworkPrimitives, - }, -}; -use reth_eth_wire::{ - capability::SharedCapabilities, multiplex::ProtocolConnection, protocol::Protocol, -}; -/// OpNetworkBuilder impls NetworkBuilder -> NetworkHandle impls NetworkProtocols -/// -/// NetworkConfig has `extra_protocols` -/// -// #![cfg_attr(not(any(test, feature = "test")), warn(unused_crate_dependencies))] -// -// pub mod args; -// pub mod node; -// -// #[cfg(any(feature = "test", test))] -// pub mod test_utils; -use reth_network::protocol::{ConnectionHandler, IntoRlpxSubProtocol, ProtocolHandler}; -use reth_node_api::{PrimitivesTy, TxTy}; -use reth_node_builder::{ - components::{ - BasicPayloadServiceBuilder, ComponentsBuilder, ConsensusBuilder, ExecutorBuilder, - NetworkBuilder, PayloadBuilderBuilder, PoolBuilder, PoolBuilderConfigOverrides, - TxPoolBuilder, - }, - node::{FullNodeTypes, NodeTypes}, - rpc::{ - EngineApiBuilder, EngineValidatorAddOn, EngineValidatorBuilder, EthApiBuilder, Identity, - RethRpcAddOns, RethRpcMiddleware, RethRpcServerHandles, RpcAddOns, RpcContext, RpcHandle, - }, - BuilderContext, DebugNode, Node, NodeAdapter, NodeComponentsBuilder, -}; -use reth_provider::{ - BlockReaderIdExt, ChainSpecProvider, ExecutionOutcome, ProviderError, StateProvider, - StateProviderFactory, -}; -use reth_transaction_pool::{ - blobstore::DiskFileBlobStore, EthPoolTransaction, PeerId, PoolPooledTx, PoolTransaction, - TransactionPool, TransactionValidationTaskExecutor, -}; -use tokio::sync::oneshot; -use tracing::info; - -#[derive(Clone, Debug, Default)] -pub struct FlashblocksProtocolHandler; - -#[derive(Clone, Debug, Default)] -pub struct FlashblocksConnectionHandler; - -/// The connection handler for the custom RLPx protocol. -pub struct FlashblocksConnection { - conn: ProtocolConnection, - initial_ping: Option, - commands: UnboundedReceiverStream, - pending_pong: Option>, -} - -impl ConnectionHandler for FlashblocksConnectionHandler { - type Connection; - - fn protocol(&self) -> Protocol { - todo!() - } - - fn on_unsupported_by_peer( - self, - supported: &SharedCapabilities, - direction: reth_network::Direction, - peer_id: PeerId, - ) -> reth_network::protocol::OnNotSupported { - todo!() - } - - fn into_connection( - self, - direction: reth_network::Direction, - peer_id: PeerId, - conn: ProtocolConnection, - ) -> Self::Connection { - todo!() - } -} - -impl ProtocolHandler for FlashblocksProtocolHandler { - type ConnectionHandler = FlashblocksConnectionHandler; - - fn on_incoming(&self, socket_addr: std::net::SocketAddr) -> Option { - todo!() - } - - fn on_outgoing( - &self, - socket_addr: std::net::SocketAddr, - peer_id: PeerId, - ) -> Option { - todo!() - } -} - -#[derive(Clone, Debug, Default)] -pub struct FlashblocksNetworkBuilder { - /// Disable transaction pool gossip - pub disable_txpool_gossip: bool, - /// Disable discovery v4 - pub disable_discovery_v4: bool, -} - -impl FlashblocksNetworkBuilder { - /// Returns the [`NetworkConfig`] that contains the settings to launch the p2p network. - /// - /// This applies the configured [`OpNetworkBuilder`] settings. - pub fn network_config( - &self, - ctx: &BuilderContext, - ) -> eyre::Result> - where - Node: FullNodeTypes>, - NetworkP: NetworkPrimitives, - { - let Self { - disable_txpool_gossip, - disable_discovery_v4, - .. - } = self.clone(); - let args = &ctx.config().network; - let network_builder = ctx - .network_config_builder()? - // apply discovery settings - .apply(|mut builder| { - let rlpx_socket = (args.addr, args.port).into(); - if disable_discovery_v4 || args.discovery.disable_discovery { - builder = builder.disable_discv4_discovery(); - } - if !args.discovery.disable_discovery { - builder = builder.discovery_v5( - args.discovery.discovery_v5_builder( - rlpx_socket, - ctx.config() - .network - .resolved_bootnodes() - .or_else(|| ctx.chain_spec().bootnodes()) - .unwrap_or_default(), - ), - ); - } - - builder - }); - - let mut network_config = ctx.build_network_config(network_builder); - - // When `sequencer_endpoint` is configured, the node will forward all transactions to a - // Sequencer node for execution and inclusion on L1, and disable its own txpool - // gossip to prevent other parties in the network from learning about them. - network_config.tx_gossip_disabled = disable_txpool_gossip; - - Ok(network_config) - } -} - -impl NetworkBuilder for FlashblocksNetworkBuilder -where - Node: FullNodeTypes>, - Pool: TransactionPool>> - + Unpin - + 'static, -{ - type Network = - NetworkHandle, PoolPooledTx>>; - - async fn build_network( - self, - ctx: &BuilderContext, - pool: Pool, - ) -> eyre::Result { - let network_config = self.network_config(ctx)?; - let network = NetworkManager::builder(network_config).await?; - let handle = ctx.start_network(network, pool); - // info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized"); - - Ok(handle) - } -} diff --git a/crates/flashblocks-p2p/src/net/mod.rs b/crates/flashblocks-p2p/src/net/mod.rs new file mode 100644 index 00000000..ec9861ed --- /dev/null +++ b/crates/flashblocks-p2p/src/net/mod.rs @@ -0,0 +1,56 @@ +use reth::chainspec::Hardforks; +use reth_eth_wire::NetPrimitivesFor; +use reth_ethereum::network::api::FullNetwork; +use reth_network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; +use reth_node_api::{PrimitivesTy, TxTy}; +use reth_node_builder::{ + BuilderContext, + components::NetworkBuilder, + node::{FullNodeTypes, NodeTypes}, +}; +use reth_transaction_pool::{PoolTransaction, TransactionPool}; +use tokio::sync::mpsc; + +use crate::protocol::{ + event::FlashblocksP2PEvent, + handler::{FlashblocksP2PNetworHandle, FlashblocksProtoHandler}, +}; + +#[derive(Clone, Debug)] +pub struct FlashblocksNetworkBuilder { + inner: T, + events: mpsc::UnboundedSender, +} + +impl FlashblocksNetworkBuilder { + /// Creates a new `FlashblocksNetworkBuilder` with the given inner builder and events channel. + pub fn new(inner: T, events: mpsc::UnboundedSender) -> Self { + Self { inner, events } + } +} + +impl NetworkBuilder for FlashblocksNetworkBuilder +where + T: NetworkBuilder, + Node: FullNodeTypes>, + Pool: TransactionPool>> + + Unpin + + 'static, + Network: FlashblocksP2PNetworHandle + + NetworkProtocols + + FullNetwork>>, +{ + type Network = T::Network; + + async fn build_network( + self, + ctx: &BuilderContext, + pool: Pool, + ) -> eyre::Result { + let handle = self.inner.build_network(ctx, pool).await?; + let handler = FlashblocksProtoHandler::::new(handle.clone(), self.events); + handle.add_rlpx_sub_protocol(handler.into_rlpx_sub_protocol()); + + Ok(handle) + } +} diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 1203a27b..692fb87c 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -1,9 +1,17 @@ use super::event::FlashblocksP2PEvent; use crate::connection::handler::FlashblocksConnectionHandler; use reth_ethereum::network::{api::PeerId, protocol::ProtocolHandler}; +use reth_network::Peers; use std::net::SocketAddr; use tokio::sync::mpsc; +pub(crate) trait FlashblocksP2PNetworHandle: + Clone + Unpin + Peers + std::fmt::Debug + 'static +{ +} + +impl FlashblocksP2PNetworHandle for N {} + /// Protocol state is an helper struct to store the protocol events. #[derive(Clone, Debug)] pub struct FlashblocksP2PState { @@ -12,16 +20,28 @@ pub struct FlashblocksP2PState { /// The protocol handler takes care of incoming and outgoing connections. #[derive(Debug)] -pub struct FlashblocksProtoHandler { +pub struct FlashblocksProtoHandler { pub state: FlashblocksP2PState, + pub network_handle: N, +} + +impl FlashblocksProtoHandler { + /// Creates a new protocol handler with the given state. + pub fn new(network_handle: N, events: mpsc::UnboundedSender) -> Self { + Self { + state: FlashblocksP2PState { events }, + network_handle, + } + } } -impl ProtocolHandler for FlashblocksProtoHandler { - type ConnectionHandler = FlashblocksConnectionHandler; +impl ProtocolHandler for FlashblocksProtoHandler { + type ConnectionHandler = FlashblocksConnectionHandler; fn on_incoming(&self, _socket_addr: SocketAddr) -> Option { - Some(FlashblocksConnectionHandler { + Some(FlashblocksConnectionHandler:: { state: self.state.clone(), + network_handle: self.network_handle.clone(), }) } @@ -30,8 +50,9 @@ impl ProtocolHandler for FlashblocksProtoHandler { _socket_addr: SocketAddr, _peer_id: PeerId, ) -> Option { - Some(FlashblocksConnectionHandler { + Some(FlashblocksConnectionHandler:: { state: self.state.clone(), + network_handle: self.network_handle.clone(), }) } } diff --git a/crates/flashblocks-rpc/src/bin/main.rs b/crates/flashblocks-rpc/src/bin/main.rs index f41a9c85..89eb3548 100644 --- a/crates/flashblocks-rpc/src/bin/main.rs +++ b/crates/flashblocks-rpc/src/bin/main.rs @@ -3,7 +3,7 @@ use clap::Parser; use ed25519_dalek::VerifyingKey; use flashblocks_p2p::protocol::handler::{FlashblocksP2PState, FlashblocksProtoHandler}; -use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, FlashblocksOverlayBuilder}; +use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlayBuilder}; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; use reth_optimism_node::{OpNode, args::RollupArgs}; @@ -59,6 +59,7 @@ pub fn main() { let custom_rlpx_handler = FlashblocksProtoHandler { + network_handle: handle.node.network.clone(), state: FlashblocksP2PState { events: tx }, }; diff --git a/crates/flashblocks-rpc/src/tests/mod.rs b/crates/flashblocks-rpc/src/tests/mod.rs index cc4e99ed..d3ed4227 100644 --- a/crates/flashblocks-rpc/src/tests/mod.rs +++ b/crates/flashblocks-rpc/src/tests/mod.rs @@ -1,6 +1,9 @@ #[cfg(test)] mod tests { - use crate::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, cache::Metadata}; + use crate::{ + EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, FlashblocksOverlayBuilder, + cache::Metadata, + }; use alloy_consensus::Receipt; use alloy_genesis::Genesis; use alloy_primitives::{Address, B256, Bytes, TxHash, U256, address, b256}; @@ -101,8 +104,7 @@ mod tests { .extend_rpc_modules(move |ctx| { // We are not going to use the websocket connection to send payloads so we use // a dummy url. - let flashblocks_overlay = - FlashblocksOverlay::new(Url::parse("ws://localhost:8546")?, chain_spec); + let flashblocks_overlay = FlashblocksOverlayBuilder::new(chain_spec); let eth_api = ctx.registry.eth_api().clone(); let api_ext = FlashblocksApiExt::new(eth_api.clone(), flashblocks_overlay.clone()); From 0dc7064327b1c9b32b0a5874aa6e1d36802cccf0 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 10 Jul 2025 10:02:33 -0700 Subject: [PATCH 09/45] Pull out flashblocks-node --- Cargo.lock | 86 +++++ Cargo.toml | 3 + crates/flashblocks-node/Cargo.toml | 66 ++++ .../src/bin => flashblocks-node/src}/main.rs | 0 .../src/tests/assets/genesis.json | 100 ++++++ crates/flashblocks-node/src/tests/mod.rs | 316 ++++++++++++++++++ crates/flashblocks-rpc/Cargo.toml | 7 - 7 files changed, 571 insertions(+), 7 deletions(-) create mode 100644 crates/flashblocks-node/Cargo.toml rename crates/{flashblocks-rpc/src/bin => flashblocks-node/src}/main.rs (100%) create mode 100644 crates/flashblocks-node/src/tests/assets/genesis.json create mode 100644 crates/flashblocks-node/src/tests/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 35e032a1..9cc86f46 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3449,6 +3449,92 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "flashblocks-api" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "bincode 2.0.1", + "blake3", + "clap", + "ed25519-dalek", + "eyre", + "futures", + "reth", + "reth-eth-wire", + "reth-ethereum", + "reth-network", + "reth-node-api", + "reth-node-builder", + "reth-provider", + "reth-transaction-pool", + "rollup-boost", + "serde", + "serde_json", + "thiserror 2.0.12", + "tokio", + "tokio-stream", + "tracing", +] + +[[package]] +name = "flashblocks-node" +version = "0.1.0" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-genesis", + "alloy-json-rpc", + "alloy-primitives", + "alloy-provider", + "alloy-rpc-client", + "alloy-rpc-types", + "alloy-rpc-types-engine", + "alloy-rpc-types-eth", + "brotli", + "clap", + "ed25519-dalek", + "eyre", + "flashblocks-p2p", + "flashblocks-rpc", + "futures-util", + "jsonrpsee 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)", + "metrics", + "metrics-derive", + "op-alloy-consensus 0.18.9", + "op-alloy-network", + "op-alloy-rpc-types", + "reth-db", + "reth-e2e-test-utils", + "reth-eth-wire", + "reth-ethereum", + "reth-network", + "reth-node-api", + "reth-node-builder", + "reth-node-core", + "reth-optimism-chainspec", + "reth-optimism-cli", + "reth-optimism-evm", + "reth-optimism-forks", + "reth-optimism-node", + "reth-optimism-primitives", + "reth-optimism-rpc", + "reth-primitives", + "reth-primitives-traits", + "reth-provider", + "reth-rpc-eth-api", + "reth-rpc-server-types", + "reth-tasks", + "reth-tracing", + "rollup-boost", + "serde", + "serde_json", + "tokio", + "tokio-tungstenite", + "tracing", + "url", +] + [[package]] name = "flashblocks-p2p" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 39433297..f03a9fe1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,11 +8,14 @@ members = [ "crates/websocket-proxy", "crates/flashblocks-rpc", "crates/flashblocks-p2p", + "crates/flashblocks-api", + "crates/flashblocks-node", ] [workspace.dependencies] rollup-boost = { path = "crates/rollup-boost" } flashblocks-p2p = { path = "crates/flashblocks-p2p" } +flashblocks-rpc = { path = "crates/flashblocks-rpc" } tracing = "0.1.4" tracing-subscriber = { version = "0.3.11", features = ["env-filter", "json"] } diff --git a/crates/flashblocks-node/Cargo.toml b/crates/flashblocks-node/Cargo.toml new file mode 100644 index 00000000..983f41ae --- /dev/null +++ b/crates/flashblocks-node/Cargo.toml @@ -0,0 +1,66 @@ +[package] +name = "flashblocks-node" +version = "0.1.0" +edition = "2024" +license = "MIT" + +[dependencies] +rollup-boost.workspace = true +flashblocks-p2p.workspace = true +flashblocks-rpc.workspace = true + +reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-optimism-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0", features = [ + "test-utils", +] } +reth-e2e-test-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-tasks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } + +reth-ethereum = { workspace = true, features = ["node", "network", "cli"] } +reth-eth-wire = { workspace = true } +reth-network = { workspace = true } +ed25519-dalek = { version = "2", features = ["serde"] } + +alloy-eips.workspace = true +alloy-primitives.workspace = true +alloy-json-rpc.workspace = true +alloy-rpc-types.workspace = true +alloy-rpc-types-engine.workspace = true +alloy-rpc-types-eth.workspace = true +alloy-consensus.workspace = true +alloy-genesis.workspace = true +alloy-rpc-client.workspace = true +alloy-provider.workspace = true +op-alloy-network.workspace = true +op-alloy-consensus.workspace = true +op-alloy-rpc-types.workspace = true + +tokio.workspace = true +tokio-tungstenite.workspace = true +url.workspace = true +tracing.workspace = true +clap.workspace = true +eyre.workspace = true +metrics.workspace = true +metrics-derive.workspace = true +serde_json.workspace = true +serde.workspace = true + +jsonrpsee = { version = "0.25.1" } +futures-util = "0.3.31" +brotli = "8.0.1" diff --git a/crates/flashblocks-rpc/src/bin/main.rs b/crates/flashblocks-node/src/main.rs similarity index 100% rename from crates/flashblocks-rpc/src/bin/main.rs rename to crates/flashblocks-node/src/main.rs diff --git a/crates/flashblocks-node/src/tests/assets/genesis.json b/crates/flashblocks-node/src/tests/assets/genesis.json new file mode 100644 index 00000000..4d703497 --- /dev/null +++ b/crates/flashblocks-node/src/tests/assets/genesis.json @@ -0,0 +1,100 @@ +{ + "config": { + "chainId": 8453, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "mergeNetsplitBlock": 0, + "bedrockBlock": 0, + "regolithTime": 0, + "terminalTotalDifficulty": 0, + "terminalTotalDifficultyPassed": true, + "optimism": { + "eip1559Elasticity": 6, + "eip1559Denominator": 50 + } + }, + "nonce": "0x0", + "timestamp": "0x0", + "extraData": "0x00", + "gasLimit": "0x1c9c380", + "difficulty": "0x0", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "0x14dc79964da2c08b23698b3d3cc7ca32193d9955": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x1cbd3b2770909d4e10f157cabc84c7264073c9ec": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x2546bcd3c84621e976d8185a91a922ae77ecec30": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x70997970c51812dc3a010c7d01b50e0d17dc79c8": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x71be63f3384f5fb98995898a86b02fb2426c5788": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x8626f6940e2eb28930efb4cef49b2d1f2c9c1199": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x90f79bf6eb2c4f870365e785982e1f101e93b906": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x976ea74026e726554db657fa54763abd0c3a0aa9": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc": { + "balance": "0xd3c21bcecceda1000000" + }, + "0x9c41de96b2088cdc640c6182dfcf5491dc574a57": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xa0ee7a142d267c1f36714e4a8f75612f20a79720": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xbcd4042de499d14e55001ccbb24a551f3b954096": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xbda5747bfd65f08deb54cb465eb87d40e51b197e": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xcd3b766ccdd6ae721141f452c550ca635964ce71": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xdd2fd4581271e230360230f9337d5c0430bf44c0": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xdf3e18d64bc6a983f673ab319ccae4f1a57c7097": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { + "balance": "0xd3c21bcecceda1000000" + }, + "0xfabb0ac9d68b0b445fb7357272ff202c5651694a": { + "balance": "0xd3c21bcecceda1000000" + } + }, + "number": "0x0" +} \ No newline at end of file diff --git a/crates/flashblocks-node/src/tests/mod.rs b/crates/flashblocks-node/src/tests/mod.rs new file mode 100644 index 00000000..d3ed4227 --- /dev/null +++ b/crates/flashblocks-node/src/tests/mod.rs @@ -0,0 +1,316 @@ +#[cfg(test)] +mod tests { + use crate::{ + EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, FlashblocksOverlayBuilder, + cache::Metadata, + }; + use alloy_consensus::Receipt; + use alloy_genesis::Genesis; + use alloy_primitives::{Address, B256, Bytes, TxHash, U256, address, b256}; + use alloy_provider::{Provider, RootProvider}; + use alloy_rpc_client::RpcClient; + use alloy_rpc_types_engine::PayloadId; + use reth_node_builder::{Node, NodeBuilder, NodeConfig, NodeHandle}; + use reth_node_core::{ + args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}, + exit::NodeExitFuture, + }; + use reth_optimism_chainspec::OpChainSpecBuilder; + use reth_optimism_node::{OpNode, args::RollupArgs}; + use reth_optimism_primitives::OpReceipt; + use reth_provider::providers::BlockchainProvider; + use reth_tasks::TaskManager; + use rollup_boost::{ + ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, FlashblocksPayloadV1, + }; + use std::{any::Any, collections::HashMap, net::SocketAddr, str::FromStr, sync::Arc}; + use tokio::sync::{mpsc, oneshot}; + use url::Url; + + pub struct NodeContext { + sender: mpsc::Sender<(FlashblocksPayloadV1, oneshot::Sender<()>)>, + http_api_addr: SocketAddr, + _node_exit_future: NodeExitFuture, + _node: Box, + _task_manager: TaskManager, + } + + impl NodeContext { + pub async fn send_payload(&self, payload: FlashblocksPayloadV1) -> eyre::Result<()> { + let (tx, rx) = oneshot::channel(); + self.sender.send((payload, tx)).await?; + rx.await?; + Ok(()) + } + + pub async fn provider(&self) -> eyre::Result { + let url = format!("http://{}", self.http_api_addr); + let client = RpcClient::builder().http(url.parse()?); + + Ok(RootProvider::new(client)) + } + + pub async fn send_test_payloads(&self) -> eyre::Result<()> { + let base_payload = create_first_payload(); + self.send_payload(base_payload).await?; + + let second_payload = create_second_payload(); + self.send_payload(second_payload).await?; + + Ok(()) + } + } + + async fn setup_node() -> eyre::Result { + let tasks = TaskManager::current(); + let exec = tasks.executor(); + + let genesis: Genesis = serde_json::from_str(include_str!("assets/genesis.json")).unwrap(); + let chain_spec = Arc::new( + OpChainSpecBuilder::base_mainnet() + .genesis(genesis) + .ecotone_activated() + .build(), + ); + + let network_config = NetworkArgs { + discovery: DiscoveryArgs { + disable_discovery: true, + ..DiscoveryArgs::default() + }, + ..NetworkArgs::default() + }; + + // Use with_unused_ports() to let Reth allocate random ports and avoid port collisions + let node_config = NodeConfig::new(chain_spec.clone()) + .with_network(network_config.clone()) + .with_rpc(RpcServerArgs::default().with_unused_ports().with_http()) + .with_unused_ports(); + + let node = OpNode::new(RollupArgs::default()); + + // Start websocket server to simulate the builder and send payloads back to the node + let (sender, mut receiver) = + mpsc::channel::<(FlashblocksPayloadV1, oneshot::Sender<()>)>(100); + + let NodeHandle { + node, + node_exit_future, + } = NodeBuilder::new(node_config.clone()) + .testing_node(exec.clone()) + .with_types_and_provider::>() + .with_components(node.components_builder()) + .with_add_ons(node.add_ons()) + .extend_rpc_modules(move |ctx| { + // We are not going to use the websocket connection to send payloads so we use + // a dummy url. + let flashblocks_overlay = FlashblocksOverlayBuilder::new(chain_spec); + + let eth_api = ctx.registry.eth_api().clone(); + let api_ext = FlashblocksApiExt::new(eth_api.clone(), flashblocks_overlay.clone()); + + ctx.modules.replace_configured(api_ext.into_rpc())?; + + tokio::spawn(async move { + while let Some((payload, tx)) = receiver.recv().await { + flashblocks_overlay.process_payload(payload).unwrap(); + tx.send(()).unwrap(); + } + }); + + Ok(()) + }) + .launch() + .await?; + + let http_api_addr = node + .rpc_server_handle() + .http_local_addr() + .ok_or_else(|| eyre::eyre!("Failed to get http api address"))?; + + Ok(NodeContext { + sender, + http_api_addr, + _node_exit_future: node_exit_future, + _node: Box::new(node), + _task_manager: tasks, + }) + } + + fn create_first_payload() -> FlashblocksPayloadV1 { + FlashblocksPayloadV1 { + payload_id: PayloadId::new([0; 8]), + index: 0, + base: Some(ExecutionPayloadBaseV1 { + parent_beacon_block_root: B256::default(), + parent_hash: B256::default(), + fee_recipient: Address::ZERO, + prev_randao: B256::default(), + block_number: 1, + gas_limit: 0, + timestamp: 0, + extra_data: Bytes::new(), + base_fee_per_gas: U256::ZERO, + }), + diff: ExecutionPayloadFlashblockDeltaV1::default(), + metadata: serde_json::to_value(Metadata { + block_number: 1, + receipts: HashMap::default(), + new_account_balances: HashMap::default(), + }) + .unwrap(), + } + } + + const TEST_ADDRESS: Address = address!("0x1234567890123456789012345678901234567890"); + const PENDING_BALANCE: u64 = 4600; + + const TX1_HASH: TxHash = + b256!("0x2be2e6f8b01b03b87ae9f0ebca8bbd420f174bef0fbcc18c7802c5378b78f548"); + const TX2_HASH: TxHash = + b256!("0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8"); + + fn create_second_payload() -> FlashblocksPayloadV1 { + // Create second payload (index 1) with transactions + // tx1 hash: 0x2be2e6f8b01b03b87ae9f0ebca8bbd420f174bef0fbcc18c7802c5378b78f548 (deposit transaction) + // tx2 hash: 0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8 + let tx1 = Bytes::from_str("0x7ef8f8a042a8ae5ec231af3d0f90f68543ec8bca1da4f7edd712d5b51b490688355a6db794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e200000044d000a118b00000000000000040000000067cb7cb0000000000077dbd4000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000014edd27304108914dd6503b19b9eeb9956982ef197febbeeed8a9eac3dbaaabdf000000000000000000000000fc56e7272eebbba5bc6c544e159483c4a38f8ba3").unwrap(); + let tx2 = Bytes::from_str("0xf8cd82016d8316e5708302c01c94f39635f2adf40608255779ff742afe13de31f57780b8646e530e9700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000156ddc81eed2a36d68302948ba0a608703e79b22164f74523d188a11f81c25a65dd59535bab1cd1d8b30d115f3ea07f4cfbbad77a139c9209d3bded89091867ff6b548dd714109c61d1f8e7a84d14").unwrap(); + + // Send another test flashblock payload + let payload = FlashblocksPayloadV1 { + payload_id: PayloadId::new([0; 8]), + index: 1, + base: None, + diff: ExecutionPayloadFlashblockDeltaV1 { + state_root: B256::default(), + receipts_root: B256::default(), + gas_used: 0, + block_hash: B256::default(), + transactions: vec![tx1, tx2], + withdrawals: Vec::new(), + logs_bloom: Default::default(), + withdrawals_root: Default::default(), + }, + metadata: serde_json::to_value(Metadata { + block_number: 1, + receipts: { + let mut receipts = HashMap::default(); + receipts.insert( + TX1_HASH.to_string(), // transaction hash as string + OpReceipt::Legacy(Receipt { + status: true.into(), + cumulative_gas_used: 21000, + logs: vec![], + }), + ); + receipts.insert( + TX2_HASH.to_string(), // transaction hash as string + OpReceipt::Legacy(Receipt { + status: true.into(), + cumulative_gas_used: 45000, + logs: vec![], + }), + ); + receipts + }, + new_account_balances: { + let mut map = HashMap::default(); + map.insert( + TEST_ADDRESS.to_string(), + format!("0x{:x}", U256::from(PENDING_BALANCE)), + ); + map + }, + }) + .unwrap(), + }; + + payload + } + + #[tokio::test] + async fn test_get_block_by_number_pending() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + let node = setup_node().await?; + let provider = node.provider().await?; + + let latest_block = provider + .get_block_by_number(alloy_eips::BlockNumberOrTag::Latest) + .await? + .expect("latest block expected"); + assert_eq!(latest_block.number(), 0); + + // Querying pending block when it does not exists yet + let pending_block = provider + .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) + .await?; + assert_eq!(pending_block.is_none(), true); + + let base_payload = create_first_payload(); + node.send_payload(base_payload).await?; + + // Query pending block after sending the base payload with an empty delta + let pending_block = provider + .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) + .await? + .expect("pending block expected"); + + assert_eq!(pending_block.number(), 1); + assert_eq!(pending_block.transactions.hashes().len(), 0); + + let second_payload = create_second_payload(); + node.send_payload(second_payload).await?; + + // Query pending block after sending the second payload with two transactions + let block = provider + .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) + .await? + .expect("pending block expected"); + + assert_eq!(block.number(), 1); + assert_eq!(block.transactions.hashes().len(), 2); + + Ok(()) + } + + #[tokio::test] + async fn test_get_balance_pending() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + let node = setup_node().await?; + let provider = node.provider().await?; + + node.send_test_payloads().await?; + + let balance = provider.get_balance(TEST_ADDRESS).await?; + assert_eq!(balance, U256::ZERO); + + let pending_balance = provider.get_balance(TEST_ADDRESS).pending().await?; + assert_eq!(pending_balance, U256::from(PENDING_BALANCE)); + + Ok(()) + } + + #[tokio::test] + async fn test_get_transaction_receipt_pending() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + let node = setup_node().await?; + let provider = node.provider().await?; + + let receipt = provider.get_transaction_receipt(TX1_HASH).await?; + assert_eq!(receipt.is_none(), true); + + node.send_test_payloads().await?; + + let receipt = provider + .get_transaction_receipt(TX1_HASH) + .await? + .expect("receipt expected"); + assert_eq!(receipt.gas_used, 21000); + + // TODO: Add a new payload and validate that the receipts from the previous payload + // are not returned. + + Ok(()) + } +} diff --git a/crates/flashblocks-rpc/Cargo.toml b/crates/flashblocks-rpc/Cargo.toml index 51525cdc..0e30b946 100644 --- a/crates/flashblocks-rpc/Cargo.toml +++ b/crates/flashblocks-rpc/Cargo.toml @@ -63,10 +63,3 @@ serde.workspace = true jsonrpsee = { version = "0.25.1" } futures-util = "0.3.31" brotli = "8.0.1" - -[[bin]] -name = "flashblocks-rpc" -path = "src/bin/main.rs" - -[lib] -path = "src/lib.rs" From e94925ec66107c284b0d8a313533fb1904c1f2ad Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 10 Jul 2025 10:55:40 -0700 Subject: [PATCH 10/45] add timestamp to Authorization --- crates/flashblocks-p2p/src/protocol/handler.rs | 3 +++ crates/rollup-boost/src/client/rpc.rs | 7 ++++++- crates/rollup-boost/src/server.rs | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 692fb87c..fedd14bc 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -1,7 +1,10 @@ use super::event::FlashblocksP2PEvent; +use crate::connection::FlashblocksCommand; use crate::connection::handler::FlashblocksConnectionHandler; +use crate::protocol::auth::Authorized; use reth_ethereum::network::{api::PeerId, protocol::ProtocolHandler}; use reth_network::Peers; +use rollup_boost::FlashblocksPayloadV1; use std::net::SocketAddr; use tokio::sync::mpsc; diff --git a/crates/rollup-boost/src/client/rpc.rs b/crates/rollup-boost/src/client/rpc.rs index d1ef6843..b8b10a20 100644 --- a/crates/rollup-boost/src/client/rpc.rs +++ b/crates/rollup-boost/src/client/rpc.rs @@ -172,7 +172,12 @@ impl RpcClient { ) { (Some(attrs), Some(sk), Some(pk)) => { let payload_id = payload_id_optimism(&fork_choice_state.head_block_hash, attrs, 3); - let authorization = Authorization::new(sk, pk.clone(), payload_id); + let authorization = Authorization::new( + payload_id, + attrs.payload_attributes.timestamp, + sk, + pk.clone(), + ); self.auth_client .fork_choice_updated_flashblocks_v1( fork_choice_state, diff --git a/crates/rollup-boost/src/server.rs b/crates/rollup-boost/src/server.rs index 12dd91ab..1ae8f7c4 100644 --- a/crates/rollup-boost/src/server.rs +++ b/crates/rollup-boost/src/server.rs @@ -290,23 +290,27 @@ where #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Authorization { pub payload_id: PayloadId, + pub timestamp: u64, pub builder_pub: VerifyingKey, pub authorizer_sig: Signature, } impl Authorization { pub fn new( + payload_id: PayloadId, + timestamp: u64, authorizer_sk: &SigningKey, builder_pub: VerifyingKey, - payload_id: PayloadId, ) -> Self { let mut msg = payload_id.0.to_vec(); + msg.extend_from_slice(×tamp.to_le_bytes()); msg.extend_from_slice(builder_pub.as_bytes()); let hash = blake3::hash(&msg); let sig = authorizer_sk.sign(hash.as_bytes()); Self { payload_id, + timestamp, builder_pub, authorizer_sig: sig, } @@ -314,6 +318,7 @@ impl Authorization { pub fn verify(&self, authorizer_pub: VerifyingKey) -> Result<(), FlashblocksP2PError> { let mut msg = self.payload_id.0.to_vec(); + msg.extend_from_slice(&self.timestamp.to_le_bytes()); msg.extend_from_slice(self.builder_pub.as_bytes()); let hash = blake3::hash(&msg); authorizer_pub From dc15ab18266e67d76657d94134c2f95a65b37362 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 11 Jul 2025 11:50:55 -0700 Subject: [PATCH 11/45] wip --- .../flashblocks-p2p/src/protocol/handler.rs | 144 ++++++++++++++++-- 1 file changed, 130 insertions(+), 14 deletions(-) diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index fedd14bc..2794d31c 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -2,10 +2,13 @@ use super::event::FlashblocksP2PEvent; use crate::connection::FlashblocksCommand; use crate::connection::handler::FlashblocksConnectionHandler; use crate::protocol::auth::Authorized; +use ed25519_dalek::VerifyingKey; +use reth::payload::PayloadId; use reth_ethereum::network::{api::PeerId, protocol::ProtocolHandler}; use reth_network::Peers; use rollup_boost::FlashblocksPayloadV1; use std::net::SocketAddr; +use tokio::sync::broadcast; use tokio::sync::mpsc; pub(crate) trait FlashblocksP2PNetworHandle: @@ -16,25 +19,140 @@ pub(crate) trait FlashblocksP2PNetworHandle: impl FlashblocksP2PNetworHandle for N {} /// Protocol state is an helper struct to store the protocol events. -#[derive(Clone, Debug)] -pub struct FlashblocksP2PState { - pub events: mpsc::UnboundedSender, +#[derive(Debug)] +pub struct FlashblocksP2PState { + /// Networkd handle, used to update peer state. + pub network_handle: N, + /// Authorizer verifying, used to verify flashblocks payloads. + pub authorizer_vk: VerifyingKey, + /// Sender of verified and strictly ordered flashbloacks payloads. + /// For consumption by the rpc overlay. + pub flashblock_stream: broadcast::Sender, + /// Sender for newly received and validated flashblocks payloads + /// which will be broadcasted to all peers. May not be strictly ordered. + pub broadcast_tx: broadcast::Sender, + /// Verified flashblock payloads received by peers. + /// May not be strictly ordered. + pub inbound_rx: mpsc::UnboundedReceiver, + /// The index of the next flashblock to emit. + pub flashblock_index: usize, + /// Timestamp of the most recent flashblocks payload. + pub payload_timestamp: u64, + /// Most recent payload id. + pub payload_id: PayloadId, + /// Buffer of flashblocks for the current payload. + pub flashblocks: Vec>, +} + +impl FlashblocksP2PState { + pub fn run(mut self) { + tokio::spawn(async move { + while let Some(event) = self.inbound_rx.recv().await { + match event { + FlashblocksCommand::FlashblocksPayloadV1 { payload } => { + // TODO: might make sense to perform verification in a separate task + if let Err(e) = payload.verify(self.authorizer_vk) { + tracing::warn!( + "Failed to verify flashblocks payload: {:?}, error: {}", + payload, + e + ); + // TODO: ban peer + continue; + } + if payload.authorization.timestamp < self.payload_timestamp { + tracing::warn!( + "Received flashblocks payload with outdated timestamp: {}", + payload.authorization.timestamp + ); + // TODO: handle peer + continue; + } + // Check if this is a new payload + if payload.authorization.timestamp > self.payload_timestamp { + self.flashblock_index = 0; + self.payload_timestamp = payload.authorization.timestamp; + self.payload_id = payload.payload.payload_id; + self.flashblocks.clear(); + } + // If we've already seen this index, skip it + // Otherwise, add it to the list + // TODO: perhaps check max index + self.flashblocks + .resize_with(payload.payload.index as usize + 1, || None); + let flashblock = &mut self.flashblocks[payload.payload.index as usize]; + if flashblock.is_none() { + // We haven't seen this index yet + // Add the flashblock to our cache + *flashblock = + Some(FlashblocksP2PEvent::FlashblocksPayloadV1(payload.clone())); + tracing::debug!( + "Received flashblocks payload with id: {}, index: {}", + payload.payload.payload_id, + payload.payload.index + ); + // Broadcast the flashblock to all peers, possible our of order + self.broadcast_tx.send(payload).ok(); + // Broadcast any flashblocks in the cache that are in order + // for i in self.flashblock_index..self.flashblocks.len() { + // if let Some(flashblock_event) = &self.flashblocks[i] { + // // Send the flashblock to the stream + // self.flashblock_stream.send(flashblock_event.clone()).ok(); + // // Update the index + // self.flashblock_index = i + 1; + // } else { + // // No more flashblocks in order, break + // break; + // } + // } + while let Some(Some(flashblock_event)) = + self.flashblocks.get(self.flashblock_index) + { + // Send the flashblock to the stream + self.flashblock_stream.send(flashblock_event).ok(); + // Update the index + self.flashblock_index += 1; + } + } + } + } + } + }); + } } /// The protocol handler takes care of incoming and outgoing connections. #[derive(Debug)] -pub struct FlashblocksProtoHandler { - pub state: FlashblocksP2PState, - pub network_handle: N, +pub struct FlashblocksProtoHandler { + /// Sender of verified and strictly ordered flashbloacks payloads. + /// For consumption by the rpc overlay. + pub flashblock_stream: mpsc::UnboundedSender, + /// Sender for newly received and validated flashblocks payloads + /// which will be broadcasted to all peers. May not be strictly ordered. + pub broadcast_tx: broadcast::Sender, + /// Verified flashblock payloads received by peers. + /// May not be strictly ordered. + pub inbound_rx: mpsc::UnboundedReceiver, } -impl FlashblocksProtoHandler { +impl FlashblocksProtoHandler { /// Creates a new protocol handler with the given state. - pub fn new(network_handle: N, events: mpsc::UnboundedSender) -> Self { - Self { - state: FlashblocksP2PState { events }, - network_handle, - } + pub fn new( + network_handle: N, + flashblock_stream: broadcast::Sender, + ) -> Self { + let (broadcast_tx, broadcast_rx) = broadcast::channel(100); + let (inbound_tx, inbound_rx) = mpsc::unbounded_channel(); + let state = FlashblocksP2PState { + flashblock_stream, + broadcast_tx, + inbound_rx, + payload_timestamp: 0, + payload_id: PayloadId::default(), + flashblocks: vec![], + }; + state.run(); + Self { network_handle } } } @@ -43,7 +161,6 @@ impl ProtocolHandler for FlashblocksProtoHandler< fn on_incoming(&self, _socket_addr: SocketAddr) -> Option { Some(FlashblocksConnectionHandler:: { - state: self.state.clone(), network_handle: self.network_handle.clone(), }) } @@ -54,7 +171,6 @@ impl ProtocolHandler for FlashblocksProtoHandler< _peer_id: PeerId, ) -> Option { Some(FlashblocksConnectionHandler:: { - state: self.state.clone(), network_handle: self.network_handle.clone(), }) } From 677574a8d7336991b180047dbe0cd5158e964173 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 11 Jul 2025 12:45:40 -0700 Subject: [PATCH 12/45] wip --- .../flashblocks-p2p/src/connection/handler.rs | 35 +++++++------ crates/flashblocks-p2p/src/connection/mod.rs | 21 ++++---- crates/flashblocks-p2p/src/protocol/event.rs | 16 ------ .../flashblocks-p2p/src/protocol/handler.rs | 49 +++++++------------ crates/flashblocks-p2p/src/protocol/mod.rs | 1 - 5 files changed, 49 insertions(+), 73 deletions(-) delete mode 100644 crates/flashblocks-p2p/src/protocol/event.rs diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs index 48e264f0..4d8ade45 100644 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -1,18 +1,25 @@ use super::FlashblocksConnection; use crate::protocol::{ - event::FlashblocksP2PEvent, handler::FlashblocksP2PState, proto::FlashblocksProtoMessage, + event::FlashblocksP2PEvent, + handler::FlashblocksP2PState, + proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}, }; use reth_ethereum::network::{ api::{Direction, PeerId}, eth_wire::{capability::SharedCapabilities, multiplex::ProtocolConnection, protocol::Protocol}, protocol::{ConnectionHandler, OnNotSupported}, }; -use tokio::sync::mpsc; -use tokio_stream::wrappers::UnboundedReceiverStream; +use tokio::sync::{ + broadcast, + mpsc::{self, UnboundedSender}, +}; +use tokio_stream::wrappers::{BroadcastStream, UnboundedReceiverStream}; /// The connection handler for the flashblocks RLPx protocol. pub struct FlashblocksConnectionHandler { - pub state: FlashblocksP2PState, + // pub state: FlashblocksP2PState, + pub inbound_tx: mpsc::UnboundedSender, + pub outbound_rx: broadcast::Receiver, pub network_handle: N, } @@ -39,19 +46,19 @@ impl ConnectionHandler for FlashblocksConnecti conn: ProtocolConnection, ) -> Self::Connection { let (tx, rx) = mpsc::unbounded_channel(); - self.state - .events - .send(FlashblocksP2PEvent::Established { - direction, - peer_id, - to_connection: tx, - }) - .ok(); + // self.state + // .flashblock_stream + // .send(FlashblocksP2PEvent::Established { + // direction, + // peer_id, + // to_connection: tx, + // }) + // .ok(); FlashblocksConnection { conn, peer_id, - commands: UnboundedReceiverStream::new(rx), - state: self.state, + inbound_tx: self.inbound_tx.clone(), + outbound_rx: BroadcastStream::new(self.outbound_rx.clone()), network_handle: self.network_handle, } } diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index 70434dff..050ec6fd 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -9,23 +9,22 @@ use std::{ pin::Pin, task::{Context, Poll, ready}, }; -use tokio_stream::wrappers::UnboundedReceiverStream; +use tokio::sync::{broadcast, mpsc}; +use tokio_stream::wrappers::{BroadcastStream, UnboundedReceiverStream}; pub(crate) mod handler; /// We define some custom commands that the subprotocol supports. -pub enum FlashblocksCommand { - /// Sends a flashblocks payload to the peer - FlashblocksPayloadV1 { - payload: Authorized, - }, +pub struct IncomingPeerMessage { + peer_id: PeerId, + msg: FlashblocksProtoMessageKind, } pub struct FlashblocksConnection { conn: ProtocolConnection, peer_id: PeerId, - commands: UnboundedReceiverStream, - state: FlashblocksP2PState, + inbound_tx: mpsc::UnboundedSender, + outbound_rx: BroadcastStream, network_handle: N, } @@ -36,9 +35,9 @@ impl Stream for FlashblocksConnection { let this = self.get_mut(); loop { - if let Poll::Ready(Some(cmd)) = this.commands.poll_next_unpin(cx) { + if let Poll::Ready(Some(cmd)) = this.outbound_rx.poll_next_unpin(cx) { return match cmd { - FlashblocksCommand::FlashblocksPayloadV1 { payload } => Poll::Ready(Some( + FlashblocksIncoming::FlashblocksPayloadV1 { payload } => Poll::Ready(Some( FlashblocksProtoMessage::flashblocks_payload(payload).encoded(), )), }; @@ -55,7 +54,7 @@ impl Stream for FlashblocksConnection { match msg.message { FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload) => { this.state - .events + .flashblock_stream .send(FlashblocksP2PEvent::FlashblocksPayloadV1(payload)) .ok(); } diff --git a/crates/flashblocks-p2p/src/protocol/event.rs b/crates/flashblocks-p2p/src/protocol/event.rs deleted file mode 100644 index 180d5c0e..00000000 --- a/crates/flashblocks-p2p/src/protocol/event.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::{connection::FlashblocksCommand, protocol::auth::Authorized}; -use reth_ethereum::network::{Direction, api::PeerId}; -use rollup_boost::FlashblocksPayloadV1; -use tokio::sync::mpsc; - -/// The events that can be emitted by our custom protocol. -#[derive(Debug)] -pub enum FlashblocksP2PEvent { - Established { - #[expect(dead_code)] - direction: Direction, - peer_id: PeerId, - to_connection: mpsc::UnboundedSender, - }, - FlashblocksPayloadV1(Authorized), -} diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 2794d31c..9956a822 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -1,7 +1,6 @@ -use super::event::FlashblocksP2PEvent; -use crate::connection::FlashblocksCommand; use crate::connection::handler::FlashblocksConnectionHandler; -use crate::protocol::auth::Authorized; +use crate::protocol::proto::FlashblocksProtoMessage; +use crate::protocol::proto::FlashblocksProtoMessageKind; use ed25519_dalek::VerifyingKey; use reth::payload::PayloadId; use reth_ethereum::network::{api::PeerId, protocol::ProtocolHandler}; @@ -27,13 +26,13 @@ pub struct FlashblocksP2PState { pub authorizer_vk: VerifyingKey, /// Sender of verified and strictly ordered flashbloacks payloads. /// For consumption by the rpc overlay. - pub flashblock_stream: broadcast::Sender, - /// Sender for newly received and validated flashblocks payloads - /// which will be broadcasted to all peers. May not be strictly ordered. - pub broadcast_tx: broadcast::Sender, + pub flashblock_stream: broadcast::Sender, /// Verified flashblock payloads received by peers. /// May not be strictly ordered. - pub inbound_rx: mpsc::UnboundedReceiver, + pub inbound_rx: mpsc::UnboundedReceiver, + /// Sender for newly received and validated flashblocks payloads + /// which will be broadcasted to all peers. May not be strictly ordered. + pub outbound_tx: broadcast::Sender, /// The index of the next flashblock to emit. pub flashblock_index: usize, /// Timestamp of the most recent flashblocks payload. @@ -41,15 +40,15 @@ pub struct FlashblocksP2PState { /// Most recent payload id. pub payload_id: PayloadId, /// Buffer of flashblocks for the current payload. - pub flashblocks: Vec>, + pub flashblocks: Vec>, } impl FlashblocksP2PState { pub fn run(mut self) { tokio::spawn(async move { - while let Some(event) = self.inbound_rx.recv().await { - match event { - FlashblocksCommand::FlashblocksPayloadV1 { payload } => { + while let Some(msg) = self.inbound_rx.recv().await { + match &msg.message { + FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload) => { // TODO: might make sense to perform verification in a separate task if let Err(e) = payload.verify(self.authorizer_vk) { tracing::warn!( @@ -84,32 +83,20 @@ impl FlashblocksP2PState { if flashblock.is_none() { // We haven't seen this index yet // Add the flashblock to our cache - *flashblock = - Some(FlashblocksP2PEvent::FlashblocksPayloadV1(payload.clone())); + *flashblock = Some(payload.clone().payload); tracing::debug!( "Received flashblocks payload with id: {}, index: {}", payload.payload.payload_id, payload.payload.index ); // Broadcast the flashblock to all peers, possible our of order - self.broadcast_tx.send(payload).ok(); + self.outbound_tx.send(msg).unwrap(); // Broadcast any flashblocks in the cache that are in order - // for i in self.flashblock_index..self.flashblocks.len() { - // if let Some(flashblock_event) = &self.flashblocks[i] { - // // Send the flashblock to the stream - // self.flashblock_stream.send(flashblock_event.clone()).ok(); - // // Update the index - // self.flashblock_index = i + 1; - // } else { - // // No more flashblocks in order, break - // break; - // } - // } while let Some(Some(flashblock_event)) = self.flashblocks.get(self.flashblock_index) { // Send the flashblock to the stream - self.flashblock_stream.send(flashblock_event).ok(); + self.flashblock_stream.send(flashblock_event.clone()).ok(); // Update the index self.flashblock_index += 1; } @@ -126,13 +113,13 @@ impl FlashblocksP2PState { pub struct FlashblocksProtoHandler { /// Sender of verified and strictly ordered flashbloacks payloads. /// For consumption by the rpc overlay. - pub flashblock_stream: mpsc::UnboundedSender, + pub flashblock_stream: mpsc::UnboundedSender, /// Sender for newly received and validated flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. - pub broadcast_tx: broadcast::Sender, + pub broadcast_tx: broadcast::Sender, /// Verified flashblock payloads received by peers. /// May not be strictly ordered. - pub inbound_rx: mpsc::UnboundedReceiver, + pub inbound_rx: mpsc::UnboundedReceiver, } impl FlashblocksProtoHandler { @@ -145,7 +132,7 @@ impl FlashblocksProtoHandler { let (inbound_tx, inbound_rx) = mpsc::unbounded_channel(); let state = FlashblocksP2PState { flashblock_stream, - broadcast_tx, + outbound_tx: broadcast_tx, inbound_rx, payload_timestamp: 0, payload_id: PayloadId::default(), diff --git a/crates/flashblocks-p2p/src/protocol/mod.rs b/crates/flashblocks-p2p/src/protocol/mod.rs index a5ccda34..326178a9 100644 --- a/crates/flashblocks-p2p/src/protocol/mod.rs +++ b/crates/flashblocks-p2p/src/protocol/mod.rs @@ -1,4 +1,3 @@ pub mod auth; -pub mod event; pub mod handler; pub mod proto; From 9c3667a4a6848605fbb10c0485151e7b288c54f5 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 11 Jul 2025 12:53:02 -0700 Subject: [PATCH 13/45] wip --- .../flashblocks-p2p/src/protocol/handler.rs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 9956a822..a5d2d574 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -111,35 +111,44 @@ impl FlashblocksP2PState { /// The protocol handler takes care of incoming and outgoing connections. #[derive(Debug)] pub struct FlashblocksProtoHandler { - /// Sender of verified and strictly ordered flashbloacks payloads. - /// For consumption by the rpc overlay. - pub flashblock_stream: mpsc::UnboundedSender, + // /// Sender of verified and strictly ordered flashbloacks payloads. + // /// For consumption by the rpc overlay. + // pub flashblock_stream: broadcast::Sender, /// Sender for newly received and validated flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. - pub broadcast_tx: broadcast::Sender, + pub outbound_rx: broadcast::Receiver, /// Verified flashblock payloads received by peers. /// May not be strictly ordered. - pub inbound_rx: mpsc::UnboundedReceiver, + pub inbound_tx: mpsc::UnboundedSender, } -impl FlashblocksProtoHandler { +impl FlashblocksProtoHandler { /// Creates a new protocol handler with the given state. - pub fn new( + pub fn new( network_handle: N, - flashblock_stream: broadcast::Sender, + authorizer_vk: VerifyingKey, + flashblock_stream: broadcast::Sender, ) -> Self { - let (broadcast_tx, broadcast_rx) = broadcast::channel(100); + let (outbound_tx, outbound_rx) = broadcast::channel(100); let (inbound_tx, inbound_rx) = mpsc::unbounded_channel(); let state = FlashblocksP2PState { + network_handle, + authorizer_vk, flashblock_stream, - outbound_tx: broadcast_tx, inbound_rx, + outbound_tx, + flashblock_index: 0, payload_timestamp: 0, payload_id: PayloadId::default(), flashblocks: vec![], }; state.run(); - Self { network_handle } + + Self { + // flashblock_stream, + outbound_rx, + inbound_tx, + } } } From 99447989d67a2a37f3e0acc6cfe589f96534225c Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 11 Jul 2025 13:04:54 -0700 Subject: [PATCH 14/45] wip --- .../flashblocks-p2p/src/connection/handler.rs | 24 ++++-------------- crates/flashblocks-p2p/src/connection/mod.rs | 7 +++--- crates/flashblocks-p2p/src/net/mod.rs | 20 +++++++++------ .../flashblocks-p2p/src/protocol/handler.rs | 25 +++++++++++-------- 4 files changed, 34 insertions(+), 42 deletions(-) diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs index 4d8ade45..bfe78517 100644 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -1,9 +1,5 @@ use super::FlashblocksConnection; -use crate::protocol::{ - event::FlashblocksP2PEvent, - handler::FlashblocksP2PState, - proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}, -}; +use crate::protocol::proto::FlashblocksProtoMessage; use reth_ethereum::network::{ api::{Direction, PeerId}, eth_wire::{capability::SharedCapabilities, multiplex::ProtocolConnection, protocol::Protocol}, @@ -11,16 +7,15 @@ use reth_ethereum::network::{ }; use tokio::sync::{ broadcast, - mpsc::{self, UnboundedSender}, + mpsc::{self}, }; -use tokio_stream::wrappers::{BroadcastStream, UnboundedReceiverStream}; +use tokio_stream::wrappers::BroadcastStream; /// The connection handler for the flashblocks RLPx protocol. pub struct FlashblocksConnectionHandler { - // pub state: FlashblocksP2PState, + pub network_handle: N, pub inbound_tx: mpsc::UnboundedSender, pub outbound_rx: broadcast::Receiver, - pub network_handle: N, } impl ConnectionHandler for FlashblocksConnectionHandler { @@ -45,20 +40,11 @@ impl ConnectionHandler for FlashblocksConnecti peer_id: PeerId, conn: ProtocolConnection, ) -> Self::Connection { - let (tx, rx) = mpsc::unbounded_channel(); - // self.state - // .flashblock_stream - // .send(FlashblocksP2PEvent::Established { - // direction, - // peer_id, - // to_connection: tx, - // }) - // .ok(); FlashblocksConnection { conn, peer_id, inbound_tx: self.inbound_tx.clone(), - outbound_rx: BroadcastStream::new(self.outbound_rx.clone()), + outbound_rx: BroadcastStream::new(self.outbound_rx.resubscribe()), network_handle: self.network_handle, } } diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index 050ec6fd..a6617500 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -1,16 +1,15 @@ -use crate::protocol::{auth::Authorized, event::FlashblocksP2PEvent, handler::FlashblocksP2PState}; +use crate::protocol::{auth::Authorized, handler::FlashblocksP2PState}; use super::protocol::proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}; use alloy_primitives::bytes::BytesMut; use futures::{Stream, StreamExt}; use reth_ethereum::network::{api::PeerId, eth_wire::multiplex::ProtocolConnection}; -use rollup_boost::FlashblocksPayloadV1; use std::{ pin::Pin, task::{Context, Poll, ready}, }; -use tokio::sync::{broadcast, mpsc}; -use tokio_stream::wrappers::{BroadcastStream, UnboundedReceiverStream}; +use tokio::sync::mpsc; +use tokio_stream::wrappers::BroadcastStream; pub(crate) mod handler; diff --git a/crates/flashblocks-p2p/src/net/mod.rs b/crates/flashblocks-p2p/src/net/mod.rs index ec9861ed..e0beca19 100644 --- a/crates/flashblocks-p2p/src/net/mod.rs +++ b/crates/flashblocks-p2p/src/net/mod.rs @@ -1,3 +1,4 @@ +use ed25519_dalek::VerifyingKey; use reth::chainspec::Hardforks; use reth_eth_wire::NetPrimitivesFor; use reth_ethereum::network::api::FullNetwork; @@ -9,22 +10,21 @@ use reth_node_builder::{ node::{FullNodeTypes, NodeTypes}, }; use reth_transaction_pool::{PoolTransaction, TransactionPool}; -use tokio::sync::mpsc; +use rollup_boost::FlashblocksPayloadV1; +use tokio::sync::{broadcast, mpsc}; -use crate::protocol::{ - event::FlashblocksP2PEvent, - handler::{FlashblocksP2PNetworHandle, FlashblocksProtoHandler}, -}; +use crate::protocol::handler::{FlashblocksP2PNetworHandle, FlashblocksProtoHandler}; #[derive(Clone, Debug)] pub struct FlashblocksNetworkBuilder { inner: T, - events: mpsc::UnboundedSender, + authorizer_vk: VerifyingKey, + events: broadcast::Sender, } impl FlashblocksNetworkBuilder { /// Creates a new `FlashblocksNetworkBuilder` with the given inner builder and events channel. - pub fn new(inner: T, events: mpsc::UnboundedSender) -> Self { + pub fn new(inner: T, events: broadcast::Sender) -> Self { Self { inner, events } } } @@ -48,7 +48,11 @@ where pool: Pool, ) -> eyre::Result { let handle = self.inner.build_network(ctx, pool).await?; - let handler = FlashblocksProtoHandler::::new(handle.clone(), self.events); + let handler = FlashblocksProtoHandler::::new( + handle.clone(), + self.authorizer_vk, + self.events, + ); handle.add_rlpx_sub_protocol(handler.into_rlpx_sub_protocol()); Ok(handle) diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index a5d2d574..b6c753e0 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -110,21 +110,20 @@ impl FlashblocksP2PState { /// The protocol handler takes care of incoming and outgoing connections. #[derive(Debug)] -pub struct FlashblocksProtoHandler { - // /// Sender of verified and strictly ordered flashbloacks payloads. - // /// For consumption by the rpc overlay. - // pub flashblock_stream: broadcast::Sender, - /// Sender for newly received and validated flashblocks payloads - /// which will be broadcasted to all peers. May not be strictly ordered. - pub outbound_rx: broadcast::Receiver, +pub struct FlashblocksProtoHandler { + /// Network handle, used to update peer state. + pub network_handle: N, /// Verified flashblock payloads received by peers. /// May not be strictly ordered. pub inbound_tx: mpsc::UnboundedSender, + /// Sender for newly received and validated flashblocks payloads + /// which will be broadcasted to all peers. May not be strictly ordered. + pub outbound_rx: broadcast::Receiver, } -impl FlashblocksProtoHandler { +impl FlashblocksProtoHandler { /// Creates a new protocol handler with the given state. - pub fn new( + pub fn new( network_handle: N, authorizer_vk: VerifyingKey, flashblock_stream: broadcast::Sender, @@ -132,7 +131,7 @@ impl FlashblocksProtoHandler { let (outbound_tx, outbound_rx) = broadcast::channel(100); let (inbound_tx, inbound_rx) = mpsc::unbounded_channel(); let state = FlashblocksP2PState { - network_handle, + network_handle: network_handle.clone(), authorizer_vk, flashblock_stream, inbound_rx, @@ -145,7 +144,7 @@ impl FlashblocksProtoHandler { state.run(); Self { - // flashblock_stream, + network_handle, outbound_rx, inbound_tx, } @@ -158,6 +157,8 @@ impl ProtocolHandler for FlashblocksProtoHandler< fn on_incoming(&self, _socket_addr: SocketAddr) -> Option { Some(FlashblocksConnectionHandler:: { network_handle: self.network_handle.clone(), + inbound_tx: self.inbound_tx.clone(), + outbound_rx: self.outbound_rx.resubscribe(), }) } @@ -168,6 +169,8 @@ impl ProtocolHandler for FlashblocksProtoHandler< ) -> Option { Some(FlashblocksConnectionHandler:: { network_handle: self.network_handle.clone(), + inbound_tx: self.inbound_tx.clone(), + outbound_rx: self.outbound_rx.resubscribe(), }) } } From 1d0e8adac6221b405b954ba2041d281312b94203 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 11 Jul 2025 13:29:51 -0700 Subject: [PATCH 15/45] wip --- crates/flashblocks-p2p/src/connection/mod.rs | 41 +++++++++++--------- crates/flashblocks-p2p/src/net/mod.rs | 14 +++++-- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index a6617500..0216ca50 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -1,5 +1,3 @@ -use crate::protocol::{auth::Authorized, handler::FlashblocksP2PState}; - use super::protocol::proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}; use alloy_primitives::bytes::BytesMut; use futures::{Stream, StreamExt}; @@ -34,30 +32,37 @@ impl Stream for FlashblocksConnection { let this = self.get_mut(); loop { - if let Poll::Ready(Some(cmd)) = this.outbound_rx.poll_next_unpin(cx) { - return match cmd { - FlashblocksIncoming::FlashblocksPayloadV1 { payload } => Poll::Ready(Some( - FlashblocksProtoMessage::flashblocks_payload(payload).encoded(), - )), - }; + // Check if there are any flashblocks ready to broadcast to our peers. + if let Poll::Ready(Some(res)) = this.outbound_rx.poll_next_unpin(cx) { + match res { + Ok(outbound) => { + return Poll::Ready(Some(outbound.encoded())); + } + Err(e) => { + tracing::error!( + "Failed to receive flashblocks message from broadcast stream: {}", + e + ); + } + } } + // Check if there are any messages from the peer. let Some(msg) = ready!(this.conn.poll_next_unpin(cx)) else { return Poll::Ready(None); }; - let Some(msg) = FlashblocksProtoMessage::decode_message(&mut &msg[..]) else { return Poll::Ready(None); }; - - match msg.message { - FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload) => { - this.state - .flashblock_stream - .send(FlashblocksP2PEvent::FlashblocksPayloadV1(payload)) - .ok(); - } - } + this.inbound_tx + .send(msg.clone()) + .map_err(|e| { + tracing::error!( + "Failed to send flashblocks message to inbound channel: {}", + e + ) + }) + .ok(); } } } diff --git a/crates/flashblocks-p2p/src/net/mod.rs b/crates/flashblocks-p2p/src/net/mod.rs index e0beca19..811d8ac8 100644 --- a/crates/flashblocks-p2p/src/net/mod.rs +++ b/crates/flashblocks-p2p/src/net/mod.rs @@ -11,7 +11,7 @@ use reth_node_builder::{ }; use reth_transaction_pool::{PoolTransaction, TransactionPool}; use rollup_boost::FlashblocksPayloadV1; -use tokio::sync::{broadcast, mpsc}; +use tokio::sync::broadcast; use crate::protocol::handler::{FlashblocksP2PNetworHandle, FlashblocksProtoHandler}; @@ -24,8 +24,16 @@ pub struct FlashblocksNetworkBuilder { impl FlashblocksNetworkBuilder { /// Creates a new `FlashblocksNetworkBuilder` with the given inner builder and events channel. - pub fn new(inner: T, events: broadcast::Sender) -> Self { - Self { inner, events } + pub fn new( + inner: T, + authorizer_vk: VerifyingKey, + events: broadcast::Sender, + ) -> Self { + Self { + inner, + authorizer_vk, + events, + } } } From 782b9514f9b6624a34f4f82d088a4ef1d0ed31b1 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 11 Jul 2025 15:12:00 -0700 Subject: [PATCH 16/45] update vis --- crates/flashblocks-p2p/src/protocol/handler.rs | 13 +++++-------- crates/flashblocks-p2p/src/protocol/proto.rs | 6 +++--- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index b6c753e0..1872d9bb 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -10,10 +10,7 @@ use std::net::SocketAddr; use tokio::sync::broadcast; use tokio::sync::mpsc; -pub(crate) trait FlashblocksP2PNetworHandle: - Clone + Unpin + Peers + std::fmt::Debug + 'static -{ -} +pub trait FlashblocksP2PNetworHandle: Clone + Unpin + Peers + std::fmt::Debug + 'static {} impl FlashblocksP2PNetworHandle for N {} @@ -24,16 +21,16 @@ pub struct FlashblocksP2PState { pub network_handle: N, /// Authorizer verifying, used to verify flashblocks payloads. pub authorizer_vk: VerifyingKey, - /// Sender of verified and strictly ordered flashbloacks payloads. - /// For consumption by the rpc overlay. - pub flashblock_stream: broadcast::Sender, /// Verified flashblock payloads received by peers. /// May not be strictly ordered. pub inbound_rx: mpsc::UnboundedReceiver, /// Sender for newly received and validated flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. pub outbound_tx: broadcast::Sender, - /// The index of the next flashblock to emit. + /// Sender of verified and strictly ordered flashbloacks payloads. + /// For consumption by the rpc overlay. + pub flashblock_stream: broadcast::Sender, + /// The index of the next flashblock to emit over the flashblocks_stream. pub flashblock_index: usize, /// Timestamp of the most recent flashblocks payload. pub payload_timestamp: u64, diff --git a/crates/flashblocks-p2p/src/protocol/proto.rs b/crates/flashblocks-p2p/src/protocol/proto.rs index 21db1df9..a55d937c 100644 --- a/crates/flashblocks-p2p/src/protocol/proto.rs +++ b/crates/flashblocks-p2p/src/protocol/proto.rs @@ -10,17 +10,17 @@ use crate::protocol::auth::Authorized; #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub(crate) enum FlashblocksProtoMessageId { +pub enum FlashblocksProtoMessageId { FlashblocksPayloadV1 = 0x00, } #[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) enum FlashblocksProtoMessageKind { +pub enum FlashblocksProtoMessageKind { FlashblocksPayloadV1(Authorized), } #[derive(Clone, Debug, PartialEq, Eq)] -pub(crate) struct FlashblocksProtoMessage { +pub struct FlashblocksProtoMessage { pub message_type: FlashblocksProtoMessageId, pub message: FlashblocksProtoMessageKind, } From f225b5e4ac949908e9911cc032a5dda2d62b68d9 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 11 Jul 2025 15:23:20 -0700 Subject: [PATCH 17/45] fix node --- crates/flashblocks-node/src/main.rs | 15 +++++++------ crates/flashblocks-rpc/src/flashblocks.rs | 27 +++++++---------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/crates/flashblocks-node/src/main.rs b/crates/flashblocks-node/src/main.rs index 89eb3548..abe2e61f 100644 --- a/crates/flashblocks-node/src/main.rs +++ b/crates/flashblocks-node/src/main.rs @@ -2,13 +2,13 @@ use clap::Parser; use ed25519_dalek::VerifyingKey; -use flashblocks_p2p::protocol::handler::{FlashblocksP2PState, FlashblocksProtoHandler}; +use flashblocks_p2p::protocol::handler::{ FlashblocksProtoHandler}; use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlayBuilder}; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; use reth_optimism_node::{OpNode, args::RollupArgs}; use rollup_boost::parse_vk; -use tokio::sync::mpsc; +use tokio::sync::{broadcast }; use tracing::info; #[derive(Debug, Clone, PartialEq, Eq, clap::Args)] @@ -35,7 +35,7 @@ pub fn main() { Cli::::parse().run(async move |builder, args| { let rollup_args = args.rollup_args; let chain_spec = builder.config().chain.clone(); - let (tx, events) = mpsc::unbounded_channel(); + let (tx, events) = broadcast::channel(100); let flashblocks_overlay_builder = FlashblocksOverlayBuilder::new(chain_spec, args.flashblocks_builder_vk, events); @@ -58,10 +58,11 @@ pub fn main() { .await?; - let custom_rlpx_handler = FlashblocksProtoHandler { - network_handle: handle.node.network.clone(), - state: FlashblocksP2PState { events: tx }, - }; + let custom_rlpx_handler = FlashblocksProtoHandler::new( + handle.node.network.clone(), + VerifyingKey::default(), + tx + ); handle .node diff --git a/crates/flashblocks-rpc/src/flashblocks.rs b/crates/flashblocks-rpc/src/flashblocks.rs index 0312fc26..3595df37 100644 --- a/crates/flashblocks-rpc/src/flashblocks.rs +++ b/crates/flashblocks-rpc/src/flashblocks.rs @@ -1,18 +1,17 @@ use crate::{FlashblocksApi, cache::FlashblocksCache}; use alloy_primitives::{Address, TxHash, U256}; use ed25519_dalek::VerifyingKey; -use flashblocks_p2p::protocol::event::FlashblocksP2PEvent; use jsonrpsee::core::async_trait; use op_alloy_network::Optimism; use reth_optimism_chainspec::OpChainSpec; use reth_rpc_eth_api::{RpcBlock, RpcReceipt}; use rollup_boost::FlashblocksPayloadV1; use std::{io::Read, sync::Arc}; -use tokio::sync::mpsc; +use tokio::sync::{broadcast, mpsc}; use tracing::{debug, error, info}; pub struct FlashblocksOverlayBuilder { - events: mpsc::UnboundedReceiver, + events: broadcast::Receiver, flashblocks_authorizor: VerifyingKey, cache: FlashblocksCache, } @@ -26,7 +25,7 @@ impl FlashblocksOverlayBuilder { pub fn new( chain_spec: Arc, flashblocks_authorizor: VerifyingKey, - events: mpsc::UnboundedReceiver, + events: broadcast::Receiver, ) -> Self { Self { events, @@ -41,21 +40,11 @@ impl FlashblocksOverlayBuilder { cache: self.cache.clone(), }; tokio::spawn(async move { - while let Some(message) = self.events.recv().await { - match message { - FlashblocksP2PEvent::Established { .. } => todo!(), - FlashblocksP2PEvent::FlashblocksPayloadV1(authorized) => { - match authorized.verify(self.flashblocks_authorizor) { - Ok(_) => { - if let Err(e) = cache_cloned.process_payload(authorized.payload) { - error!("failed to process payload: {}", e); - } - } - Err(e) => { - error!("{e:?}"); - } - } - } + loop { + // TODO: handle this error + let payload = self.events.recv().await.unwrap(); + if let Err(e) = cache_cloned.process_payload(payload) { + error!("failed to process payload: {}", e); } } }); From 1fa5b68b687fb0f90996b300862913e41619a1eb Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 11 Jul 2025 15:57:04 -0700 Subject: [PATCH 18/45] rename vars --- Cargo.lock | 1 + crates/flashblocks-node/src/main.rs | 6 ++-- crates/flashblocks-p2p/Cargo.toml | 1 + .../flashblocks-p2p/src/connection/handler.rs | 4 +-- crates/flashblocks-p2p/src/net/mod.rs | 17 +++++++--- .../flashblocks-p2p/src/protocol/handler.rs | 33 ++++++++++--------- 6 files changed, 37 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9cc86f46..8acd97aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3546,6 +3546,7 @@ dependencies = [ "ed25519-dalek", "eyre", "futures", + "parking_lot", "reth", "reth-eth-wire", "reth-ethereum", diff --git a/crates/flashblocks-node/src/main.rs b/crates/flashblocks-node/src/main.rs index abe2e61f..d42bd34a 100644 --- a/crates/flashblocks-node/src/main.rs +++ b/crates/flashblocks-node/src/main.rs @@ -35,10 +35,10 @@ pub fn main() { Cli::::parse().run(async move |builder, args| { let rollup_args = args.rollup_args; let chain_spec = builder.config().chain.clone(); - let (tx, events) = broadcast::channel(100); + let (inbound_tx, inbound_rx) = broadcast::channel(100); let flashblocks_overlay_builder = - FlashblocksOverlayBuilder::new(chain_spec, args.flashblocks_builder_vk, events); + FlashblocksOverlayBuilder::new(chain_spec, args.flashblocks_builder_vk, inbound_rx); let flashblocks_overlay = flashblocks_overlay_builder.start()?; info!(target: "reth::cli", "Launching Flashblocks RPC overlay node"); @@ -61,7 +61,7 @@ pub fn main() { let custom_rlpx_handler = FlashblocksProtoHandler::new( handle.node.network.clone(), VerifyingKey::default(), - tx + inbound_tx ); handle diff --git a/crates/flashblocks-p2p/Cargo.toml b/crates/flashblocks-p2p/Cargo.toml index 1645cbcf..de33b548 100644 --- a/crates/flashblocks-p2p/Cargo.toml +++ b/crates/flashblocks-p2p/Cargo.toml @@ -43,6 +43,7 @@ tracing.workspace = true alloy-primitives.workspace = true serde_json = "1.0" thiserror = "2" +parking_lot = "0.12" rollup-boost = { path = "../rollup-boost" } diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs index bfe78517..dca6add0 100644 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -15,7 +15,7 @@ use tokio_stream::wrappers::BroadcastStream; pub struct FlashblocksConnectionHandler { pub network_handle: N, pub inbound_tx: mpsc::UnboundedSender, - pub outbound_rx: broadcast::Receiver, + pub flashblocks_sender_rx: broadcast::Receiver, } impl ConnectionHandler for FlashblocksConnectionHandler { @@ -44,7 +44,7 @@ impl ConnectionHandler for FlashblocksConnecti conn, peer_id, inbound_tx: self.inbound_tx.clone(), - outbound_rx: BroadcastStream::new(self.outbound_rx.resubscribe()), + outbound_rx: BroadcastStream::new(self.flashblocks_sender_rx.resubscribe()), network_handle: self.network_handle, } } diff --git a/crates/flashblocks-p2p/src/net/mod.rs b/crates/flashblocks-p2p/src/net/mod.rs index 811d8ac8..6464c640 100644 --- a/crates/flashblocks-p2p/src/net/mod.rs +++ b/crates/flashblocks-p2p/src/net/mod.rs @@ -13,13 +13,17 @@ use reth_transaction_pool::{PoolTransaction, TransactionPool}; use rollup_boost::FlashblocksPayloadV1; use tokio::sync::broadcast; -use crate::protocol::handler::{FlashblocksP2PNetworHandle, FlashblocksProtoHandler}; +use crate::protocol::{ + handler::{FlashblocksP2PNetworHandle, FlashblocksProtoHandler}, + proto::FlashblocksProtoMessage, +}; #[derive(Clone, Debug)] pub struct FlashblocksNetworkBuilder { inner: T, authorizer_vk: VerifyingKey, - events: broadcast::Sender, + flashblocks_receiver_tx: broadcast::Sender, + flashblock_sender_tx: broadcast::Sender, } impl FlashblocksNetworkBuilder { @@ -27,12 +31,14 @@ impl FlashblocksNetworkBuilder { pub fn new( inner: T, authorizer_vk: VerifyingKey, - events: broadcast::Sender, + flashblocks_receiver_tx: broadcast::Sender, + flashblock_sender_tx: broadcast::Sender, ) -> Self { Self { inner, authorizer_vk, - events, + flashblocks_receiver_tx, + flashblock_sender_tx, } } } @@ -59,7 +65,8 @@ where let handler = FlashblocksProtoHandler::::new( handle.clone(), self.authorizer_vk, - self.events, + self.flashblocks_receiver_tx, + self.flashblock_sender_tx, ); handle.add_rlpx_sub_protocol(handler.into_rlpx_sub_protocol()); diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 1872d9bb..9768f3f3 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -24,12 +24,12 @@ pub struct FlashblocksP2PState { /// Verified flashblock payloads received by peers. /// May not be strictly ordered. pub inbound_rx: mpsc::UnboundedReceiver, - /// Sender for newly received and validated flashblocks payloads + /// Sender for newly created or received and validated flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. - pub outbound_tx: broadcast::Sender, - /// Sender of verified and strictly ordered flashbloacks payloads. + pub flashblock_sender_tx: broadcast::Sender, + /// Receiver of verified and strictly ordered flashbloacks payloads. /// For consumption by the rpc overlay. - pub flashblock_stream: broadcast::Sender, + pub flashblock_receiver_tx: broadcast::Sender, /// The index of the next flashblock to emit over the flashblocks_stream. pub flashblock_index: usize, /// Timestamp of the most recent flashblocks payload. @@ -87,13 +87,15 @@ impl FlashblocksP2PState { payload.payload.index ); // Broadcast the flashblock to all peers, possible our of order - self.outbound_tx.send(msg).unwrap(); + self.flashblock_sender_tx.send(msg).unwrap(); // Broadcast any flashblocks in the cache that are in order while let Some(Some(flashblock_event)) = self.flashblocks.get(self.flashblock_index) { // Send the flashblock to the stream - self.flashblock_stream.send(flashblock_event.clone()).ok(); + self.flashblock_receiver_tx + .send(flashblock_event.clone()) + .ok(); // Update the index self.flashblock_index += 1; } @@ -113,9 +115,9 @@ pub struct FlashblocksProtoHandler { /// Verified flashblock payloads received by peers. /// May not be strictly ordered. pub inbound_tx: mpsc::UnboundedSender, - /// Sender for newly received and validated flashblocks payloads + /// Sender for newly received and validated or created flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. - pub outbound_rx: broadcast::Receiver, + pub flashblocks_sender_rx: broadcast::Receiver, } impl FlashblocksProtoHandler { @@ -123,16 +125,17 @@ impl FlashblocksProtoHandler { pub fn new( network_handle: N, authorizer_vk: VerifyingKey, - flashblock_stream: broadcast::Sender, + flashblock_receiver_tx: broadcast::Sender, + flashblock_sender_tx: broadcast::Sender, ) -> Self { - let (outbound_tx, outbound_rx) = broadcast::channel(100); let (inbound_tx, inbound_rx) = mpsc::unbounded_channel(); + let flashblock_sender_rx = flashblock_sender_tx.subscribe(); let state = FlashblocksP2PState { network_handle: network_handle.clone(), authorizer_vk, - flashblock_stream, + flashblock_receiver_tx, inbound_rx, - outbound_tx, + flashblock_sender_tx, flashblock_index: 0, payload_timestamp: 0, payload_id: PayloadId::default(), @@ -142,8 +145,8 @@ impl FlashblocksProtoHandler { Self { network_handle, - outbound_rx, inbound_tx, + flashblocks_sender_rx: flashblock_sender_rx, } } } @@ -155,7 +158,7 @@ impl ProtocolHandler for FlashblocksProtoHandler< Some(FlashblocksConnectionHandler:: { network_handle: self.network_handle.clone(), inbound_tx: self.inbound_tx.clone(), - outbound_rx: self.outbound_rx.resubscribe(), + flashblocks_sender_rx: self.flashblocks_sender_rx.resubscribe(), }) } @@ -167,7 +170,7 @@ impl ProtocolHandler for FlashblocksProtoHandler< Some(FlashblocksConnectionHandler:: { network_handle: self.network_handle.clone(), inbound_tx: self.inbound_tx.clone(), - outbound_rx: self.outbound_rx.resubscribe(), + flashblocks_sender_rx: self.flashblocks_sender_rx.resubscribe(), }) } } From e6b0e9d7b7873606641f7f7696e5c1a3632258a1 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Mon, 14 Jul 2025 10:51:42 -0700 Subject: [PATCH 19/45] switch to Mutex for p2p state --- .../flashblocks-p2p/src/connection/handler.rs | 27 ++- crates/flashblocks-p2p/src/connection/mod.rs | 161 +++++++++++++++--- .../flashblocks-p2p/src/protocol/handler.rs | 135 ++++----------- 3 files changed, 187 insertions(+), 136 deletions(-) diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs index dca6add0..66f79e62 100644 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -1,10 +1,18 @@ +use std::sync::Arc; + use super::FlashblocksConnection; -use crate::protocol::proto::FlashblocksProtoMessage; +use crate::protocol::{ + handler::{FlashblocksP2PNetworHandle, FlashblocksP2PState}, + proto::FlashblocksProtoMessage, +}; +use ed25519_dalek::VerifyingKey; +use parking_lot::{Mutex, RwLock}; use reth_ethereum::network::{ api::{Direction, PeerId}, eth_wire::{capability::SharedCapabilities, multiplex::ProtocolConnection, protocol::Protocol}, protocol::{ConnectionHandler, OnNotSupported}, }; +use rollup_boost::FlashblocksPayloadV1; use tokio::sync::{ broadcast, mpsc::{self}, @@ -14,11 +22,13 @@ use tokio_stream::wrappers::BroadcastStream; /// The connection handler for the flashblocks RLPx protocol. pub struct FlashblocksConnectionHandler { pub network_handle: N, - pub inbound_tx: mpsc::UnboundedSender, - pub flashblocks_sender_rx: broadcast::Receiver, + pub authorizer_vk: VerifyingKey, + pub peer_tx: broadcast::Sender, + pub flashblock_tx: broadcast::Sender, + pub state: Arc>, } -impl ConnectionHandler for FlashblocksConnectionHandler { +impl ConnectionHandler for FlashblocksConnectionHandler { type Connection = FlashblocksConnection; fn protocol(&self) -> Protocol { @@ -43,9 +53,14 @@ impl ConnectionHandler for FlashblocksConnecti FlashblocksConnection { conn, peer_id, - inbound_tx: self.inbound_tx.clone(), - outbound_rx: BroadcastStream::new(self.flashblocks_sender_rx.resubscribe()), network_handle: self.network_handle, + authorizer_vk: self.authorizer_vk, + peer_tx: self.peer_tx.clone(), + peer_rx: BroadcastStream::new(self.peer_tx.subscribe()), + flashblock_tx: self.flashblock_tx.clone(), + state: self.state.clone(), + payload_id: Default::default(), + received: Vec::new(), } } } diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index 0216ca50..bf1b77fe 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -1,31 +1,54 @@ -use super::protocol::proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}; +use crate::protocol::{ + auth::Authorized, + handler::{FlashblocksP2PNetworHandle, FlashblocksP2PState}, + proto::{FlashblocksProtoMessageId, FlashblocksProtoMessageKind}, +}; + +use super::protocol::proto::FlashblocksProtoMessage; use alloy_primitives::bytes::BytesMut; +use ed25519_dalek::VerifyingKey; use futures::{Stream, StreamExt}; +use parking_lot::{Mutex, RwLock}; +use reth::payload::PayloadId; use reth_ethereum::network::{api::PeerId, eth_wire::multiplex::ProtocolConnection}; +use reth_network::types::ReputationChangeKind; +use rollup_boost::FlashblocksPayloadV1; use std::{ pin::Pin, + sync::Arc, task::{Context, Poll, ready}, }; -use tokio::sync::mpsc; +use tokio::sync::broadcast; use tokio_stream::wrappers::BroadcastStream; pub(crate) mod handler; -/// We define some custom commands that the subprotocol supports. -pub struct IncomingPeerMessage { - peer_id: PeerId, - msg: FlashblocksProtoMessageKind, -} - pub struct FlashblocksConnection { - conn: ProtocolConnection, - peer_id: PeerId, - inbound_tx: mpsc::UnboundedSender, - outbound_rx: BroadcastStream, - network_handle: N, + pub conn: ProtocolConnection, + pub peer_id: PeerId, + // inbound_tx: mpsc::UnboundedSender, + pub network_handle: N, + /// Authorizer verifying, used to verify flashblocks payloads. + pub authorizer_vk: VerifyingKey, + /// Sender for newly created or received and validated flashblocks payloads + /// which will be broadcasted to all peers. May not be strictly ordered. + pub peer_tx: broadcast::Sender, + /// Receiver for newly created or received and validated flashblocks payloads + /// which will be broadcasted to all peers. May not be strictly ordered. + pub peer_rx: BroadcastStream, + /// Channel of verified and strictly ordered flashbloacks payloads. + /// For consumption by the rpc overlay. + pub flashblock_tx: broadcast::Sender, + /// Mutable state of the flashblocks protocol. + pub state: Arc>, + /// Most recent payload received from this peer. + pub payload_id: PayloadId, + /// A list of flashblocks indices that we have already received from + /// this peer for the current payload. + pub received: Vec, } -impl Stream for FlashblocksConnection { +impl Stream for FlashblocksConnection { type Item = BytesMut; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -33,7 +56,7 @@ impl Stream for FlashblocksConnection { loop { // Check if there are any flashblocks ready to broadcast to our peers. - if let Poll::Ready(Some(res)) = this.outbound_rx.poll_next_unpin(cx) { + if let Poll::Ready(Some(res)) = this.peer_rx.poll_next_unpin(cx) { match res { Ok(outbound) => { return Poll::Ready(Some(outbound.encoded())); @@ -54,15 +77,105 @@ impl Stream for FlashblocksConnection { let Some(msg) = FlashblocksProtoMessage::decode_message(&mut &msg[..]) else { return Poll::Ready(None); }; - this.inbound_tx - .send(msg.clone()) - .map_err(|e| { - tracing::error!( - "Failed to send flashblocks message to inbound channel: {}", - e - ) - }) - .ok(); + + match msg.message { + FlashblocksProtoMessageKind::FlashblocksPayloadV1(authorized) => { + this.handle_flashblocks_payload_v1(authorized, msg.message_type); + } + } + } + } +} + +impl FlashblocksConnection { + fn handle_flashblocks_payload_v1( + &mut self, + message: Authorized, + message_type: FlashblocksProtoMessageId, + ) -> () { + let mut state = self.state.lock(); + + if let Err(e) = message.verify(self.authorizer_vk) { + tracing::warn!( + "Failed to verify flashblocks payload: {:?}, error: {}", + message, + e + ); + self.network_handle + .reputation_change(self.peer_id, ReputationChangeKind::BadMessage); + return; + } + + if message.authorization.timestamp < state.payload_timestamp { + tracing::warn!( + "Received flashblocks payload with outdated timestamp: {}", + message.authorization.timestamp + ); + self.network_handle + .reputation_change(self.peer_id, ReputationChangeKind::BadMessage); + return; + } + + // Check if this is a new payload + if message.authorization.timestamp > state.payload_timestamp { + state.flashblock_index = 0; + state.payload_timestamp = message.authorization.timestamp; + state.payload_id = message.payload.payload_id; + state.flashblocks.fill(None); + } + + // Check if this peer is spamming us with the same payload + if self.payload_id != message.payload.payload_id { + self.payload_id = message.payload.payload_id; + self.received.fill(false); + } + let len = self.received.len(); + self.received + .resize_with(len.max(message.payload.index as usize + 1), || false); + if self.received[message.payload.index as usize] { + // We've already seen this index, skip it + tracing::warn!( + "Received duplicate flashblocks payload with id: {}, index: {}, from peer: {}", + message.payload.payload_id, + message.payload.index, + self.peer_id + ); + self.network_handle + .reputation_change(self.peer_id, ReputationChangeKind::AlreadySeenTransaction); + return; + } + self.received[message.payload.index as usize] = true; + + // If we've already seen this index, skip it + // Otherwise, add it to the list + // TODO: perhaps check max index + let len = state.flashblocks.len(); + state + .flashblocks + .resize_with(len.max(message.payload.index as usize + 1), || None); + let flashblock = &mut state.flashblocks[message.payload.index as usize]; + if flashblock.is_none() { + // We haven't seen this index yet + // Add the flashblock to our cache + *flashblock = Some(message.clone().payload); + tracing::debug!( + "Received flashblocks payload with id: {}, index: {}", + message.payload.payload_id, + message.payload.index + ); + // Broadcast the flashblock to all peers, possibly out of order + let message = FlashblocksProtoMessage { + message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(message), + message_type, + }; + self.peer_tx.send(message).unwrap(); + // Broadcast any flashblocks in the cache that are in order + while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { + // Send the flashblock to the stream + self.flashblock_tx.send(flashblock_event.clone()).ok(); + // Update the index + state.flashblock_index += 1; + } } } } diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 9768f3f3..7ae6f2a3 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -1,14 +1,14 @@ use crate::connection::handler::FlashblocksConnectionHandler; use crate::protocol::proto::FlashblocksProtoMessage; -use crate::protocol::proto::FlashblocksProtoMessageKind; use ed25519_dalek::VerifyingKey; +use parking_lot::Mutex; use reth::payload::PayloadId; use reth_ethereum::network::{api::PeerId, protocol::ProtocolHandler}; use reth_network::Peers; use rollup_boost::FlashblocksPayloadV1; use std::net::SocketAddr; +use std::sync::Arc; use tokio::sync::broadcast; -use tokio::sync::mpsc; pub trait FlashblocksP2PNetworHandle: Clone + Unpin + Peers + std::fmt::Debug + 'static {} @@ -16,20 +16,7 @@ impl FlashblocksP2PNetworH /// Protocol state is an helper struct to store the protocol events. #[derive(Debug)] -pub struct FlashblocksP2PState { - /// Networkd handle, used to update peer state. - pub network_handle: N, - /// Authorizer verifying, used to verify flashblocks payloads. - pub authorizer_vk: VerifyingKey, - /// Verified flashblock payloads received by peers. - /// May not be strictly ordered. - pub inbound_rx: mpsc::UnboundedReceiver, - /// Sender for newly created or received and validated flashblocks payloads - /// which will be broadcasted to all peers. May not be strictly ordered. - pub flashblock_sender_tx: broadcast::Sender, - /// Receiver of verified and strictly ordered flashbloacks payloads. - /// For consumption by the rpc overlay. - pub flashblock_receiver_tx: broadcast::Sender, +pub struct FlashblocksP2PState { /// The index of the next flashblock to emit over the flashblocks_stream. pub flashblock_index: usize, /// Timestamp of the most recent flashblocks payload. @@ -40,84 +27,21 @@ pub struct FlashblocksP2PState { pub flashblocks: Vec>, } -impl FlashblocksP2PState { - pub fn run(mut self) { - tokio::spawn(async move { - while let Some(msg) = self.inbound_rx.recv().await { - match &msg.message { - FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload) => { - // TODO: might make sense to perform verification in a separate task - if let Err(e) = payload.verify(self.authorizer_vk) { - tracing::warn!( - "Failed to verify flashblocks payload: {:?}, error: {}", - payload, - e - ); - // TODO: ban peer - continue; - } - if payload.authorization.timestamp < self.payload_timestamp { - tracing::warn!( - "Received flashblocks payload with outdated timestamp: {}", - payload.authorization.timestamp - ); - // TODO: handle peer - continue; - } - // Check if this is a new payload - if payload.authorization.timestamp > self.payload_timestamp { - self.flashblock_index = 0; - self.payload_timestamp = payload.authorization.timestamp; - self.payload_id = payload.payload.payload_id; - self.flashblocks.clear(); - } - // If we've already seen this index, skip it - // Otherwise, add it to the list - // TODO: perhaps check max index - self.flashblocks - .resize_with(payload.payload.index as usize + 1, || None); - let flashblock = &mut self.flashblocks[payload.payload.index as usize]; - if flashblock.is_none() { - // We haven't seen this index yet - // Add the flashblock to our cache - *flashblock = Some(payload.clone().payload); - tracing::debug!( - "Received flashblocks payload with id: {}, index: {}", - payload.payload.payload_id, - payload.payload.index - ); - // Broadcast the flashblock to all peers, possible our of order - self.flashblock_sender_tx.send(msg).unwrap(); - // Broadcast any flashblocks in the cache that are in order - while let Some(Some(flashblock_event)) = - self.flashblocks.get(self.flashblock_index) - { - // Send the flashblock to the stream - self.flashblock_receiver_tx - .send(flashblock_event.clone()) - .ok(); - // Update the index - self.flashblock_index += 1; - } - } - } - } - } - }); - } -} - /// The protocol handler takes care of incoming and outgoing connections. #[derive(Debug)] pub struct FlashblocksProtoHandler { /// Network handle, used to update peer state. pub network_handle: N, - /// Verified flashblock payloads received by peers. - /// May not be strictly ordered. - pub inbound_tx: mpsc::UnboundedSender, - /// Sender for newly received and validated or created flashblocks payloads + /// Authorizer verifying, used to verify flashblocks payloads. + pub authorizer_vk: VerifyingKey, + /// Sender for newly created or received and validated flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. - pub flashblocks_sender_rx: broadcast::Receiver, + pub peer_tx: broadcast::Sender, + /// Receiver of verified and strictly ordered flashbloacks payloads. + /// For consumption by the rpc overlay. + pub flashblock_tx: broadcast::Sender, + /// Mutable state of the flashblocks protocol. + pub state: Arc>, } impl FlashblocksProtoHandler { @@ -125,28 +49,23 @@ impl FlashblocksProtoHandler { pub fn new( network_handle: N, authorizer_vk: VerifyingKey, - flashblock_receiver_tx: broadcast::Sender, - flashblock_sender_tx: broadcast::Sender, + flashblock_tx: broadcast::Sender, + peer_tx: broadcast::Sender, ) -> Self { - let (inbound_tx, inbound_rx) = mpsc::unbounded_channel(); - let flashblock_sender_rx = flashblock_sender_tx.subscribe(); - let state = FlashblocksP2PState { - network_handle: network_handle.clone(), - authorizer_vk, - flashblock_receiver_tx, - inbound_rx, - flashblock_sender_tx, + // let flashblock_sender_rx = flashblock_sender_tx.subscribe(); + let state = Arc::new(Mutex::new(FlashblocksP2PState { flashblock_index: 0, payload_timestamp: 0, payload_id: PayloadId::default(), flashblocks: vec![], - }; - state.run(); + })); Self { network_handle, - inbound_tx, - flashblocks_sender_rx: flashblock_sender_rx, + authorizer_vk, + peer_tx, + flashblock_tx, + state, } } } @@ -157,8 +76,10 @@ impl ProtocolHandler for FlashblocksProtoHandler< fn on_incoming(&self, _socket_addr: SocketAddr) -> Option { Some(FlashblocksConnectionHandler:: { network_handle: self.network_handle.clone(), - inbound_tx: self.inbound_tx.clone(), - flashblocks_sender_rx: self.flashblocks_sender_rx.resubscribe(), + authorizer_vk: self.authorizer_vk, + peer_tx: self.peer_tx.clone(), + flashblock_tx: self.flashblock_tx.clone(), + state: self.state.clone(), }) } @@ -169,8 +90,10 @@ impl ProtocolHandler for FlashblocksProtoHandler< ) -> Option { Some(FlashblocksConnectionHandler:: { network_handle: self.network_handle.clone(), - inbound_tx: self.inbound_tx.clone(), - flashblocks_sender_rx: self.flashblocks_sender_rx.resubscribe(), + authorizer_vk: self.authorizer_vk, + peer_tx: self.peer_tx.clone(), + flashblock_tx: self.flashblock_tx.clone(), + state: self.state.clone(), }) } } From 4ac463375c7dc3d35fc58ebbc948ce5e7e0b32e2 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Mon, 14 Jul 2025 11:07:56 -0700 Subject: [PATCH 20/45] update protocol to accept new blocks --- crates/flashblocks-node/src/main.rs | 4 +++- crates/flashblocks-p2p/src/connection/handler.rs | 7 ++----- crates/flashblocks-p2p/src/connection/mod.rs | 7 ++++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/flashblocks-node/src/main.rs b/crates/flashblocks-node/src/main.rs index d42bd34a..6ec8b70c 100644 --- a/crates/flashblocks-node/src/main.rs +++ b/crates/flashblocks-node/src/main.rs @@ -36,6 +36,7 @@ pub fn main() { let rollup_args = args.rollup_args; let chain_spec = builder.config().chain.clone(); let (inbound_tx, inbound_rx) = broadcast::channel(100); + let (outbound_tx, _outbound_rx) = broadcast::channel(100); let flashblocks_overlay_builder = FlashblocksOverlayBuilder::new(chain_spec, args.flashblocks_builder_vk, inbound_rx); @@ -61,7 +62,8 @@ pub fn main() { let custom_rlpx_handler = FlashblocksProtoHandler::new( handle.node.network.clone(), VerifyingKey::default(), - inbound_tx + inbound_tx, + outbound_tx, ); handle diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs index 66f79e62..64cebef5 100644 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -6,17 +6,14 @@ use crate::protocol::{ proto::FlashblocksProtoMessage, }; use ed25519_dalek::VerifyingKey; -use parking_lot::{Mutex, RwLock}; +use parking_lot::Mutex; use reth_ethereum::network::{ api::{Direction, PeerId}, eth_wire::{capability::SharedCapabilities, multiplex::ProtocolConnection, protocol::Protocol}, protocol::{ConnectionHandler, OnNotSupported}, }; use rollup_boost::FlashblocksPayloadV1; -use tokio::sync::{ - broadcast, - mpsc::{self}, -}; +use tokio::sync::broadcast; use tokio_stream::wrappers::BroadcastStream; /// The connection handler for the flashblocks RLPx protocol. diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index bf1b77fe..caebf9c5 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -26,7 +26,6 @@ pub(crate) mod handler; pub struct FlashblocksConnection { pub conn: ProtocolConnection, pub peer_id: PeerId, - // inbound_tx: mpsc::UnboundedSender, pub network_handle: N, /// Authorizer verifying, used to verify flashblocks payloads. pub authorizer_vk: VerifyingKey, @@ -59,6 +58,7 @@ impl Stream for FlashblocksConnection { if let Poll::Ready(Some(res)) = this.peer_rx.poll_next_unpin(cx) { match res { Ok(outbound) => { + // TODO: handle the case where this peer is the one that sent the original return Poll::Ready(Some(outbound.encoded())); } Err(e) => { @@ -92,7 +92,7 @@ impl FlashblocksConnection { &mut self, message: Authorized, message_type: FlashblocksProtoMessageId, - ) -> () { + ) { let mut state = self.state.lock(); if let Err(e) = message.verify(self.authorizer_vk) { @@ -133,7 +133,8 @@ impl FlashblocksConnection { self.received .resize_with(len.max(message.payload.index as usize + 1), || false); if self.received[message.payload.index as usize] { - // We've already seen this index, skip it + // We've already seen this index from this peer. + // They could be trying to DOS us. tracing::warn!( "Received duplicate flashblocks payload with id: {}, index: {}, from peer: {}", message.payload.payload_id, From 0cbf403085f548e45b2af21484ad1e5a671ee29d Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Mon, 14 Jul 2025 12:46:16 -0700 Subject: [PATCH 21/45] move tests directory --- Cargo.lock | 1 + Cargo.toml | 1 + crates/flashblocks-node/Cargo.toml | 1 + crates/flashblocks-node/src/main.rs | 8 +- crates/flashblocks-node/src/tests/mod.rs | 316 ---------------- .../{src => }/tests/assets/genesis.json | 0 crates/flashblocks-node/tests/p2p.rs | 338 ++++++++++++++++++ .../flashblocks-p2p/src/protocol/handler.rs | 2 + crates/flashblocks-rpc/src/flashblocks.rs | 32 +- crates/flashblocks-rpc/src/lib.rs | 2 +- .../src/tests/assets/genesis.json | 100 ------ crates/flashblocks-rpc/src/tests/mod.rs | 316 ---------------- 12 files changed, 364 insertions(+), 753 deletions(-) delete mode 100644 crates/flashblocks-node/src/tests/mod.rs rename crates/flashblocks-node/{src => }/tests/assets/genesis.json (100%) create mode 100644 crates/flashblocks-node/tests/p2p.rs delete mode 100644 crates/flashblocks-rpc/src/tests/assets/genesis.json delete mode 100644 crates/flashblocks-rpc/src/tests/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 8acd97aa..43c40af3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3509,6 +3509,7 @@ dependencies = [ "reth-eth-wire", "reth-ethereum", "reth-network", + "reth-network-peers", "reth-node-api", "reth-node-builder", "reth-node-core", diff --git a/Cargo.toml b/Cargo.toml index f03a9fe1..457620a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-eth-wire = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-network = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-network-peers = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } diff --git a/crates/flashblocks-node/Cargo.toml b/crates/flashblocks-node/Cargo.toml index 983f41ae..46ac8902 100644 --- a/crates/flashblocks-node/Cargo.toml +++ b/crates/flashblocks-node/Cargo.toml @@ -34,6 +34,7 @@ reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1 reth-ethereum = { workspace = true, features = ["node", "network", "cli"] } reth-eth-wire = { workspace = true } reth-network = { workspace = true } +reth-network-peers = { workspace = true } ed25519-dalek = { version = "2", features = ["serde"] } alloy-eips.workspace = true diff --git a/crates/flashblocks-node/src/main.rs b/crates/flashblocks-node/src/main.rs index 6ec8b70c..a0968684 100644 --- a/crates/flashblocks-node/src/main.rs +++ b/crates/flashblocks-node/src/main.rs @@ -3,7 +3,7 @@ use clap::Parser; use ed25519_dalek::VerifyingKey; use flashblocks_p2p::protocol::handler::{ FlashblocksProtoHandler}; -use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlayBuilder}; +use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay }; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; use reth_optimism_node::{OpNode, args::RollupArgs}; @@ -38,9 +38,9 @@ pub fn main() { let (inbound_tx, inbound_rx) = broadcast::channel(100); let (outbound_tx, _outbound_rx) = broadcast::channel(100); - let flashblocks_overlay_builder = - FlashblocksOverlayBuilder::new(chain_spec, args.flashblocks_builder_vk, inbound_rx); - let flashblocks_overlay = flashblocks_overlay_builder.start()?; + let flashblocks_overlay = + FlashblocksOverlay::new(chain_spec, inbound_rx); + flashblocks_overlay.clone().start()?; info!(target: "reth::cli", "Launching Flashblocks RPC overlay node"); let handle = builder diff --git a/crates/flashblocks-node/src/tests/mod.rs b/crates/flashblocks-node/src/tests/mod.rs deleted file mode 100644 index d3ed4227..00000000 --- a/crates/flashblocks-node/src/tests/mod.rs +++ /dev/null @@ -1,316 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::{ - EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, FlashblocksOverlayBuilder, - cache::Metadata, - }; - use alloy_consensus::Receipt; - use alloy_genesis::Genesis; - use alloy_primitives::{Address, B256, Bytes, TxHash, U256, address, b256}; - use alloy_provider::{Provider, RootProvider}; - use alloy_rpc_client::RpcClient; - use alloy_rpc_types_engine::PayloadId; - use reth_node_builder::{Node, NodeBuilder, NodeConfig, NodeHandle}; - use reth_node_core::{ - args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}, - exit::NodeExitFuture, - }; - use reth_optimism_chainspec::OpChainSpecBuilder; - use reth_optimism_node::{OpNode, args::RollupArgs}; - use reth_optimism_primitives::OpReceipt; - use reth_provider::providers::BlockchainProvider; - use reth_tasks::TaskManager; - use rollup_boost::{ - ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, FlashblocksPayloadV1, - }; - use std::{any::Any, collections::HashMap, net::SocketAddr, str::FromStr, sync::Arc}; - use tokio::sync::{mpsc, oneshot}; - use url::Url; - - pub struct NodeContext { - sender: mpsc::Sender<(FlashblocksPayloadV1, oneshot::Sender<()>)>, - http_api_addr: SocketAddr, - _node_exit_future: NodeExitFuture, - _node: Box, - _task_manager: TaskManager, - } - - impl NodeContext { - pub async fn send_payload(&self, payload: FlashblocksPayloadV1) -> eyre::Result<()> { - let (tx, rx) = oneshot::channel(); - self.sender.send((payload, tx)).await?; - rx.await?; - Ok(()) - } - - pub async fn provider(&self) -> eyre::Result { - let url = format!("http://{}", self.http_api_addr); - let client = RpcClient::builder().http(url.parse()?); - - Ok(RootProvider::new(client)) - } - - pub async fn send_test_payloads(&self) -> eyre::Result<()> { - let base_payload = create_first_payload(); - self.send_payload(base_payload).await?; - - let second_payload = create_second_payload(); - self.send_payload(second_payload).await?; - - Ok(()) - } - } - - async fn setup_node() -> eyre::Result { - let tasks = TaskManager::current(); - let exec = tasks.executor(); - - let genesis: Genesis = serde_json::from_str(include_str!("assets/genesis.json")).unwrap(); - let chain_spec = Arc::new( - OpChainSpecBuilder::base_mainnet() - .genesis(genesis) - .ecotone_activated() - .build(), - ); - - let network_config = NetworkArgs { - discovery: DiscoveryArgs { - disable_discovery: true, - ..DiscoveryArgs::default() - }, - ..NetworkArgs::default() - }; - - // Use with_unused_ports() to let Reth allocate random ports and avoid port collisions - let node_config = NodeConfig::new(chain_spec.clone()) - .with_network(network_config.clone()) - .with_rpc(RpcServerArgs::default().with_unused_ports().with_http()) - .with_unused_ports(); - - let node = OpNode::new(RollupArgs::default()); - - // Start websocket server to simulate the builder and send payloads back to the node - let (sender, mut receiver) = - mpsc::channel::<(FlashblocksPayloadV1, oneshot::Sender<()>)>(100); - - let NodeHandle { - node, - node_exit_future, - } = NodeBuilder::new(node_config.clone()) - .testing_node(exec.clone()) - .with_types_and_provider::>() - .with_components(node.components_builder()) - .with_add_ons(node.add_ons()) - .extend_rpc_modules(move |ctx| { - // We are not going to use the websocket connection to send payloads so we use - // a dummy url. - let flashblocks_overlay = FlashblocksOverlayBuilder::new(chain_spec); - - let eth_api = ctx.registry.eth_api().clone(); - let api_ext = FlashblocksApiExt::new(eth_api.clone(), flashblocks_overlay.clone()); - - ctx.modules.replace_configured(api_ext.into_rpc())?; - - tokio::spawn(async move { - while let Some((payload, tx)) = receiver.recv().await { - flashblocks_overlay.process_payload(payload).unwrap(); - tx.send(()).unwrap(); - } - }); - - Ok(()) - }) - .launch() - .await?; - - let http_api_addr = node - .rpc_server_handle() - .http_local_addr() - .ok_or_else(|| eyre::eyre!("Failed to get http api address"))?; - - Ok(NodeContext { - sender, - http_api_addr, - _node_exit_future: node_exit_future, - _node: Box::new(node), - _task_manager: tasks, - }) - } - - fn create_first_payload() -> FlashblocksPayloadV1 { - FlashblocksPayloadV1 { - payload_id: PayloadId::new([0; 8]), - index: 0, - base: Some(ExecutionPayloadBaseV1 { - parent_beacon_block_root: B256::default(), - parent_hash: B256::default(), - fee_recipient: Address::ZERO, - prev_randao: B256::default(), - block_number: 1, - gas_limit: 0, - timestamp: 0, - extra_data: Bytes::new(), - base_fee_per_gas: U256::ZERO, - }), - diff: ExecutionPayloadFlashblockDeltaV1::default(), - metadata: serde_json::to_value(Metadata { - block_number: 1, - receipts: HashMap::default(), - new_account_balances: HashMap::default(), - }) - .unwrap(), - } - } - - const TEST_ADDRESS: Address = address!("0x1234567890123456789012345678901234567890"); - const PENDING_BALANCE: u64 = 4600; - - const TX1_HASH: TxHash = - b256!("0x2be2e6f8b01b03b87ae9f0ebca8bbd420f174bef0fbcc18c7802c5378b78f548"); - const TX2_HASH: TxHash = - b256!("0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8"); - - fn create_second_payload() -> FlashblocksPayloadV1 { - // Create second payload (index 1) with transactions - // tx1 hash: 0x2be2e6f8b01b03b87ae9f0ebca8bbd420f174bef0fbcc18c7802c5378b78f548 (deposit transaction) - // tx2 hash: 0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8 - let tx1 = Bytes::from_str("0x7ef8f8a042a8ae5ec231af3d0f90f68543ec8bca1da4f7edd712d5b51b490688355a6db794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e200000044d000a118b00000000000000040000000067cb7cb0000000000077dbd4000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000014edd27304108914dd6503b19b9eeb9956982ef197febbeeed8a9eac3dbaaabdf000000000000000000000000fc56e7272eebbba5bc6c544e159483c4a38f8ba3").unwrap(); - let tx2 = Bytes::from_str("0xf8cd82016d8316e5708302c01c94f39635f2adf40608255779ff742afe13de31f57780b8646e530e9700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000156ddc81eed2a36d68302948ba0a608703e79b22164f74523d188a11f81c25a65dd59535bab1cd1d8b30d115f3ea07f4cfbbad77a139c9209d3bded89091867ff6b548dd714109c61d1f8e7a84d14").unwrap(); - - // Send another test flashblock payload - let payload = FlashblocksPayloadV1 { - payload_id: PayloadId::new([0; 8]), - index: 1, - base: None, - diff: ExecutionPayloadFlashblockDeltaV1 { - state_root: B256::default(), - receipts_root: B256::default(), - gas_used: 0, - block_hash: B256::default(), - transactions: vec![tx1, tx2], - withdrawals: Vec::new(), - logs_bloom: Default::default(), - withdrawals_root: Default::default(), - }, - metadata: serde_json::to_value(Metadata { - block_number: 1, - receipts: { - let mut receipts = HashMap::default(); - receipts.insert( - TX1_HASH.to_string(), // transaction hash as string - OpReceipt::Legacy(Receipt { - status: true.into(), - cumulative_gas_used: 21000, - logs: vec![], - }), - ); - receipts.insert( - TX2_HASH.to_string(), // transaction hash as string - OpReceipt::Legacy(Receipt { - status: true.into(), - cumulative_gas_used: 45000, - logs: vec![], - }), - ); - receipts - }, - new_account_balances: { - let mut map = HashMap::default(); - map.insert( - TEST_ADDRESS.to_string(), - format!("0x{:x}", U256::from(PENDING_BALANCE)), - ); - map - }, - }) - .unwrap(), - }; - - payload - } - - #[tokio::test] - async fn test_get_block_by_number_pending() -> eyre::Result<()> { - reth_tracing::init_test_tracing(); - let node = setup_node().await?; - let provider = node.provider().await?; - - let latest_block = provider - .get_block_by_number(alloy_eips::BlockNumberOrTag::Latest) - .await? - .expect("latest block expected"); - assert_eq!(latest_block.number(), 0); - - // Querying pending block when it does not exists yet - let pending_block = provider - .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) - .await?; - assert_eq!(pending_block.is_none(), true); - - let base_payload = create_first_payload(); - node.send_payload(base_payload).await?; - - // Query pending block after sending the base payload with an empty delta - let pending_block = provider - .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) - .await? - .expect("pending block expected"); - - assert_eq!(pending_block.number(), 1); - assert_eq!(pending_block.transactions.hashes().len(), 0); - - let second_payload = create_second_payload(); - node.send_payload(second_payload).await?; - - // Query pending block after sending the second payload with two transactions - let block = provider - .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) - .await? - .expect("pending block expected"); - - assert_eq!(block.number(), 1); - assert_eq!(block.transactions.hashes().len(), 2); - - Ok(()) - } - - #[tokio::test] - async fn test_get_balance_pending() -> eyre::Result<()> { - reth_tracing::init_test_tracing(); - let node = setup_node().await?; - let provider = node.provider().await?; - - node.send_test_payloads().await?; - - let balance = provider.get_balance(TEST_ADDRESS).await?; - assert_eq!(balance, U256::ZERO); - - let pending_balance = provider.get_balance(TEST_ADDRESS).pending().await?; - assert_eq!(pending_balance, U256::from(PENDING_BALANCE)); - - Ok(()) - } - - #[tokio::test] - async fn test_get_transaction_receipt_pending() -> eyre::Result<()> { - reth_tracing::init_test_tracing(); - let node = setup_node().await?; - let provider = node.provider().await?; - - let receipt = provider.get_transaction_receipt(TX1_HASH).await?; - assert_eq!(receipt.is_none(), true); - - node.send_test_payloads().await?; - - let receipt = provider - .get_transaction_receipt(TX1_HASH) - .await? - .expect("receipt expected"); - assert_eq!(receipt.gas_used, 21000); - - // TODO: Add a new payload and validate that the receipts from the previous payload - // are not returned. - - Ok(()) - } -} diff --git a/crates/flashblocks-node/src/tests/assets/genesis.json b/crates/flashblocks-node/tests/assets/genesis.json similarity index 100% rename from crates/flashblocks-node/src/tests/assets/genesis.json rename to crates/flashblocks-node/tests/assets/genesis.json diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs new file mode 100644 index 00000000..54b703ab --- /dev/null +++ b/crates/flashblocks-node/tests/p2p.rs @@ -0,0 +1,338 @@ +use alloy_consensus::Receipt; +use alloy_genesis::Genesis; +use alloy_primitives::{Address, B256, Bytes, TxHash, U256, address, b256}; +use alloy_provider::{Provider, RootProvider}; +use alloy_rpc_client::RpcClient; +use alloy_rpc_types_engine::PayloadId; +use ed25519_dalek::VerifyingKey; +use flashblocks_p2p::protocol::handler::FlashblocksProtoHandler; +use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, Metadata}; +use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; +use reth_network::{Peers, PeersInfo, protocol::IntoRlpxSubProtocol as _}; +use reth_network_peers::{NodeRecord, PeerId}; +use reth_node_builder::{Node, NodeBuilder, NodeConfig, NodeHandle}; +use reth_node_core::{ + args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}, + exit::NodeExitFuture, +}; +use reth_optimism_chainspec::OpChainSpecBuilder; +use reth_optimism_node::{OpNode, args::RollupArgs}; +use reth_optimism_primitives::OpReceipt; +use reth_provider::providers::BlockchainProvider; +use reth_tasks::TaskManager; +use rollup_boost::{ + ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, FlashblocksPayloadV1, +}; +use std::{any::Any, collections::HashMap, net::SocketAddr, str::FromStr, sync::Arc}; +use tokio::sync::{broadcast, mpsc, oneshot}; + +pub struct NodeContext { + sender: mpsc::Sender<(FlashblocksPayloadV1, oneshot::Sender<()>)>, + pub local_node_record: NodeRecord, + http_api_addr: SocketAddr, + _node_exit_future: NodeExitFuture, + _node: Box, + _task_manager: TaskManager, +} + +impl NodeContext { + pub async fn send_payload(&self, payload: FlashblocksPayloadV1) -> eyre::Result<()> { + let (tx, rx) = oneshot::channel(); + self.sender.send((payload, tx)).await?; + rx.await?; + Ok(()) + } + + pub async fn provider(&self) -> eyre::Result { + let url = format!("http://{}", self.http_api_addr); + let client = RpcClient::builder().http(url.parse()?); + + Ok(RootProvider::new(client)) + } + + pub async fn send_test_payloads(&self) -> eyre::Result<()> { + let base_payload = create_first_payload(); + self.send_payload(base_payload).await?; + + let second_payload = create_second_payload(); + self.send_payload(second_payload).await?; + + Ok(()) + } +} + +async fn setup_node(trusted_peer: Option<(PeerId, SocketAddr)>) -> eyre::Result { + let tasks = TaskManager::current(); + let exec = tasks.executor(); + + let (outbound_tx, outbound_rx) = broadcast::channel(100); + let (inbound_tx, inbound_rx) = broadcast::channel(100); + + let genesis: Genesis = serde_json::from_str(include_str!("assets/genesis.json")).unwrap(); + let chain_spec = Arc::new( + OpChainSpecBuilder::base_mainnet() + .genesis(genesis) + .ecotone_activated() + .build(), + ); + + let network_config = NetworkArgs { + discovery: DiscoveryArgs { + disable_discovery: true, + ..DiscoveryArgs::default() + }, + ..NetworkArgs::default() + }; + + // Use with_unused_ports() to let Reth allocate random ports and avoid port collisions + let node_config = NodeConfig::new(chain_spec.clone()) + .with_network(network_config.clone()) + .with_rpc(RpcServerArgs::default().with_unused_ports().with_http()) + .with_unused_ports(); + + let node = OpNode::new(RollupArgs::default()); + + // Start websocket server to simulate the builder and send payloads back to the node + let (sender, mut receiver) = mpsc::channel::<(FlashblocksPayloadV1, oneshot::Sender<()>)>(100); + + let NodeHandle { + node, + node_exit_future, + } = NodeBuilder::new(node_config.clone()) + .testing_node(exec.clone()) + .with_types_and_provider::>() + .with_components(node.components_builder()) + .with_add_ons(node.add_ons()) + .extend_rpc_modules(move |ctx| { + // We are not going to use the websocket connection to send payloads so we use + // a dummy url. + let flashblocks_overlay = FlashblocksOverlay::new(chain_spec, inbound_rx); + flashblocks_overlay.clone().start()?; + + let eth_api = ctx.registry.eth_api().clone(); + let api_ext = FlashblocksApiExt::new(eth_api.clone(), flashblocks_overlay.clone()); + + ctx.modules.replace_configured(api_ext.into_rpc())?; + + tokio::spawn(async move { + while let Some((payload, tx)) = receiver.recv().await { + flashblocks_overlay.process_payload(payload).unwrap(); + tx.send(()).unwrap(); + } + }); + + Ok(()) + }) + .launch() + .await?; + + let verifying_key = VerifyingKey::default(); + let custom_rlpx_handler = FlashblocksProtoHandler::new( + node.network.clone(), + VerifyingKey::default(), + inbound_tx, + outbound_tx, + ); + + node.network + .add_rlpx_sub_protocol(custom_rlpx_handler.into_rlpx_sub_protocol()); + + if let Some((peer_id, addr)) = trusted_peer { + // If a trusted peer is provided, add it to the network + node.network.add_trusted_peer(peer_id, addr); + } + + let http_api_addr = node + .rpc_server_handle() + .http_local_addr() + .ok_or_else(|| eyre::eyre!("Failed to get http api address"))?; + + let network_handle = node.network.clone(); + let local_node_record = network_handle.local_node_record(); + + Ok(NodeContext { + sender, + local_node_record, + http_api_addr, + _node_exit_future: node_exit_future, + _node: Box::new(node), + _task_manager: tasks, + }) +} + +fn create_first_payload() -> FlashblocksPayloadV1 { + FlashblocksPayloadV1 { + payload_id: PayloadId::new([0; 8]), + index: 0, + base: Some(ExecutionPayloadBaseV1 { + parent_beacon_block_root: B256::default(), + parent_hash: B256::default(), + fee_recipient: Address::ZERO, + prev_randao: B256::default(), + block_number: 1, + gas_limit: 0, + timestamp: 0, + extra_data: Bytes::new(), + base_fee_per_gas: U256::ZERO, + }), + diff: ExecutionPayloadFlashblockDeltaV1::default(), + metadata: serde_json::to_value(Metadata { + block_number: 1, + receipts: HashMap::default(), + new_account_balances: HashMap::default(), + }) + .unwrap(), + } +} + +const TEST_ADDRESS: Address = address!("0x1234567890123456789012345678901234567890"); +const PENDING_BALANCE: u64 = 4600; + +const TX1_HASH: TxHash = + b256!("0x2be2e6f8b01b03b87ae9f0ebca8bbd420f174bef0fbcc18c7802c5378b78f548"); +const TX2_HASH: TxHash = + b256!("0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8"); + +fn create_second_payload() -> FlashblocksPayloadV1 { + // Create second payload (index 1) with transactions + // tx1 hash: 0x2be2e6f8b01b03b87ae9f0ebca8bbd420f174bef0fbcc18c7802c5378b78f548 (deposit transaction) + // tx2 hash: 0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8 + let tx1 = Bytes::from_str("0x7ef8f8a042a8ae5ec231af3d0f90f68543ec8bca1da4f7edd712d5b51b490688355a6db794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e200000044d000a118b00000000000000040000000067cb7cb0000000000077dbd4000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000014edd27304108914dd6503b19b9eeb9956982ef197febbeeed8a9eac3dbaaabdf000000000000000000000000fc56e7272eebbba5bc6c544e159483c4a38f8ba3").unwrap(); + let tx2 = Bytes::from_str("0xf8cd82016d8316e5708302c01c94f39635f2adf40608255779ff742afe13de31f57780b8646e530e9700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000156ddc81eed2a36d68302948ba0a608703e79b22164f74523d188a11f81c25a65dd59535bab1cd1d8b30d115f3ea07f4cfbbad77a139c9209d3bded89091867ff6b548dd714109c61d1f8e7a84d14").unwrap(); + + // Send another test flashblock payload + let payload = FlashblocksPayloadV1 { + payload_id: PayloadId::new([0; 8]), + index: 1, + base: None, + diff: ExecutionPayloadFlashblockDeltaV1 { + state_root: B256::default(), + receipts_root: B256::default(), + gas_used: 0, + block_hash: B256::default(), + transactions: vec![tx1, tx2], + withdrawals: Vec::new(), + logs_bloom: Default::default(), + withdrawals_root: Default::default(), + }, + metadata: serde_json::to_value(Metadata { + block_number: 1, + receipts: { + let mut receipts = HashMap::default(); + receipts.insert( + TX1_HASH.to_string(), // transaction hash as string + OpReceipt::Legacy(Receipt { + status: true.into(), + cumulative_gas_used: 21000, + logs: vec![], + }), + ); + receipts.insert( + TX2_HASH.to_string(), // transaction hash as string + OpReceipt::Legacy(Receipt { + status: true.into(), + cumulative_gas_used: 45000, + logs: vec![], + }), + ); + receipts + }, + new_account_balances: { + let mut map = HashMap::default(); + map.insert( + TEST_ADDRESS.to_string(), + format!("0x{:x}", U256::from(PENDING_BALANCE)), + ); + map + }, + }) + .unwrap(), + }; + + payload +} + +#[tokio::test] +async fn test_get_block_by_number_pending() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + let node = setup_node(None).await?; + let provider = node.provider().await?; + + let latest_block = provider + .get_block_by_number(alloy_eips::BlockNumberOrTag::Latest) + .await? + .expect("latest block expected"); + assert_eq!(latest_block.number(), 0); + + // Querying pending block when it does not exists yet + let pending_block = provider + .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) + .await?; + assert_eq!(pending_block.is_none(), true); + + let base_payload = create_first_payload(); + node.send_payload(base_payload).await?; + + // Query pending block after sending the base payload with an empty delta + let pending_block = provider + .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) + .await? + .expect("pending block expected"); + + assert_eq!(pending_block.number(), 1); + assert_eq!(pending_block.transactions.hashes().len(), 0); + + let second_payload = create_second_payload(); + node.send_payload(second_payload).await?; + + // Query pending block after sending the second payload with two transactions + let block = provider + .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) + .await? + .expect("pending block expected"); + + assert_eq!(block.number(), 1); + assert_eq!(block.transactions.hashes().len(), 2); + + Ok(()) +} + +#[tokio::test] +async fn test_get_balance_pending() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + let node = setup_node(None).await?; + let provider = node.provider().await?; + + node.send_test_payloads().await?; + + let balance = provider.get_balance(TEST_ADDRESS).await?; + assert_eq!(balance, U256::ZERO); + + let pending_balance = provider.get_balance(TEST_ADDRESS).pending().await?; + assert_eq!(pending_balance, U256::from(PENDING_BALANCE)); + + Ok(()) +} + +#[tokio::test] +async fn test_get_transaction_receipt_pending() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + let node = setup_node(None).await?; + let provider = node.provider().await?; + + let receipt = provider.get_transaction_receipt(TX1_HASH).await?; + assert_eq!(receipt.is_none(), true); + + node.send_test_payloads().await?; + + let receipt = provider + .get_transaction_receipt(TX1_HASH) + .await? + .expect("receipt expected"); + assert_eq!(receipt.gas_used, 21000); + + // TODO: Add a new payload and validate that the receipts from the previous payload + // are not returned. + + Ok(()) +} diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 7ae6f2a3..b9ac054e 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -36,6 +36,8 @@ pub struct FlashblocksProtoHandler { pub authorizer_vk: VerifyingKey, /// Sender for newly created or received and validated flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. + /// TODO: we still need an internal listener for this channel to + /// handle locally created flashblocks. pub peer_tx: broadcast::Sender, /// Receiver of verified and strictly ordered flashbloacks payloads. /// For consumption by the rpc overlay. diff --git a/crates/flashblocks-rpc/src/flashblocks.rs b/crates/flashblocks-rpc/src/flashblocks.rs index 3595df37..71c176d0 100644 --- a/crates/flashblocks-rpc/src/flashblocks.rs +++ b/crates/flashblocks-rpc/src/flashblocks.rs @@ -1,44 +1,44 @@ use crate::{FlashblocksApi, cache::FlashblocksCache}; use alloy_primitives::{Address, TxHash, U256}; -use ed25519_dalek::VerifyingKey; use jsonrpsee::core::async_trait; use op_alloy_network::Optimism; use reth_optimism_chainspec::OpChainSpec; use reth_rpc_eth_api::{RpcBlock, RpcReceipt}; use rollup_boost::FlashblocksPayloadV1; use std::{io::Read, sync::Arc}; -use tokio::sync::{broadcast, mpsc}; -use tracing::{debug, error, info}; +use tokio::sync::broadcast; +use tracing::error; -pub struct FlashblocksOverlayBuilder { +pub struct FlashblocksOverlay { events: broadcast::Receiver, - flashblocks_authorizor: VerifyingKey, cache: FlashblocksCache, } -#[derive(Clone)] -pub struct FlashblocksOverlay { - cache: FlashblocksCache, +impl Clone for FlashblocksOverlay { + fn clone(&self) -> Self { + Self { + events: self.events.resubscribe(), + cache: self.cache.clone(), + } + } } -impl FlashblocksOverlayBuilder { +impl FlashblocksOverlay { pub fn new( chain_spec: Arc, - flashblocks_authorizor: VerifyingKey, events: broadcast::Receiver, ) -> Self { Self { events, - flashblocks_authorizor, cache: FlashblocksCache::new(chain_spec), } } - pub fn start(mut self) -> eyre::Result { + pub fn start(mut self) -> eyre::Result<()> { let cache_cloned = self.cache.clone(); - let overlay = FlashblocksOverlay { - cache: self.cache.clone(), - }; + // let overlay = FlashblocksOverlay { + // cache: self.cache.clone(), + // }; tokio::spawn(async move { loop { // TODO: handle this error @@ -49,7 +49,7 @@ impl FlashblocksOverlayBuilder { } }); - Ok(overlay) + Ok(()) } pub fn process_payload(&self, payload: FlashblocksPayloadV1) -> eyre::Result<()> { diff --git a/crates/flashblocks-rpc/src/lib.rs b/crates/flashblocks-rpc/src/lib.rs index 5887b2a7..3bb78141 100644 --- a/crates/flashblocks-rpc/src/lib.rs +++ b/crates/flashblocks-rpc/src/lib.rs @@ -8,4 +8,4 @@ mod metrics; pub use metrics::*; mod cache; -mod tests; +pub use cache::*; diff --git a/crates/flashblocks-rpc/src/tests/assets/genesis.json b/crates/flashblocks-rpc/src/tests/assets/genesis.json deleted file mode 100644 index 4d703497..00000000 --- a/crates/flashblocks-rpc/src/tests/assets/genesis.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "config": { - "chainId": 8453, - "homesteadBlock": 0, - "eip150Block": 0, - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "constantinopleBlock": 0, - "petersburgBlock": 0, - "istanbulBlock": 0, - "muirGlacierBlock": 0, - "berlinBlock": 0, - "londonBlock": 0, - "arrowGlacierBlock": 0, - "grayGlacierBlock": 0, - "mergeNetsplitBlock": 0, - "bedrockBlock": 0, - "regolithTime": 0, - "terminalTotalDifficulty": 0, - "terminalTotalDifficultyPassed": true, - "optimism": { - "eip1559Elasticity": 6, - "eip1559Denominator": 50 - } - }, - "nonce": "0x0", - "timestamp": "0x0", - "extraData": "0x00", - "gasLimit": "0x1c9c380", - "difficulty": "0x0", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "coinbase": "0x0000000000000000000000000000000000000000", - "alloc": { - "0x14dc79964da2c08b23698b3d3cc7ca32193d9955": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x1cbd3b2770909d4e10f157cabc84c7264073c9ec": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x2546bcd3c84621e976d8185a91a922ae77ecec30": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x70997970c51812dc3a010c7d01b50e0d17dc79c8": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x71be63f3384f5fb98995898a86b02fb2426c5788": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x8626f6940e2eb28930efb4cef49b2d1f2c9c1199": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x90f79bf6eb2c4f870365e785982e1f101e93b906": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x976ea74026e726554db657fa54763abd0c3a0aa9": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc": { - "balance": "0xd3c21bcecceda1000000" - }, - "0x9c41de96b2088cdc640c6182dfcf5491dc574a57": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xa0ee7a142d267c1f36714e4a8f75612f20a79720": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xbcd4042de499d14e55001ccbb24a551f3b954096": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xbda5747bfd65f08deb54cb465eb87d40e51b197e": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xcd3b766ccdd6ae721141f452c550ca635964ce71": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xdd2fd4581271e230360230f9337d5c0430bf44c0": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xdf3e18d64bc6a983f673ab319ccae4f1a57c7097": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266": { - "balance": "0xd3c21bcecceda1000000" - }, - "0xfabb0ac9d68b0b445fb7357272ff202c5651694a": { - "balance": "0xd3c21bcecceda1000000" - } - }, - "number": "0x0" -} \ No newline at end of file diff --git a/crates/flashblocks-rpc/src/tests/mod.rs b/crates/flashblocks-rpc/src/tests/mod.rs deleted file mode 100644 index d3ed4227..00000000 --- a/crates/flashblocks-rpc/src/tests/mod.rs +++ /dev/null @@ -1,316 +0,0 @@ -#[cfg(test)] -mod tests { - use crate::{ - EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, FlashblocksOverlayBuilder, - cache::Metadata, - }; - use alloy_consensus::Receipt; - use alloy_genesis::Genesis; - use alloy_primitives::{Address, B256, Bytes, TxHash, U256, address, b256}; - use alloy_provider::{Provider, RootProvider}; - use alloy_rpc_client::RpcClient; - use alloy_rpc_types_engine::PayloadId; - use reth_node_builder::{Node, NodeBuilder, NodeConfig, NodeHandle}; - use reth_node_core::{ - args::{DiscoveryArgs, NetworkArgs, RpcServerArgs}, - exit::NodeExitFuture, - }; - use reth_optimism_chainspec::OpChainSpecBuilder; - use reth_optimism_node::{OpNode, args::RollupArgs}; - use reth_optimism_primitives::OpReceipt; - use reth_provider::providers::BlockchainProvider; - use reth_tasks::TaskManager; - use rollup_boost::{ - ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, FlashblocksPayloadV1, - }; - use std::{any::Any, collections::HashMap, net::SocketAddr, str::FromStr, sync::Arc}; - use tokio::sync::{mpsc, oneshot}; - use url::Url; - - pub struct NodeContext { - sender: mpsc::Sender<(FlashblocksPayloadV1, oneshot::Sender<()>)>, - http_api_addr: SocketAddr, - _node_exit_future: NodeExitFuture, - _node: Box, - _task_manager: TaskManager, - } - - impl NodeContext { - pub async fn send_payload(&self, payload: FlashblocksPayloadV1) -> eyre::Result<()> { - let (tx, rx) = oneshot::channel(); - self.sender.send((payload, tx)).await?; - rx.await?; - Ok(()) - } - - pub async fn provider(&self) -> eyre::Result { - let url = format!("http://{}", self.http_api_addr); - let client = RpcClient::builder().http(url.parse()?); - - Ok(RootProvider::new(client)) - } - - pub async fn send_test_payloads(&self) -> eyre::Result<()> { - let base_payload = create_first_payload(); - self.send_payload(base_payload).await?; - - let second_payload = create_second_payload(); - self.send_payload(second_payload).await?; - - Ok(()) - } - } - - async fn setup_node() -> eyre::Result { - let tasks = TaskManager::current(); - let exec = tasks.executor(); - - let genesis: Genesis = serde_json::from_str(include_str!("assets/genesis.json")).unwrap(); - let chain_spec = Arc::new( - OpChainSpecBuilder::base_mainnet() - .genesis(genesis) - .ecotone_activated() - .build(), - ); - - let network_config = NetworkArgs { - discovery: DiscoveryArgs { - disable_discovery: true, - ..DiscoveryArgs::default() - }, - ..NetworkArgs::default() - }; - - // Use with_unused_ports() to let Reth allocate random ports and avoid port collisions - let node_config = NodeConfig::new(chain_spec.clone()) - .with_network(network_config.clone()) - .with_rpc(RpcServerArgs::default().with_unused_ports().with_http()) - .with_unused_ports(); - - let node = OpNode::new(RollupArgs::default()); - - // Start websocket server to simulate the builder and send payloads back to the node - let (sender, mut receiver) = - mpsc::channel::<(FlashblocksPayloadV1, oneshot::Sender<()>)>(100); - - let NodeHandle { - node, - node_exit_future, - } = NodeBuilder::new(node_config.clone()) - .testing_node(exec.clone()) - .with_types_and_provider::>() - .with_components(node.components_builder()) - .with_add_ons(node.add_ons()) - .extend_rpc_modules(move |ctx| { - // We are not going to use the websocket connection to send payloads so we use - // a dummy url. - let flashblocks_overlay = FlashblocksOverlayBuilder::new(chain_spec); - - let eth_api = ctx.registry.eth_api().clone(); - let api_ext = FlashblocksApiExt::new(eth_api.clone(), flashblocks_overlay.clone()); - - ctx.modules.replace_configured(api_ext.into_rpc())?; - - tokio::spawn(async move { - while let Some((payload, tx)) = receiver.recv().await { - flashblocks_overlay.process_payload(payload).unwrap(); - tx.send(()).unwrap(); - } - }); - - Ok(()) - }) - .launch() - .await?; - - let http_api_addr = node - .rpc_server_handle() - .http_local_addr() - .ok_or_else(|| eyre::eyre!("Failed to get http api address"))?; - - Ok(NodeContext { - sender, - http_api_addr, - _node_exit_future: node_exit_future, - _node: Box::new(node), - _task_manager: tasks, - }) - } - - fn create_first_payload() -> FlashblocksPayloadV1 { - FlashblocksPayloadV1 { - payload_id: PayloadId::new([0; 8]), - index: 0, - base: Some(ExecutionPayloadBaseV1 { - parent_beacon_block_root: B256::default(), - parent_hash: B256::default(), - fee_recipient: Address::ZERO, - prev_randao: B256::default(), - block_number: 1, - gas_limit: 0, - timestamp: 0, - extra_data: Bytes::new(), - base_fee_per_gas: U256::ZERO, - }), - diff: ExecutionPayloadFlashblockDeltaV1::default(), - metadata: serde_json::to_value(Metadata { - block_number: 1, - receipts: HashMap::default(), - new_account_balances: HashMap::default(), - }) - .unwrap(), - } - } - - const TEST_ADDRESS: Address = address!("0x1234567890123456789012345678901234567890"); - const PENDING_BALANCE: u64 = 4600; - - const TX1_HASH: TxHash = - b256!("0x2be2e6f8b01b03b87ae9f0ebca8bbd420f174bef0fbcc18c7802c5378b78f548"); - const TX2_HASH: TxHash = - b256!("0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8"); - - fn create_second_payload() -> FlashblocksPayloadV1 { - // Create second payload (index 1) with transactions - // tx1 hash: 0x2be2e6f8b01b03b87ae9f0ebca8bbd420f174bef0fbcc18c7802c5378b78f548 (deposit transaction) - // tx2 hash: 0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8 - let tx1 = Bytes::from_str("0x7ef8f8a042a8ae5ec231af3d0f90f68543ec8bca1da4f7edd712d5b51b490688355a6db794deaddeaddeaddeaddeaddeaddeaddeaddead00019442000000000000000000000000000000000000158080830f424080b8a4440a5e200000044d000a118b00000000000000040000000067cb7cb0000000000077dbd4000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000014edd27304108914dd6503b19b9eeb9956982ef197febbeeed8a9eac3dbaaabdf000000000000000000000000fc56e7272eebbba5bc6c544e159483c4a38f8ba3").unwrap(); - let tx2 = Bytes::from_str("0xf8cd82016d8316e5708302c01c94f39635f2adf40608255779ff742afe13de31f57780b8646e530e9700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000156ddc81eed2a36d68302948ba0a608703e79b22164f74523d188a11f81c25a65dd59535bab1cd1d8b30d115f3ea07f4cfbbad77a139c9209d3bded89091867ff6b548dd714109c61d1f8e7a84d14").unwrap(); - - // Send another test flashblock payload - let payload = FlashblocksPayloadV1 { - payload_id: PayloadId::new([0; 8]), - index: 1, - base: None, - diff: ExecutionPayloadFlashblockDeltaV1 { - state_root: B256::default(), - receipts_root: B256::default(), - gas_used: 0, - block_hash: B256::default(), - transactions: vec![tx1, tx2], - withdrawals: Vec::new(), - logs_bloom: Default::default(), - withdrawals_root: Default::default(), - }, - metadata: serde_json::to_value(Metadata { - block_number: 1, - receipts: { - let mut receipts = HashMap::default(); - receipts.insert( - TX1_HASH.to_string(), // transaction hash as string - OpReceipt::Legacy(Receipt { - status: true.into(), - cumulative_gas_used: 21000, - logs: vec![], - }), - ); - receipts.insert( - TX2_HASH.to_string(), // transaction hash as string - OpReceipt::Legacy(Receipt { - status: true.into(), - cumulative_gas_used: 45000, - logs: vec![], - }), - ); - receipts - }, - new_account_balances: { - let mut map = HashMap::default(); - map.insert( - TEST_ADDRESS.to_string(), - format!("0x{:x}", U256::from(PENDING_BALANCE)), - ); - map - }, - }) - .unwrap(), - }; - - payload - } - - #[tokio::test] - async fn test_get_block_by_number_pending() -> eyre::Result<()> { - reth_tracing::init_test_tracing(); - let node = setup_node().await?; - let provider = node.provider().await?; - - let latest_block = provider - .get_block_by_number(alloy_eips::BlockNumberOrTag::Latest) - .await? - .expect("latest block expected"); - assert_eq!(latest_block.number(), 0); - - // Querying pending block when it does not exists yet - let pending_block = provider - .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) - .await?; - assert_eq!(pending_block.is_none(), true); - - let base_payload = create_first_payload(); - node.send_payload(base_payload).await?; - - // Query pending block after sending the base payload with an empty delta - let pending_block = provider - .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) - .await? - .expect("pending block expected"); - - assert_eq!(pending_block.number(), 1); - assert_eq!(pending_block.transactions.hashes().len(), 0); - - let second_payload = create_second_payload(); - node.send_payload(second_payload).await?; - - // Query pending block after sending the second payload with two transactions - let block = provider - .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) - .await? - .expect("pending block expected"); - - assert_eq!(block.number(), 1); - assert_eq!(block.transactions.hashes().len(), 2); - - Ok(()) - } - - #[tokio::test] - async fn test_get_balance_pending() -> eyre::Result<()> { - reth_tracing::init_test_tracing(); - let node = setup_node().await?; - let provider = node.provider().await?; - - node.send_test_payloads().await?; - - let balance = provider.get_balance(TEST_ADDRESS).await?; - assert_eq!(balance, U256::ZERO); - - let pending_balance = provider.get_balance(TEST_ADDRESS).pending().await?; - assert_eq!(pending_balance, U256::from(PENDING_BALANCE)); - - Ok(()) - } - - #[tokio::test] - async fn test_get_transaction_receipt_pending() -> eyre::Result<()> { - reth_tracing::init_test_tracing(); - let node = setup_node().await?; - let provider = node.provider().await?; - - let receipt = provider.get_transaction_receipt(TX1_HASH).await?; - assert_eq!(receipt.is_none(), true); - - node.send_test_payloads().await?; - - let receipt = provider - .get_transaction_receipt(TX1_HASH) - .await? - .expect("receipt expected"); - assert_eq!(receipt.gas_used, 21000); - - // TODO: Add a new payload and validate that the receipts from the previous payload - // are not returned. - - Ok(()) - } -} From dcf2a4317898bac57ca56a9b51343b20dd5f372b Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Mon, 14 Jul 2025 14:38:06 -0700 Subject: [PATCH 22/45] getting integration tests working --- crates/flashblocks-node/tests/p2p.rs | 264 +++++++++++------- .../flashblocks-p2p/src/connection/handler.rs | 2 + crates/flashblocks-p2p/src/connection/mod.rs | 7 +- .../flashblocks-p2p/src/protocol/handler.rs | 5 +- 4 files changed, 181 insertions(+), 97 deletions(-) diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index 54b703ab..56025905 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -4,8 +4,12 @@ use alloy_primitives::{Address, B256, Bytes, TxHash, U256, address, b256}; use alloy_provider::{Provider, RootProvider}; use alloy_rpc_client::RpcClient; use alloy_rpc_types_engine::PayloadId; -use ed25519_dalek::VerifyingKey; -use flashblocks_p2p::protocol::handler::FlashblocksProtoHandler; +use ed25519_dalek::{SigningKey, VerifyingKey}; +use flashblocks_p2p::protocol::{ + auth::Authorized, + handler::FlashblocksProtoHandler, + proto::{FlashblocksProtoMessage, FlashblocksProtoMessageId, FlashblocksProtoMessageKind}, +}; use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, Metadata}; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_network::{Peers, PeersInfo, protocol::IntoRlpxSubProtocol as _}; @@ -19,29 +23,29 @@ use reth_optimism_chainspec::OpChainSpecBuilder; use reth_optimism_node::{OpNode, args::RollupArgs}; use reth_optimism_primitives::OpReceipt; use reth_provider::providers::BlockchainProvider; -use reth_tasks::TaskManager; +use reth_tasks::{TaskExecutor, TaskManager}; use rollup_boost::{ - ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, FlashblocksPayloadV1, + Authorization, ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, FlashblocksPayloadV1, }; use std::{any::Any, collections::HashMap, net::SocketAddr, str::FromStr, sync::Arc}; -use tokio::sync::{broadcast, mpsc, oneshot}; +use tokio::sync::broadcast; +use tracing::info; pub struct NodeContext { - sender: mpsc::Sender<(FlashblocksPayloadV1, oneshot::Sender<()>)>, + inbound_tx: broadcast::Sender, + outbound_tx: broadcast::Sender, pub local_node_record: NodeRecord, http_api_addr: SocketAddr, _node_exit_future: NodeExitFuture, _node: Box, - _task_manager: TaskManager, } impl NodeContext { - pub async fn send_payload(&self, payload: FlashblocksPayloadV1) -> eyre::Result<()> { - let (tx, rx) = oneshot::channel(); - self.sender.send((payload, tx)).await?; - rx.await?; - Ok(()) - } + // pub async fn send_payload(&self, payload: FlashblocksPayloadV1) -> eyre::Result<()> { + // self.sender.send(payload).await?; + // rx.await?; + // Ok(()) + // } pub async fn provider(&self) -> eyre::Result { let url = format!("http://{}", self.http_api_addr); @@ -50,22 +54,23 @@ impl NodeContext { Ok(RootProvider::new(client)) } - pub async fn send_test_payloads(&self) -> eyre::Result<()> { - let base_payload = create_first_payload(); - self.send_payload(base_payload).await?; - - let second_payload = create_second_payload(); - self.send_payload(second_payload).await?; - - Ok(()) - } + // pub async fn send_test_payloads(&self) -> eyre::Result<()> { + // let base_payload = create_first_payload(); + // self.send_payload(base_payload).await?; + // + // let second_payload = create_second_payload(); + // self.send_payload(second_payload).await?; + // + // Ok(()) + // } } -async fn setup_node(trusted_peer: Option<(PeerId, SocketAddr)>) -> eyre::Result { - let tasks = TaskManager::current(); - let exec = tasks.executor(); - - let (outbound_tx, outbound_rx) = broadcast::channel(100); +async fn setup_node( + exec: TaskExecutor, + authorizer: SigningKey, + trusted_peer: Option<(PeerId, SocketAddr)>, +) -> eyre::Result { + let (outbound_tx, _outbound_rx) = broadcast::channel(100); let (inbound_tx, inbound_rx) = broadcast::channel(100); let genesis: Genesis = serde_json::from_str(include_str!("assets/genesis.json")).unwrap(); @@ -92,9 +97,6 @@ async fn setup_node(trusted_peer: Option<(PeerId, SocketAddr)>) -> eyre::Result< let node = OpNode::new(RollupArgs::default()); - // Start websocket server to simulate the builder and send payloads back to the node - let (sender, mut receiver) = mpsc::channel::<(FlashblocksPayloadV1, oneshot::Sender<()>)>(100); - let NodeHandle { node, node_exit_future, @@ -114,34 +116,30 @@ async fn setup_node(trusted_peer: Option<(PeerId, SocketAddr)>) -> eyre::Result< ctx.modules.replace_configured(api_ext.into_rpc())?; - tokio::spawn(async move { - while let Some((payload, tx)) = receiver.recv().await { - flashblocks_overlay.process_payload(payload).unwrap(); - tx.send(()).unwrap(); - } - }); - Ok(()) }) .launch() .await?; - let verifying_key = VerifyingKey::default(); let custom_rlpx_handler = FlashblocksProtoHandler::new( node.network.clone(), - VerifyingKey::default(), - inbound_tx, - outbound_tx, + authorizer.verifying_key(), + inbound_tx.clone(), + outbound_tx.clone(), ); node.network .add_rlpx_sub_protocol(custom_rlpx_handler.into_rlpx_sub_protocol()); + tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; + if let Some((peer_id, addr)) = trusted_peer { // If a trusted peer is provided, add it to the network node.network.add_trusted_peer(peer_id, addr); } + tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; + let http_api_addr = node .rpc_server_handle() .http_local_addr() @@ -151,12 +149,12 @@ async fn setup_node(trusted_peer: Option<(PeerId, SocketAddr)>) -> eyre::Result< let local_node_record = network_handle.local_node_record(); Ok(NodeContext { - sender, + inbound_tx, + outbound_tx, local_node_record, http_api_addr, _node_exit_future: node_exit_future, _node: Box::new(node), - _task_manager: tasks, }) } @@ -201,7 +199,7 @@ fn create_second_payload() -> FlashblocksPayloadV1 { let tx2 = Bytes::from_str("0xf8cd82016d8316e5708302c01c94f39635f2adf40608255779ff742afe13de31f57780b8646e530e9700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000156ddc81eed2a36d68302948ba0a608703e79b22164f74523d188a11f81c25a65dd59535bab1cd1d8b30d115f3ea07f4cfbbad77a139c9209d3bded89091867ff6b548dd714109c61d1f8e7a84d14").unwrap(); // Send another test flashblock payload - let payload = FlashblocksPayloadV1 { + FlashblocksPayloadV1 { payload_id: PayloadId::new([0; 8]), index: 1, base: None, @@ -247,34 +245,109 @@ fn create_second_payload() -> FlashblocksPayloadV1 { }, }) .unwrap(), - }; - - payload + } } +// #[tokio::test] +// async fn test_get_block_by_number_pending() -> eyre::Result<()> { +// reth_tracing::init_test_tracing(); +// let node = setup_node(None).await?; +// let provider = node.provider().await?; +// +// let latest_block = provider +// .get_block_by_number(alloy_eips::BlockNumberOrTag::Latest) +// .await? +// .expect("latest block expected"); +// assert_eq!(latest_block.number(), 0); +// +// // Querying pending block when it does not exists yet +// let pending_block = provider +// .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) +// .await?; +// assert_eq!(pending_block.is_none(), true); +// +// let base_payload = create_first_payload(); +// node.sender.send(base_payload.clone())?; +// tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; +// +// // Query pending block after sending the base payload with an empty delta +// let pending_block = provider +// .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) +// .await? +// .expect("pending block expected"); +// +// assert_eq!(pending_block.number(), 1); +// assert_eq!(pending_block.transactions.hashes().len(), 0); +// +// let second_payload = create_second_payload(); +// node.sender.send(second_payload.clone())?; +// tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; +// +// // Query pending block after sending the second payload with two transactions +// let block = provider +// .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) +// .await? +// .expect("pending block expected"); +// +// assert_eq!(block.number(), 1); +// assert_eq!(block.transactions.hashes().len(), 2); +// +// Ok(()) +// } + #[tokio::test] -async fn test_get_block_by_number_pending() -> eyre::Result<()> { +async fn test_peering() -> eyre::Result<()> { reth_tracing::init_test_tracing(); - let node = setup_node(None).await?; - let provider = node.provider().await?; + let authorizer = SigningKey::from_bytes(&[0u8; 32]); + let builder = SigningKey::from_bytes(&[1u8; 32]); - let latest_block = provider + let tasks = TaskManager::current(); + let exec = tasks.executor(); + // let exec = TaskExecutor::current(); + let node_0 = setup_node(exec, authorizer.clone(), None).await?; + let provider_0 = node_0.provider().await?; + let enr_0 = node_0.local_node_record; + + let node_1 = setup_node( + TaskExecutor::current(), + authorizer.clone(), + Some((enr_0.id, enr_0.tcp_addr())), + ) + .await?; + let provider_1 = node_1.provider().await?; + + tokio::time::sleep(tokio::time::Duration::from_millis(20000)).await; + + let latest_block = provider_1 .get_block_by_number(alloy_eips::BlockNumberOrTag::Latest) .await? .expect("latest block expected"); assert_eq!(latest_block.number(), 0); // Querying pending block when it does not exists yet - let pending_block = provider + let pending_block = provider_1 .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) .await?; assert_eq!(pending_block.is_none(), true); let base_payload = create_first_payload(); - node.send_payload(base_payload).await?; + info!("Sending base payload"); + let authorization = Authorization::new( + base_payload.payload_id, + 0, + &authorizer, + builder.verifying_key(), + ); + let authorized = Authorized::new(&builder, authorization, base_payload.clone()); + let proto_message = FlashblocksProtoMessage { + message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(authorized), + message_type: FlashblocksProtoMessageId::FlashblocksPayloadV1, + }; + node_1.outbound_tx.send(proto_message)?; + tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; // Query pending block after sending the base payload with an empty delta - let pending_block = provider + let pending_block = provider_0 .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) .await? .expect("pending block expected"); @@ -283,10 +356,11 @@ async fn test_get_block_by_number_pending() -> eyre::Result<()> { assert_eq!(pending_block.transactions.hashes().len(), 0); let second_payload = create_second_payload(); - node.send_payload(second_payload).await?; + node_1.inbound_tx.send(second_payload.clone())?; + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; // Query pending block after sending the second payload with two transactions - let block = provider + let block = provider_0 .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) .await? .expect("pending block expected"); @@ -297,42 +371,42 @@ async fn test_get_block_by_number_pending() -> eyre::Result<()> { Ok(()) } -#[tokio::test] -async fn test_get_balance_pending() -> eyre::Result<()> { - reth_tracing::init_test_tracing(); - let node = setup_node(None).await?; - let provider = node.provider().await?; - - node.send_test_payloads().await?; - - let balance = provider.get_balance(TEST_ADDRESS).await?; - assert_eq!(balance, U256::ZERO); - - let pending_balance = provider.get_balance(TEST_ADDRESS).pending().await?; - assert_eq!(pending_balance, U256::from(PENDING_BALANCE)); - - Ok(()) -} - -#[tokio::test] -async fn test_get_transaction_receipt_pending() -> eyre::Result<()> { - reth_tracing::init_test_tracing(); - let node = setup_node(None).await?; - let provider = node.provider().await?; - - let receipt = provider.get_transaction_receipt(TX1_HASH).await?; - assert_eq!(receipt.is_none(), true); - - node.send_test_payloads().await?; - - let receipt = provider - .get_transaction_receipt(TX1_HASH) - .await? - .expect("receipt expected"); - assert_eq!(receipt.gas_used, 21000); - - // TODO: Add a new payload and validate that the receipts from the previous payload - // are not returned. - - Ok(()) -} +// #[tokio::test] +// async fn test_get_balance_pending() -> eyre::Result<()> { +// reth_tracing::init_test_tracing(); +// let node = setup_node(None).await?; +// let provider = node.provider().await?; +// +// node.send_test_payloads().await?; +// +// let balance = provider.get_balance(TEST_ADDRESS).await?; +// assert_eq!(balance, U256::ZERO); +// +// let pending_balance = provider.get_balance(TEST_ADDRESS).pending().await?; +// assert_eq!(pending_balance, U256::from(PENDING_BALANCE)); +// +// Ok(()) +// } +// +// #[tokio::test] +// async fn test_get_transaction_receipt_pending() -> eyre::Result<()> { +// reth_tracing::init_test_tracing(); +// let node = setup_node(None).await?; +// let provider = node.provider().await?; +// +// let receipt = provider.get_transaction_receipt(TX1_HASH).await?; +// assert_eq!(receipt.is_none(), true); +// +// node.send_test_payloads().await?; +// +// let receipt = provider +// .get_transaction_receipt(TX1_HASH) +// .await? +// .expect("receipt expected"); +// assert_eq!(receipt.gas_used, 21000); +// +// // TODO: Add a new payload and validate that the receipts from the previous payload +// // are not returned. +// +// Ok(()) +// } diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs index 64cebef5..5efc0e7b 100644 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ b/crates/flashblocks-p2p/src/connection/handler.rs @@ -15,6 +15,7 @@ use reth_ethereum::network::{ use rollup_boost::FlashblocksPayloadV1; use tokio::sync::broadcast; use tokio_stream::wrappers::BroadcastStream; +use tracing::debug; /// The connection handler for the flashblocks RLPx protocol. pub struct FlashblocksConnectionHandler { @@ -47,6 +48,7 @@ impl ConnectionHandler for FlashblocksConnectionH peer_id: PeerId, conn: ProtocolConnection, ) -> Self::Connection { + debug!(%peer_id, %direction, "New connection established with flashblocks peer"); FlashblocksConnection { conn, peer_id, diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index caebf9c5..3e22d364 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -9,7 +9,7 @@ use alloy_primitives::bytes::BytesMut; use ed25519_dalek::VerifyingKey; use futures::{Stream, StreamExt}; use parking_lot::{Mutex, RwLock}; -use reth::payload::PayloadId; +use reth::{payload::PayloadId, rpc::api::eth::helpers::trace}; use reth_ethereum::network::{api::PeerId, eth_wire::multiplex::ProtocolConnection}; use reth_network::types::ReputationChangeKind; use rollup_boost::FlashblocksPayloadV1; @@ -20,6 +20,7 @@ use std::{ }; use tokio::sync::broadcast; use tokio_stream::wrappers::BroadcastStream; +use tracing::trace; pub(crate) mod handler; @@ -59,6 +60,7 @@ impl Stream for FlashblocksConnection { match res { Ok(outbound) => { // TODO: handle the case where this peer is the one that sent the original + trace!(peer_id = %this.peer_id, target = "flashblocks", "Broadcasting flashblocks message"); return Poll::Ready(Some(outbound.encoded())); } Err(e) => { @@ -78,6 +80,9 @@ impl Stream for FlashblocksConnection { return Poll::Ready(None); }; + trace!(peer_id = %this.peer_id, target = "flashblocks", + "Received flashblocks message from peer", + ); match msg.message { FlashblocksProtoMessageKind::FlashblocksPayloadV1(authorized) => { this.handle_flashblocks_payload_v1(authorized, msg.message_type); diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index b9ac054e..fa9d05d3 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -9,6 +9,7 @@ use rollup_boost::FlashblocksPayloadV1; use std::net::SocketAddr; use std::sync::Arc; use tokio::sync::broadcast; +use tracing::info; pub trait FlashblocksP2PNetworHandle: Clone + Unpin + Peers + std::fmt::Debug + 'static {} @@ -54,7 +55,7 @@ impl FlashblocksProtoHandler { flashblock_tx: broadcast::Sender, peer_tx: broadcast::Sender, ) -> Self { - // let flashblock_sender_rx = flashblock_sender_tx.subscribe(); + println!("Protocol created: {authorizer_vk:?}"); let state = Arc::new(Mutex::new(FlashblocksP2PState { flashblock_index: 0, payload_timestamp: 0, @@ -76,6 +77,7 @@ impl ProtocolHandler for FlashblocksProtoHandler< type ConnectionHandler = FlashblocksConnectionHandler; fn on_incoming(&self, _socket_addr: SocketAddr) -> Option { + info!("here4"); Some(FlashblocksConnectionHandler:: { network_handle: self.network_handle.clone(), authorizer_vk: self.authorizer_vk, @@ -90,6 +92,7 @@ impl ProtocolHandler for FlashblocksProtoHandler< _socket_addr: SocketAddr, _peer_id: PeerId, ) -> Option { + info!("here3"); Some(FlashblocksConnectionHandler:: { network_handle: self.network_handle.clone(), authorizer_vk: self.authorizer_vk, From 485adf03f8f943b29350705bb17c990d3fc17e17 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Mon, 14 Jul 2025 15:36:21 -0700 Subject: [PATCH 23/45] get tests passing --- crates/flashblocks-node/tests/p2p.rs | 49 +++++++-------- crates/flashblocks-p2p/src/connection/mod.rs | 3 +- .../flashblocks-p2p/src/protocol/handler.rs | 61 ++++++++++++++++--- 3 files changed, 78 insertions(+), 35 deletions(-) diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index 56025905..1cb26e0e 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -4,7 +4,7 @@ use alloy_primitives::{Address, B256, Bytes, TxHash, U256, address, b256}; use alloy_provider::{Provider, RootProvider}; use alloy_rpc_client::RpcClient; use alloy_rpc_types_engine::PayloadId; -use ed25519_dalek::{SigningKey, VerifyingKey}; +use ed25519_dalek::SigningKey; use flashblocks_p2p::protocol::{ auth::Authorized, handler::FlashblocksProtoHandler, @@ -41,28 +41,12 @@ pub struct NodeContext { } impl NodeContext { - // pub async fn send_payload(&self, payload: FlashblocksPayloadV1) -> eyre::Result<()> { - // self.sender.send(payload).await?; - // rx.await?; - // Ok(()) - // } - pub async fn provider(&self) -> eyre::Result { let url = format!("http://{}", self.http_api_addr); let client = RpcClient::builder().http(url.parse()?); Ok(RootProvider::new(client)) } - - // pub async fn send_test_payloads(&self) -> eyre::Result<()> { - // let base_payload = create_first_payload(); - // self.send_payload(base_payload).await?; - // - // let second_payload = create_second_payload(); - // self.send_payload(second_payload).await?; - // - // Ok(()) - // } } async fn setup_node( @@ -158,7 +142,7 @@ async fn setup_node( }) } -fn create_first_payload() -> FlashblocksPayloadV1 { +fn payload_0() -> FlashblocksPayloadV1 { FlashblocksPayloadV1 { payload_id: PayloadId::new([0; 8]), index: 0, @@ -191,7 +175,7 @@ const TX1_HASH: TxHash = const TX2_HASH: TxHash = b256!("0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8"); -fn create_second_payload() -> FlashblocksPayloadV1 { +fn payload_2() -> FlashblocksPayloadV1 { // Create second payload (index 1) with transactions // tx1 hash: 0x2be2e6f8b01b03b87ae9f0ebca8bbd420f174bef0fbcc18c7802c5378b78f548 (deposit transaction) // tx2 hash: 0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8 @@ -328,17 +312,17 @@ async fn test_peering() -> eyre::Result<()> { let pending_block = provider_1 .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) .await?; - assert_eq!(pending_block.is_none(), true); + assert!(pending_block.is_none()); - let base_payload = create_first_payload(); + let payload_0 = payload_0(); info!("Sending base payload"); let authorization = Authorization::new( - base_payload.payload_id, + payload_0.payload_id, 0, &authorizer, builder.verifying_key(), ); - let authorized = Authorized::new(&builder, authorization, base_payload.clone()); + let authorized = Authorized::new(&builder, authorization, payload_0.clone()); let proto_message = FlashblocksProtoMessage { message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(authorized), message_type: FlashblocksProtoMessageId::FlashblocksPayloadV1, @@ -355,9 +339,22 @@ async fn test_peering() -> eyre::Result<()> { assert_eq!(pending_block.number(), 1); assert_eq!(pending_block.transactions.hashes().len(), 0); - let second_payload = create_second_payload(); - node_1.inbound_tx.send(second_payload.clone())?; - tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + let payload_1 = payload_2(); + info!("Sending base payload"); + let authorization = Authorization::new( + payload_1.payload_id, + 0, + &authorizer, + builder.verifying_key(), + ); + let authorized = Authorized::new(&builder, authorization, payload_1.clone()); + let proto_message = FlashblocksProtoMessage { + message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(authorized), + message_type: FlashblocksProtoMessageId::FlashblocksPayloadV1, + }; + + node_1.outbound_tx.send(proto_message)?; + tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; // Query pending block after sending the second payload with two transactions let block = provider_0 diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/connection/mod.rs index 3e22d364..60f42095 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/connection/mod.rs @@ -20,7 +20,7 @@ use std::{ }; use tokio::sync::broadcast; use tokio_stream::wrappers::BroadcastStream; -use tracing::trace; +use tracing::{debug, trace}; pub(crate) mod handler; @@ -178,6 +178,7 @@ impl FlashblocksConnection { // Broadcast any flashblocks in the cache that are in order while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { // Send the flashblock to the stream + debug!(%flashblock_event.payload_id, %state.flashblock_index, "Publishing new flashblock"); self.flashblock_tx.send(flashblock_event.clone()).ok(); // Update the index state.flashblock_index += 1; diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index fa9d05d3..b6564e98 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -1,5 +1,5 @@ use crate::connection::handler::FlashblocksConnectionHandler; -use crate::protocol::proto::FlashblocksProtoMessage; +use crate::protocol::proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}; use ed25519_dalek::VerifyingKey; use parking_lot::Mutex; use reth::payload::PayloadId; @@ -9,7 +9,7 @@ use rollup_boost::FlashblocksPayloadV1; use std::net::SocketAddr; use std::sync::Arc; use tokio::sync::broadcast; -use tracing::info; +use tracing::{debug, info}; pub trait FlashblocksP2PNetworHandle: Clone + Unpin + Peers + std::fmt::Debug + 'static {} @@ -35,10 +35,8 @@ pub struct FlashblocksProtoHandler { pub network_handle: N, /// Authorizer verifying, used to verify flashblocks payloads. pub authorizer_vk: VerifyingKey, - /// Sender for newly created or received and validated flashblocks payloads - /// which will be broadcasted to all peers. May not be strictly ordered. - /// TODO: we still need an internal listener for this channel to - /// handle locally created flashblocks. + /// Sender for flashblocks payloads which will be broadcasted to all peers. + /// May not be strictly ordered. pub peer_tx: broadcast::Sender, /// Receiver of verified and strictly ordered flashbloacks payloads. /// For consumption by the rpc overlay. @@ -53,9 +51,8 @@ impl FlashblocksProtoHandler { network_handle: N, authorizer_vk: VerifyingKey, flashblock_tx: broadcast::Sender, - peer_tx: broadcast::Sender, + publish_tx: broadcast::Sender, ) -> Self { - println!("Protocol created: {authorizer_vk:?}"); let state = Arc::new(Mutex::new(FlashblocksP2PState { flashblock_index: 0, payload_timestamp: 0, @@ -63,6 +60,54 @@ impl FlashblocksProtoHandler { flashblocks: vec![], })); + // TODO: Clean up duplicated code + let state_clone = state.clone(); + let mut publish_rx = publish_tx.subscribe(); + let peer_tx = broadcast::Sender::new(100); + let peer_tx_clone = peer_tx.clone(); + let flashblock_tx_clone = flashblock_tx.clone(); + tokio::spawn({ + async move { + while let Ok(msg) = publish_rx.recv().await { + let message = match msg.message { + FlashblocksProtoMessageKind::FlashblocksPayloadV1(message) => message, + }; + // Check if we have a payload id and flashblocks to emit. + let mut state = state_clone.lock(); + let len = state.flashblocks.len(); + state + .flashblocks + .resize_with(len.max(message.payload.index as usize + 1), || None); + let flashblock = &mut state.flashblocks[message.payload.index as usize]; + if flashblock.is_none() { + // We haven't seen this index yet + // Add the flashblock to our cache + *flashblock = Some(message.clone().payload); + tracing::debug!( + "Received flashblocks payload with id: {}, index: {}", + message.payload.payload_id, + message.payload.index + ); + let message = FlashblocksProtoMessage { + message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(message), + message_type: msg.message_type, + }; + peer_tx_clone.send(message).ok(); + // Broadcast any flashblocks in the cache that are in order + while let Some(Some(flashblock_event)) = + state.flashblocks.get(state.flashblock_index) + { + // Send the flashblock to the stream + debug!(payload_id = %flashblock_event.payload_id, flashblock_index = %state.flashblock_index, "Publishing new flashblock"); + flashblock_tx_clone.send(flashblock_event.clone()).ok(); + // Update the index + state.flashblock_index += 1; + } + } + } + } + }); + Self { network_handle, authorizer_vk, From 6d252fefbb55400f7cd6dcd4d5a1939f18d6627b Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Mon, 14 Jul 2025 17:13:30 -0700 Subject: [PATCH 24/45] get rid of duplicate code --- crates/flashblocks-node/src/main.rs | 4 +- crates/flashblocks-node/tests/p2p.rs | 4 +- .../flashblocks-p2p/src/connection/handler.rs | 65 ------- crates/flashblocks-p2p/src/lib.rs | 1 - crates/flashblocks-p2p/src/net/mod.rs | 4 +- .../mod.rs => protocol/connection.rs} | 78 ++------ .../flashblocks-p2p/src/protocol/handler.rs | 180 +++++++++++------- crates/flashblocks-p2p/src/protocol/mod.rs | 1 + 8 files changed, 138 insertions(+), 199 deletions(-) delete mode 100644 crates/flashblocks-p2p/src/connection/handler.rs rename crates/flashblocks-p2p/src/{connection/mod.rs => protocol/connection.rs} (65%) diff --git a/crates/flashblocks-node/src/main.rs b/crates/flashblocks-node/src/main.rs index a0968684..5ef91073 100644 --- a/crates/flashblocks-node/src/main.rs +++ b/crates/flashblocks-node/src/main.rs @@ -2,7 +2,7 @@ use clap::Parser; use ed25519_dalek::VerifyingKey; -use flashblocks_p2p::protocol::handler::{ FlashblocksProtoHandler}; +use flashblocks_p2p::protocol::handler::FlashblocksHandler; use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay }; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; @@ -59,7 +59,7 @@ pub fn main() { .await?; - let custom_rlpx_handler = FlashblocksProtoHandler::new( + let custom_rlpx_handler = FlashblocksHandler::new( handle.node.network.clone(), VerifyingKey::default(), inbound_tx, diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index 1cb26e0e..845db460 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -5,9 +5,9 @@ use alloy_provider::{Provider, RootProvider}; use alloy_rpc_client::RpcClient; use alloy_rpc_types_engine::PayloadId; use ed25519_dalek::SigningKey; +use flashblocks_p2p::protocol::handler::FlashblocksHandler; use flashblocks_p2p::protocol::{ auth::Authorized, - handler::FlashblocksProtoHandler, proto::{FlashblocksProtoMessage, FlashblocksProtoMessageId, FlashblocksProtoMessageKind}, }; use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, Metadata}; @@ -105,7 +105,7 @@ async fn setup_node( .launch() .await?; - let custom_rlpx_handler = FlashblocksProtoHandler::new( + let custom_rlpx_handler = FlashblocksHandler::new( node.network.clone(), authorizer.verifying_key(), inbound_tx.clone(), diff --git a/crates/flashblocks-p2p/src/connection/handler.rs b/crates/flashblocks-p2p/src/connection/handler.rs deleted file mode 100644 index 5efc0e7b..00000000 --- a/crates/flashblocks-p2p/src/connection/handler.rs +++ /dev/null @@ -1,65 +0,0 @@ -use std::sync::Arc; - -use super::FlashblocksConnection; -use crate::protocol::{ - handler::{FlashblocksP2PNetworHandle, FlashblocksP2PState}, - proto::FlashblocksProtoMessage, -}; -use ed25519_dalek::VerifyingKey; -use parking_lot::Mutex; -use reth_ethereum::network::{ - api::{Direction, PeerId}, - eth_wire::{capability::SharedCapabilities, multiplex::ProtocolConnection, protocol::Protocol}, - protocol::{ConnectionHandler, OnNotSupported}, -}; -use rollup_boost::FlashblocksPayloadV1; -use tokio::sync::broadcast; -use tokio_stream::wrappers::BroadcastStream; -use tracing::debug; - -/// The connection handler for the flashblocks RLPx protocol. -pub struct FlashblocksConnectionHandler { - pub network_handle: N, - pub authorizer_vk: VerifyingKey, - pub peer_tx: broadcast::Sender, - pub flashblock_tx: broadcast::Sender, - pub state: Arc>, -} - -impl ConnectionHandler for FlashblocksConnectionHandler { - type Connection = FlashblocksConnection; - - fn protocol(&self) -> Protocol { - FlashblocksProtoMessage::protocol() - } - - fn on_unsupported_by_peer( - self, - _supported: &SharedCapabilities, - _direction: Direction, - _peer_id: PeerId, - ) -> OnNotSupported { - OnNotSupported::KeepAlive - } - - fn into_connection( - self, - direction: Direction, - peer_id: PeerId, - conn: ProtocolConnection, - ) -> Self::Connection { - debug!(%peer_id, %direction, "New connection established with flashblocks peer"); - FlashblocksConnection { - conn, - peer_id, - network_handle: self.network_handle, - authorizer_vk: self.authorizer_vk, - peer_tx: self.peer_tx.clone(), - peer_rx: BroadcastStream::new(self.peer_tx.subscribe()), - flashblock_tx: self.flashblock_tx.clone(), - state: self.state.clone(), - payload_id: Default::default(), - received: Vec::new(), - } - } -} diff --git a/crates/flashblocks-p2p/src/lib.rs b/crates/flashblocks-p2p/src/lib.rs index 9bdbbc9d..c6479e8b 100644 --- a/crates/flashblocks-p2p/src/lib.rs +++ b/crates/flashblocks-p2p/src/lib.rs @@ -1,3 +1,2 @@ -pub mod connection; pub mod net; pub mod protocol; diff --git a/crates/flashblocks-p2p/src/net/mod.rs b/crates/flashblocks-p2p/src/net/mod.rs index 6464c640..94da4a54 100644 --- a/crates/flashblocks-p2p/src/net/mod.rs +++ b/crates/flashblocks-p2p/src/net/mod.rs @@ -14,7 +14,7 @@ use rollup_boost::FlashblocksPayloadV1; use tokio::sync::broadcast; use crate::protocol::{ - handler::{FlashblocksP2PNetworHandle, FlashblocksProtoHandler}, + handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}, proto::FlashblocksProtoMessage, }; @@ -62,7 +62,7 @@ where pool: Pool, ) -> eyre::Result { let handle = self.inner.build_network(ctx, pool).await?; - let handler = FlashblocksProtoHandler::::new( + let handler = FlashblocksHandler::::new( handle.clone(), self.authorizer_vk, self.flashblocks_receiver_tx, diff --git a/crates/flashblocks-p2p/src/connection/mod.rs b/crates/flashblocks-p2p/src/protocol/connection.rs similarity index 65% rename from crates/flashblocks-p2p/src/connection/mod.rs rename to crates/flashblocks-p2p/src/protocol/connection.rs index 60f42095..b76e9e0e 100644 --- a/crates/flashblocks-p2p/src/connection/mod.rs +++ b/crates/flashblocks-p2p/src/protocol/connection.rs @@ -1,46 +1,29 @@ use crate::protocol::{ auth::Authorized, - handler::{FlashblocksP2PNetworHandle, FlashblocksP2PState}, - proto::{FlashblocksProtoMessageId, FlashblocksProtoMessageKind}, + handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}, + proto::{FlashblocksProtoMessage, FlashblocksProtoMessageId, FlashblocksProtoMessageKind}, }; -use super::protocol::proto::FlashblocksProtoMessage; use alloy_primitives::bytes::BytesMut; -use ed25519_dalek::VerifyingKey; use futures::{Stream, StreamExt}; -use parking_lot::{Mutex, RwLock}; -use reth::{payload::PayloadId, rpc::api::eth::helpers::trace}; +use reth::payload::PayloadId; use reth_ethereum::network::{api::PeerId, eth_wire::multiplex::ProtocolConnection}; use reth_network::types::ReputationChangeKind; use rollup_boost::FlashblocksPayloadV1; use std::{ pin::Pin, - sync::Arc, task::{Context, Poll, ready}, }; -use tokio::sync::broadcast; use tokio_stream::wrappers::BroadcastStream; -use tracing::{debug, trace}; - -pub(crate) mod handler; +use tracing::trace; pub struct FlashblocksConnection { + pub handler: FlashblocksHandler, pub conn: ProtocolConnection, pub peer_id: PeerId, - pub network_handle: N, - /// Authorizer verifying, used to verify flashblocks payloads. - pub authorizer_vk: VerifyingKey, - /// Sender for newly created or received and validated flashblocks payloads - /// which will be broadcasted to all peers. May not be strictly ordered. - pub peer_tx: broadcast::Sender, /// Receiver for newly created or received and validated flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. pub peer_rx: BroadcastStream, - /// Channel of verified and strictly ordered flashbloacks payloads. - /// For consumption by the rpc overlay. - pub flashblock_tx: broadcast::Sender, - /// Mutable state of the flashblocks protocol. - pub state: Arc>, /// Most recent payload received from this peer. pub payload_id: PayloadId, /// A list of flashblocks indices that we have already received from @@ -98,15 +81,17 @@ impl FlashblocksConnection { message: Authorized, message_type: FlashblocksProtoMessageId, ) { - let mut state = self.state.lock(); + let mut state = self.handler.state.lock(); - if let Err(e) = message.verify(self.authorizer_vk) { + if let Err(e) = message.verify(self.handler.ctx.authorizer_vk) { tracing::warn!( "Failed to verify flashblocks payload: {:?}, error: {}", message, e ); - self.network_handle + self.handler + .ctx + .network_handle .reputation_change(self.peer_id, ReputationChangeKind::BadMessage); return; } @@ -116,7 +101,9 @@ impl FlashblocksConnection { "Received flashblocks payload with outdated timestamp: {}", message.authorization.timestamp ); - self.network_handle + self.handler + .ctx + .network_handle .reputation_change(self.peer_id, ReputationChangeKind::BadMessage); return; } @@ -146,43 +133,20 @@ impl FlashblocksConnection { message.payload.index, self.peer_id ); - self.network_handle + self.handler + .ctx + .network_handle .reputation_change(self.peer_id, ReputationChangeKind::AlreadySeenTransaction); return; } self.received[message.payload.index as usize] = true; - // If we've already seen this index, skip it - // Otherwise, add it to the list - // TODO: perhaps check max index - let len = state.flashblocks.len(); - state - .flashblocks - .resize_with(len.max(message.payload.index as usize + 1), || None); - let flashblock = &mut state.flashblocks[message.payload.index as usize]; - if flashblock.is_none() { - // We haven't seen this index yet - // Add the flashblock to our cache - *flashblock = Some(message.clone().payload); - tracing::debug!( - "Received flashblocks payload with id: {}, index: {}", - message.payload.payload_id, - message.payload.index - ); - // Broadcast the flashblock to all peers, possibly out of order - let message = FlashblocksProtoMessage { + self.handler.ctx.publish( + &mut state, + FlashblocksProtoMessage { message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(message), message_type, - }; - self.peer_tx.send(message).unwrap(); - // Broadcast any flashblocks in the cache that are in order - while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { - // Send the flashblock to the stream - debug!(%flashblock_event.payload_id, %state.flashblock_index, "Publishing new flashblock"); - self.flashblock_tx.send(flashblock_event.clone()).ok(); - // Update the index - state.flashblock_index += 1; - } - } + }, + ); } } diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index b6564e98..3257f3ae 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -1,5 +1,7 @@ -use crate::connection::handler::FlashblocksConnectionHandler; -use crate::protocol::proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}; +use crate::protocol::{ + connection::FlashblocksConnection, + proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}, +}; use ed25519_dalek::VerifyingKey; use parking_lot::Mutex; use reth::payload::PayloadId; @@ -9,7 +11,14 @@ use rollup_boost::FlashblocksPayloadV1; use std::net::SocketAddr; use std::sync::Arc; use tokio::sync::broadcast; -use tracing::{debug, info}; +use tracing::debug; + +use reth_ethereum::network::{ + api::Direction, + eth_wire::{capability::SharedCapabilities, multiplex::ProtocolConnection, protocol::Protocol}, + protocol::{ConnectionHandler, OnNotSupported}, +}; +use tokio_stream::wrappers::BroadcastStream; pub trait FlashblocksP2PNetworHandle: Clone + Unpin + Peers + std::fmt::Debug + 'static {} @@ -29,8 +38,8 @@ pub struct FlashblocksP2PState { } /// The protocol handler takes care of incoming and outgoing connections. -#[derive(Debug)] -pub struct FlashblocksProtoHandler { +#[derive(Clone, Debug)] +pub struct FlashblocksP2PCtx { /// Network handle, used to update peer state. pub network_handle: N, /// Authorizer verifying, used to verify flashblocks payloads. @@ -41,11 +50,18 @@ pub struct FlashblocksProtoHandler { /// Receiver of verified and strictly ordered flashbloacks payloads. /// For consumption by the rpc overlay. pub flashblock_tx: broadcast::Sender, +} + +/// The protocol handler takes care of incoming and outgoing connections. +#[derive(Clone, Debug)] +pub struct FlashblocksHandler { + /// Network handle, used to update peer state. + pub ctx: FlashblocksP2PCtx, /// Mutable state of the flashblocks protocol. pub state: Arc>, } -impl FlashblocksProtoHandler { +impl FlashblocksHandler { /// Creates a new protocol handler with the given state. pub fn new( network_handle: N, @@ -53,83 +69,79 @@ impl FlashblocksProtoHandler { flashblock_tx: broadcast::Sender, publish_tx: broadcast::Sender, ) -> Self { + let peer_tx = broadcast::Sender::new(100); let state = Arc::new(Mutex::new(FlashblocksP2PState { flashblock_index: 0, payload_timestamp: 0, payload_id: PayloadId::default(), flashblocks: vec![], })); + let ctx = FlashblocksP2PCtx { + network_handle: network_handle.clone(), + authorizer_vk, + peer_tx, + flashblock_tx, + }; - // TODO: Clean up duplicated code let state_clone = state.clone(); - let mut publish_rx = publish_tx.subscribe(); - let peer_tx = broadcast::Sender::new(100); - let peer_tx_clone = peer_tx.clone(); - let flashblock_tx_clone = flashblock_tx.clone(); + let ctx_clone = ctx.clone(); tokio::spawn({ async move { - while let Ok(msg) = publish_rx.recv().await { - let message = match msg.message { - FlashblocksProtoMessageKind::FlashblocksPayloadV1(message) => message, - }; - // Check if we have a payload id and flashblocks to emit. + while let Ok(msg) = publish_tx.subscribe().recv().await { let mut state = state_clone.lock(); - let len = state.flashblocks.len(); - state - .flashblocks - .resize_with(len.max(message.payload.index as usize + 1), || None); - let flashblock = &mut state.flashblocks[message.payload.index as usize]; - if flashblock.is_none() { - // We haven't seen this index yet - // Add the flashblock to our cache - *flashblock = Some(message.clone().payload); - tracing::debug!( - "Received flashblocks payload with id: {}, index: {}", - message.payload.payload_id, - message.payload.index - ); - let message = FlashblocksProtoMessage { - message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(message), - message_type: msg.message_type, - }; - peer_tx_clone.send(message).ok(); - // Broadcast any flashblocks in the cache that are in order - while let Some(Some(flashblock_event)) = - state.flashblocks.get(state.flashblock_index) - { - // Send the flashblock to the stream - debug!(payload_id = %flashblock_event.payload_id, flashblock_index = %state.flashblock_index, "Publishing new flashblock"); - flashblock_tx_clone.send(flashblock_event.clone()).ok(); - // Update the index - state.flashblock_index += 1; - } - } + ctx_clone.publish(&mut state, msg); } } }); - Self { - network_handle, - authorizer_vk, - peer_tx, - flashblock_tx, - state, + Self { ctx, state } + } +} + +impl FlashblocksP2PCtx { + /// Commit new and already verified flashblocks payloads to the state + /// broadcast them to peers, and publish them to the stream. + pub fn publish(&self, state: &mut FlashblocksP2PState, msg: FlashblocksProtoMessage) { + // If we've already seen this index, skip it + // Otherwise, add it to the list + let FlashblocksProtoMessageKind::FlashblocksPayloadV1(message) = msg.message; + // TODO: perhaps check max index + let len = state.flashblocks.len(); + state + .flashblocks + .resize_with(len.max(message.payload.index as usize + 1), || None); + let flashblock = &mut state.flashblocks[message.payload.index as usize]; + if flashblock.is_none() { + // We haven't seen this index yet + // Add the flashblock to our cache + *flashblock = Some(message.clone().payload); + tracing::debug!( + "Received flashblocks payload with id: {}, index: {}", + message.payload.payload_id, + message.payload.index + ); + let message = FlashblocksProtoMessage { + message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(message), + message_type: msg.message_type, + }; + self.peer_tx.send(message).ok(); + // Broadcast any flashblocks in the cache that are in order + while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { + // Send the flashblock to the stream + debug!(payload_id = %flashblock_event.payload_id, flashblock_index = %state.flashblock_index, "Publishing new flashblock"); + self.flashblock_tx.send(flashblock_event.clone()).ok(); + // Update the index + state.flashblock_index += 1; + } } } } -impl ProtocolHandler for FlashblocksProtoHandler { - type ConnectionHandler = FlashblocksConnectionHandler; +impl ProtocolHandler for FlashblocksHandler { + type ConnectionHandler = Self; fn on_incoming(&self, _socket_addr: SocketAddr) -> Option { - info!("here4"); - Some(FlashblocksConnectionHandler:: { - network_handle: self.network_handle.clone(), - authorizer_vk: self.authorizer_vk, - peer_tx: self.peer_tx.clone(), - flashblock_tx: self.flashblock_tx.clone(), - state: self.state.clone(), - }) + Some(self.clone()) } fn on_outgoing( @@ -137,13 +149,41 @@ impl ProtocolHandler for FlashblocksProtoHandler< _socket_addr: SocketAddr, _peer_id: PeerId, ) -> Option { - info!("here3"); - Some(FlashblocksConnectionHandler:: { - network_handle: self.network_handle.clone(), - authorizer_vk: self.authorizer_vk, - peer_tx: self.peer_tx.clone(), - flashblock_tx: self.flashblock_tx.clone(), - state: self.state.clone(), - }) + Some(self.clone()) + } +} + +impl ConnectionHandler for FlashblocksHandler { + type Connection = FlashblocksConnection; + + fn protocol(&self) -> Protocol { + FlashblocksProtoMessage::protocol() + } + + fn on_unsupported_by_peer( + self, + _supported: &SharedCapabilities, + _direction: Direction, + _peer_id: PeerId, + ) -> OnNotSupported { + OnNotSupported::KeepAlive + } + + fn into_connection( + self, + direction: Direction, + peer_id: PeerId, + conn: ProtocolConnection, + ) -> Self::Connection { + debug!(%peer_id, %direction, "New connection established with flashblocks peer"); + + FlashblocksConnection { + peer_rx: BroadcastStream::new(self.ctx.peer_tx.subscribe()), + handler: self, + conn, + peer_id, + payload_id: Default::default(), + received: Vec::new(), + } } } diff --git a/crates/flashblocks-p2p/src/protocol/mod.rs b/crates/flashblocks-p2p/src/protocol/mod.rs index 326178a9..f1db9d4d 100644 --- a/crates/flashblocks-p2p/src/protocol/mod.rs +++ b/crates/flashblocks-p2p/src/protocol/mod.rs @@ -1,3 +1,4 @@ pub mod auth; +pub mod connection; pub mod handler; pub mod proto; From a476ee963e4d225cb19b34a777b3e4d357aad5d2 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Tue, 15 Jul 2025 18:21:46 -0700 Subject: [PATCH 25/45] rlp encoding --- Cargo.lock | 2 + Cargo.toml | 1 + crates/flashblocks-node/tests/p2p.rs | 19 +- crates/flashblocks-p2p/Cargo.toml | 1 + crates/flashblocks-p2p/src/net/mod.rs | 11 +- crates/flashblocks-p2p/src/protocol/auth.rs | 36 -- .../src/protocol/connection.rs | 64 ++- .../flashblocks-p2p/src/protocol/handler.rs | 28 +- crates/flashblocks-p2p/src/protocol/mod.rs | 2 - crates/flashblocks-p2p/src/protocol/proto.rs | 89 ---- crates/rollup-boost/Cargo.toml | 1 + crates/rollup-boost/src/flashblocks/mod.rs | 3 + crates/rollup-boost/src/flashblocks/p2p.rs | 458 ++++++++++++++++++ .../src/flashblocks/primitives.rs | 228 ++++++++- crates/rollup-boost/src/server.rs | 43 +- 15 files changed, 752 insertions(+), 234 deletions(-) delete mode 100644 crates/flashblocks-p2p/src/protocol/auth.rs delete mode 100644 crates/flashblocks-p2p/src/protocol/proto.rs create mode 100644 crates/rollup-boost/src/flashblocks/p2p.rs diff --git a/Cargo.lock b/Cargo.lock index 43c40af3..0679f3aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3541,6 +3541,7 @@ name = "flashblocks-p2p" version = "0.1.0" dependencies = [ "alloy-primitives", + "alloy-rlp", "bincode 2.0.1", "blake3", "clap", @@ -10668,6 +10669,7 @@ dependencies = [ "alloy-consensus", "alloy-eips", "alloy-primitives", + "alloy-rlp", "alloy-rpc-types-engine", "alloy-rpc-types-eth", "alloy-serde", diff --git a/Cargo.toml b/Cargo.toml index 457620a3..cd0acd34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ alloy-consensus = "1.0.13" alloy-rpc-types = "1.0.13" alloy-genesis = "1.0.13" alloy-rpc-client = "1.0.13" +alloy-rlp = "0.3.12" alloy-provider = "1.0.13" op-alloy-network = "0.18.7" op-alloy-rpc-types-engine = "0.18.7" diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index 845db460..ab56f5e1 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -6,10 +6,6 @@ use alloy_rpc_client::RpcClient; use alloy_rpc_types_engine::PayloadId; use ed25519_dalek::SigningKey; use flashblocks_p2p::protocol::handler::FlashblocksHandler; -use flashblocks_p2p::protocol::{ - auth::Authorized, - proto::{FlashblocksProtoMessage, FlashblocksProtoMessageId, FlashblocksProtoMessageKind}, -}; use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, Metadata}; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_network::{Peers, PeersInfo, protocol::IntoRlpxSubProtocol as _}; @@ -25,7 +21,8 @@ use reth_optimism_primitives::OpReceipt; use reth_provider::providers::BlockchainProvider; use reth_tasks::{TaskExecutor, TaskManager}; use rollup_boost::{ - Authorization, ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, FlashblocksPayloadV1, + Authorization, Authorized, ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, + FlashblocksP2PMsg, FlashblocksPayloadV1, }; use std::{any::Any, collections::HashMap, net::SocketAddr, str::FromStr, sync::Arc}; use tokio::sync::broadcast; @@ -33,7 +30,7 @@ use tracing::info; pub struct NodeContext { inbound_tx: broadcast::Sender, - outbound_tx: broadcast::Sender, + outbound_tx: broadcast::Sender, pub local_node_record: NodeRecord, http_api_addr: SocketAddr, _node_exit_future: NodeExitFuture, @@ -323,10 +320,7 @@ async fn test_peering() -> eyre::Result<()> { builder.verifying_key(), ); let authorized = Authorized::new(&builder, authorization, payload_0.clone()); - let proto_message = FlashblocksProtoMessage { - message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(authorized), - message_type: FlashblocksProtoMessageId::FlashblocksPayloadV1, - }; + let proto_message = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized); node_1.outbound_tx.send(proto_message)?; tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; @@ -348,10 +342,7 @@ async fn test_peering() -> eyre::Result<()> { builder.verifying_key(), ); let authorized = Authorized::new(&builder, authorization, payload_1.clone()); - let proto_message = FlashblocksProtoMessage { - message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(authorized), - message_type: FlashblocksProtoMessageId::FlashblocksPayloadV1, - }; + let proto_message = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized); node_1.outbound_tx.send(proto_message)?; tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; diff --git a/crates/flashblocks-p2p/Cargo.toml b/crates/flashblocks-p2p/Cargo.toml index de33b548..1ac17331 100644 --- a/crates/flashblocks-p2p/Cargo.toml +++ b/crates/flashblocks-p2p/Cargo.toml @@ -41,6 +41,7 @@ futures = { workspace = true } clap.workspace = true tracing.workspace = true alloy-primitives.workspace = true +alloy-rlp.workspace = true serde_json = "1.0" thiserror = "2" parking_lot = "0.12" diff --git a/crates/flashblocks-p2p/src/net/mod.rs b/crates/flashblocks-p2p/src/net/mod.rs index 94da4a54..d61f2551 100644 --- a/crates/flashblocks-p2p/src/net/mod.rs +++ b/crates/flashblocks-p2p/src/net/mod.rs @@ -10,20 +10,17 @@ use reth_node_builder::{ node::{FullNodeTypes, NodeTypes}, }; use reth_transaction_pool::{PoolTransaction, TransactionPool}; -use rollup_boost::FlashblocksPayloadV1; +use rollup_boost::{FlashblocksP2PMsg, FlashblocksPayloadV1}; use tokio::sync::broadcast; -use crate::protocol::{ - handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}, - proto::FlashblocksProtoMessage, -}; +use crate::protocol::handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}; #[derive(Clone, Debug)] pub struct FlashblocksNetworkBuilder { inner: T, authorizer_vk: VerifyingKey, flashblocks_receiver_tx: broadcast::Sender, - flashblock_sender_tx: broadcast::Sender, + flashblock_sender_tx: broadcast::Sender, } impl FlashblocksNetworkBuilder { @@ -32,7 +29,7 @@ impl FlashblocksNetworkBuilder { inner: T, authorizer_vk: VerifyingKey, flashblocks_receiver_tx: broadcast::Sender, - flashblock_sender_tx: broadcast::Sender, + flashblock_sender_tx: broadcast::Sender, ) -> Self { Self { inner, diff --git a/crates/flashblocks-p2p/src/protocol/auth.rs b/crates/flashblocks-p2p/src/protocol/auth.rs deleted file mode 100644 index 0afd9647..00000000 --- a/crates/flashblocks-p2p/src/protocol/auth.rs +++ /dev/null @@ -1,36 +0,0 @@ -use blake3::hash as blake3_hash; -use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; -use rollup_boost::{Authorization, FlashblocksP2PError}; -use serde::{Deserialize, Serialize}; -use serde_json; - -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Authorized { - pub payload: T, - pub authorization: Authorization, - pub builder_sig: Signature, -} - -impl Authorized { - pub fn new(builder_sk: &SigningKey, authorization: Authorization, payload: T) -> Self { - let hash = blake3_hash(&serde_json::to_vec(&payload).unwrap()); - let builder_sig = builder_sk.sign(hash.as_bytes()); - - Self { - payload, - authorization, - builder_sig, - } - } - - pub fn verify(&self, authorizer_pub: VerifyingKey) -> Result<(), FlashblocksP2PError> { - self.authorization.verify(authorizer_pub)?; - - let hash = blake3_hash(&serde_json::to_vec(&self.payload).unwrap()); - - self.authorization - .builder_pub - .verify(hash.as_bytes(), &self.builder_sig) - .map_err(|_| FlashblocksP2PError::InvalidBuilderSig) - } -} diff --git a/crates/flashblocks-p2p/src/protocol/connection.rs b/crates/flashblocks-p2p/src/protocol/connection.rs index b76e9e0e..2e73e09c 100644 --- a/crates/flashblocks-p2p/src/protocol/connection.rs +++ b/crates/flashblocks-p2p/src/protocol/connection.rs @@ -1,15 +1,12 @@ -use crate::protocol::{ - auth::Authorized, - handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}, - proto::{FlashblocksProtoMessage, FlashblocksProtoMessageId, FlashblocksProtoMessageKind}, -}; +use crate::protocol::handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}; +use alloy_rlp::{Decodable, Encodable, Header}; use alloy_primitives::bytes::BytesMut; use futures::{Stream, StreamExt}; use reth::payload::PayloadId; use reth_ethereum::network::{api::PeerId, eth_wire::multiplex::ProtocolConnection}; use reth_network::types::ReputationChangeKind; -use rollup_boost::FlashblocksPayloadV1; +use rollup_boost::{Authorized, FlashblocksP2PMsg, FlashblocksPayloadV1}; use std::{ pin::Pin, task::{Context, Poll, ready}, @@ -23,7 +20,7 @@ pub struct FlashblocksConnection { pub peer_id: PeerId, /// Receiver for newly created or received and validated flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. - pub peer_rx: BroadcastStream, + pub peer_rx: BroadcastStream, /// Most recent payload received from this peer. pub payload_id: PayloadId, /// A list of flashblocks indices that we have already received from @@ -44,7 +41,13 @@ impl Stream for FlashblocksConnection { Ok(outbound) => { // TODO: handle the case where this peer is the one that sent the original trace!(peer_id = %this.peer_id, target = "flashblocks", "Broadcasting flashblocks message"); - return Poll::Ready(Some(outbound.encoded())); + let mut buf = BytesMut::with_capacity(outbound.length()); + outbound.encode(&mut buf); + trace!(peer_id = %this.peer_id, target = "flashblocks", + "Encoded flashblocks message with length: {}", buf.len()); + let new = FlashblocksP2PMsg::decode(&mut &buf[..]).unwrap(); + println!("Broadcasting flashblocks message: {:?}", new); + return Poll::Ready(Some(buf)); } Err(e) => { tracing::error!( @@ -56,19 +59,38 @@ impl Stream for FlashblocksConnection { } // Check if there are any messages from the peer. + trace!(peer_id = %this.peer_id, target = "flashblocks", + "Polling for messages from peer"); let Some(msg) = ready!(this.conn.poll_next_unpin(cx)) else { + trace!(peer_id = %this.peer_id, target = "flashblocks", + "Connection closed, no more messages from peer"); return Poll::Ready(None); }; - let Some(msg) = FlashblocksProtoMessage::decode_message(&mut &msg[..]) else { - return Poll::Ready(None); + trace!(peer_id = %this.peer_id, target = "flashblocks", + "Received message from peer: {}", msg.len()); + // TODO: handle max buffer size + let msg = match FlashblocksP2PMsg::decode(&mut &msg[..]) { + Ok(msg) => msg, + Err(e) => { + tracing::warn!( + "Failed to decode flashblocks message from peer {}: {}", + this.peer_id, + e + ); + this.handler + .ctx + .network_handle + .reputation_change(this.peer_id, ReputationChangeKind::BadMessage); + return Poll::Ready(None); + } }; trace!(peer_id = %this.peer_id, target = "flashblocks", "Received flashblocks message from peer", ); - match msg.message { - FlashblocksProtoMessageKind::FlashblocksPayloadV1(authorized) => { - this.handle_flashblocks_payload_v1(authorized, msg.message_type); + match msg { + FlashblocksP2PMsg::FlashblocksPayloadV1(authorized) => { + this.handle_flashblocks_payload_v1(authorized); } } } @@ -76,11 +98,7 @@ impl Stream for FlashblocksConnection { } impl FlashblocksConnection { - fn handle_flashblocks_payload_v1( - &mut self, - message: Authorized, - message_type: FlashblocksProtoMessageId, - ) { + fn handle_flashblocks_payload_v1(&mut self, message: Authorized) { let mut state = self.handler.state.lock(); if let Err(e) = message.verify(self.handler.ctx.authorizer_vk) { @@ -141,12 +159,8 @@ impl FlashblocksConnection { } self.received[message.payload.index as usize] = true; - self.handler.ctx.publish( - &mut state, - FlashblocksProtoMessage { - message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(message), - message_type, - }, - ); + self.handler + .ctx + .publish(&mut state, FlashblocksP2PMsg::FlashblocksPayloadV1(message)); } } diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 3257f3ae..040621f3 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -1,13 +1,11 @@ -use crate::protocol::{ - connection::FlashblocksConnection, - proto::{FlashblocksProtoMessage, FlashblocksProtoMessageKind}, -}; +use crate::protocol::connection::FlashblocksConnection; use ed25519_dalek::VerifyingKey; use parking_lot::Mutex; use reth::payload::PayloadId; +use reth_eth_wire::Capability; use reth_ethereum::network::{api::PeerId, protocol::ProtocolHandler}; use reth_network::Peers; -use rollup_boost::FlashblocksPayloadV1; +use rollup_boost::{FlashblocksP2PMsg, FlashblocksPayloadV1}; use std::net::SocketAddr; use std::sync::Arc; use tokio::sync::broadcast; @@ -46,7 +44,7 @@ pub struct FlashblocksP2PCtx { pub authorizer_vk: VerifyingKey, /// Sender for flashblocks payloads which will be broadcasted to all peers. /// May not be strictly ordered. - pub peer_tx: broadcast::Sender, + pub peer_tx: broadcast::Sender, /// Receiver of verified and strictly ordered flashbloacks payloads. /// For consumption by the rpc overlay. pub flashblock_tx: broadcast::Sender, @@ -67,7 +65,7 @@ impl FlashblocksHandler { network_handle: N, authorizer_vk: VerifyingKey, flashblock_tx: broadcast::Sender, - publish_tx: broadcast::Sender, + publish_tx: broadcast::Sender, ) -> Self { let peer_tx = broadcast::Sender::new(100); let state = Arc::new(Mutex::new(FlashblocksP2PState { @@ -96,15 +94,20 @@ impl FlashblocksHandler { Self { ctx, state } } + + /// Returns the capability for the `flashblocks` protocol. + pub fn capability() -> Capability { + Capability::new_static("flashblocks", 1) + } } impl FlashblocksP2PCtx { /// Commit new and already verified flashblocks payloads to the state /// broadcast them to peers, and publish them to the stream. - pub fn publish(&self, state: &mut FlashblocksP2PState, msg: FlashblocksProtoMessage) { + pub fn publish(&self, state: &mut FlashblocksP2PState, msg: FlashblocksP2PMsg) { // If we've already seen this index, skip it // Otherwise, add it to the list - let FlashblocksProtoMessageKind::FlashblocksPayloadV1(message) = msg.message; + let FlashblocksP2PMsg::FlashblocksPayloadV1(message) = msg; // TODO: perhaps check max index let len = state.flashblocks.len(); state @@ -120,10 +123,7 @@ impl FlashblocksP2PCtx { message.payload.payload_id, message.payload.index ); - let message = FlashblocksProtoMessage { - message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(message), - message_type: msg.message_type, - }; + let message = FlashblocksP2PMsg::FlashblocksPayloadV1(message); self.peer_tx.send(message).ok(); // Broadcast any flashblocks in the cache that are in order while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { @@ -157,7 +157,7 @@ impl ConnectionHandler for FlashblocksHandler type Connection = FlashblocksConnection; fn protocol(&self) -> Protocol { - FlashblocksProtoMessage::protocol() + Protocol::new(Self::capability(), 1) } fn on_unsupported_by_peer( diff --git a/crates/flashblocks-p2p/src/protocol/mod.rs b/crates/flashblocks-p2p/src/protocol/mod.rs index f1db9d4d..cf8c8107 100644 --- a/crates/flashblocks-p2p/src/protocol/mod.rs +++ b/crates/flashblocks-p2p/src/protocol/mod.rs @@ -1,4 +1,2 @@ -pub mod auth; pub mod connection; pub mod handler; -pub mod proto; diff --git a/crates/flashblocks-p2p/src/protocol/proto.rs b/crates/flashblocks-p2p/src/protocol/proto.rs deleted file mode 100644 index a55d937c..00000000 --- a/crates/flashblocks-p2p/src/protocol/proto.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Simple RLPx Flashblocks protocol for propagating FlashblocksPayloadV1 messages -//! following [RLPx specs](https://github.com/ethereum/devp2p/blob/master/rlpx.md) - -use alloy_primitives::bytes::{Buf, BufMut, BytesMut}; -use reth_ethereum::network::eth_wire::{Capability, protocol::Protocol}; -use rollup_boost::FlashblocksPayloadV1; -use serde_json; - -use crate::protocol::auth::Authorized; - -#[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum FlashblocksProtoMessageId { - FlashblocksPayloadV1 = 0x00, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum FlashblocksProtoMessageKind { - FlashblocksPayloadV1(Authorized), -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct FlashblocksProtoMessage { - pub message_type: FlashblocksProtoMessageId, - pub message: FlashblocksProtoMessageKind, -} - -impl FlashblocksProtoMessage { - /// Returns the capability for the `flashblocks` protocol. - pub fn capability() -> Capability { - Capability::new_static("flashblocks", 1) - } - - /// Returns the protocol for the `flashblocks` protocol. - pub fn protocol() -> Protocol { - Protocol::new(Self::capability(), 1) - } - - /// Creates a flashblocks payload message - pub fn flashblocks_payload(payload: Authorized) -> Self { - Self { - message_type: FlashblocksProtoMessageId::FlashblocksPayloadV1, - message: FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload), - } - } - - /// Creates a new `FlashblocksProtoMessage` with the given message ID and payload. - pub fn encoded(&self) -> BytesMut { - let mut buf = BytesMut::new(); - buf.put_u8(self.message_type as u8); - match &self.message { - FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload) => { - // Serialize the payload as JSON for transmission - let json = serde_json::to_string(payload).unwrap_or_default(); - buf.put(json.as_bytes()); - } - } - buf - } - - /// Decodes a `FlashblocksProtoMessage` from the given message buffer. - pub fn decode_message(buf: &mut &[u8]) -> Option { - if buf.is_empty() { - return None; - } - let id = buf[0]; - buf.advance(1); - let message_type = match id { - 0x00 => FlashblocksProtoMessageId::FlashblocksPayloadV1, - _ => return None, - }; - - let message = match message_type { - FlashblocksProtoMessageId::FlashblocksPayloadV1 => { - // Deserialize the JSON payload - let json_str = String::from_utf8_lossy(&buf[..]); - match serde_json::from_str::>(&json_str) { - Ok(payload) => FlashblocksProtoMessageKind::FlashblocksPayloadV1(payload), - Err(_) => return None, - } - } - }; - - Some(Self { - message_type, - message, - }) - } -} diff --git a/crates/rollup-boost/Cargo.toml b/crates/rollup-boost/Cargo.toml index 2a973d6c..50b2d3a8 100644 --- a/crates/rollup-boost/Cargo.toml +++ b/crates/rollup-boost/Cargo.toml @@ -24,6 +24,7 @@ alloy-rpc-types-engine.workspace = true alloy-rpc-types-eth.workspace = true alloy-primitives.workspace = true alloy-serde.workspace = true +alloy-rlp.workspace = true tokio-tungstenite.workspace = true metrics-derive.workspace = true diff --git a/crates/rollup-boost/src/flashblocks/mod.rs b/crates/rollup-boost/src/flashblocks/mod.rs index e7cafcff..c5a9412e 100644 --- a/crates/rollup-boost/src/flashblocks/mod.rs +++ b/crates/rollup-boost/src/flashblocks/mod.rs @@ -17,4 +17,7 @@ pub use args::*; mod error; pub use error::*; +mod p2p; +pub use p2p::*; + mod metrics; diff --git a/crates/rollup-boost/src/flashblocks/p2p.rs b/crates/rollup-boost/src/flashblocks/p2p.rs new file mode 100644 index 00000000..d9dcddde --- /dev/null +++ b/crates/rollup-boost/src/flashblocks/p2p.rs @@ -0,0 +1,458 @@ +use alloy_primitives::{B64, Bytes}; +use alloy_rlp::{Decodable, Encodable, Header}; +use alloy_rpc_types_engine::PayloadId; +use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; +use serde::{Deserialize, Serialize}; + +use crate::{FlashblocksP2PError, FlashblocksPayloadV1}; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Authorization { + pub payload_id: PayloadId, + pub timestamp: u64, + pub builder_pub: VerifyingKey, + pub authorizer_sig: Signature, +} + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Authorized { + pub payload: T, + pub authorization: Authorization, + pub builder_sig: Signature, +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Eq)] +#[repr(u8)] +pub enum FlashblocksP2PMsg { + FlashblocksPayloadV1(Authorized) = 0x00, +} + +impl Authorization { + pub fn new( + payload_id: PayloadId, + timestamp: u64, + authorizer_sk: &SigningKey, + builder_pub: VerifyingKey, + ) -> Self { + let mut msg = payload_id.0.to_vec(); + msg.extend_from_slice(×tamp.to_le_bytes()); + msg.extend_from_slice(builder_pub.as_bytes()); + let hash = blake3::hash(&msg); + let sig = authorizer_sk.sign(hash.as_bytes()); + + Self { + payload_id, + timestamp, + builder_pub, + authorizer_sig: sig, + } + } + + pub fn verify(&self, authorizer_pub: VerifyingKey) -> Result<(), FlashblocksP2PError> { + let mut msg = self.payload_id.0.to_vec(); + msg.extend_from_slice(&self.timestamp.to_le_bytes()); + msg.extend_from_slice(self.builder_pub.as_bytes()); + let hash = blake3::hash(&msg); + authorizer_pub + .verify(hash.as_bytes(), &self.authorizer_sig) + .map_err(|_| FlashblocksP2PError::InvalidAuthorizerSig) + } +} + +impl Encodable for Authorization { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + // pre-serialize the key & sig once so we can reuse the bytes & lengths + let pub_bytes = Bytes::copy_from_slice(self.builder_pub.as_bytes()); // 33 bytes + let sig_bytes = Bytes::copy_from_slice(&self.authorizer_sig.to_bytes()); // 64 bytes + + let payload_len = self.payload_id.0.length() + + self.timestamp.length() + + pub_bytes.length() + + sig_bytes.length(); + + Header { + list: true, + payload_length: payload_len, + } + .encode(out); + + // 1. payload_id (inner B64 already Encodable) + self.payload_id.0.encode(out); + // 2. timestamp + self.timestamp.encode(out); + // 3. builder_pub + pub_bytes.encode(out); + // 4. authorizer_sig + sig_bytes.encode(out); + } + + fn length(&self) -> usize { + let pub_bytes = Bytes::copy_from_slice(self.builder_pub.as_bytes()); + let sig_bytes = Bytes::copy_from_slice(&self.authorizer_sig.to_bytes()); + + let payload_len = self.payload_id.0.length() + + self.timestamp.length() + + pub_bytes.length() + + sig_bytes.length(); + + Header { + list: true, + payload_length: payload_len, + } + .length() + + payload_len + } +} + +impl Decodable for Authorization { + fn decode(buf: &mut &[u8]) -> Result { + let header = Header::decode(buf)?; + if !header.list { + return Err(alloy_rlp::Error::UnexpectedString); + } + let mut body = &buf[..header.payload_length as usize]; + + // 1. payload_id + let payload_id = alloy_rpc_types_engine::PayloadId(B64::decode(&mut body)?.into()); + + // 2. timestamp + let timestamp = u64::decode(&mut body)?; + + // 3. builder_pub + let pub_bytes = Bytes::decode(&mut body)?; + let builder_pub = VerifyingKey::try_from(pub_bytes.as_ref()) + .map_err(|_| alloy_rlp::Error::Custom("bad builder_pub"))?; + + // 4. authorizer_sig + let sig_bytes = Bytes::decode(&mut body)?; + let authorizer_sig = Signature::try_from(sig_bytes.as_ref()) + .map_err(|_| alloy_rlp::Error::Custom("bad signature"))?; + + // advance caller’s slice cursor + *buf = &buf[header.payload_length as usize..]; + + Ok(Self { + payload_id, + timestamp, + builder_pub, + authorizer_sig, + }) + } +} + +impl Authorized { + pub fn new(builder_sk: &SigningKey, authorization: Authorization, payload: T) -> Self { + let hash = blake3::hash(&serde_json::to_vec(&payload).unwrap()); + let builder_sig = builder_sk.sign(hash.as_bytes()); + + Self { + payload, + authorization, + builder_sig, + } + } + + pub fn verify(&self, authorizer_pub: VerifyingKey) -> Result<(), FlashblocksP2PError> { + self.authorization.verify(authorizer_pub)?; + + let hash = blake3::hash(&serde_json::to_vec(&self.payload).unwrap()); + + self.authorization + .builder_pub + .verify(hash.as_bytes(), &self.builder_sig) + .map_err(|_| FlashblocksP2PError::InvalidBuilderSig) + } +} + +impl Encodable for Authorized +where + T: Encodable + Serialize, +{ + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + // encode once so we know the length beforehand + let sig_bytes = Bytes::copy_from_slice(&self.builder_sig.to_bytes()); + let payload_len = self.payload.length() + self.authorization.length() + sig_bytes.length(); + + Header { + list: true, + payload_length: payload_len, + } + .encode(out); + + // 1. payload + self.payload.encode(out); + // 2. authorization + self.authorization.encode(out); + // 3. builder signature + sig_bytes.encode(out); + } + + fn length(&self) -> usize { + let sig_bytes = Bytes::copy_from_slice(&self.builder_sig.to_bytes()); + let payload_len = self.payload.length() + self.authorization.length() + sig_bytes.length(); + + Header { + list: true, + payload_length: payload_len, + } + .length() + + payload_len + } +} + +impl Decodable for Authorized +where + T: Decodable + Serialize, +{ + fn decode(buf: &mut &[u8]) -> Result { + let header = Header::decode(buf)?; + if !header.list { + return Err(alloy_rlp::Error::UnexpectedString); + } + + let mut body = &buf[..header.payload_length as usize]; + + // 1. payload + let payload = T::decode(&mut body)?; + // 2. authorization + let authorization = Authorization::decode(&mut body)?; + // 3. builder signature + let sig_bytes = Bytes::decode(&mut body)?; + let builder_sig = Signature::try_from(sig_bytes.as_ref()) + .map_err(|_| alloy_rlp::Error::Custom("bad signature"))?; + + // advance caller’s cursor + *buf = &buf[header.payload_length as usize..]; + + Ok(Self { + payload, + authorization, + builder_sig, + }) + } +} + +impl Encodable for FlashblocksP2PMsg { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + match self { + Self::FlashblocksPayloadV1(p) => { + let len = 1 + p.length(); + Header { + list: true, + payload_length: len, + } + .encode(out); + 0u8.encode(out); + p.encode(out); + } + } + } + + fn length(&self) -> usize { + match self { + Self::FlashblocksPayloadV1(p) => { + let inner_len = 1 + p.length(); + Header { + list: true, + payload_length: inner_len, + } + .length() + + inner_len + } + } + } +} + +impl Decodable for FlashblocksP2PMsg { + fn decode(buf: &mut &[u8]) -> Result { + let header = Header::decode(buf)?; + let mut body = &buf[..header.payload_length as usize]; + + let tag = u8::decode(&mut body)?; + let variant = match tag { + 0x00 => { + Self::FlashblocksPayloadV1(Authorized::::decode(&mut body)?) + } + _ => return Err(alloy_rlp::Error::Custom("unknown variant tag")), + }; + + *buf = &buf[header.payload_length as usize..]; + Ok(variant) + } +} + +#[cfg(test)] +mod tests { + use crate::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; + + use super::*; + use alloy_primitives::{Address, B256, Bloom, U256}; + use alloy_rlp::{Decodable, encode}; + use alloy_rpc_types_eth::Withdrawal; + + fn sample_keys() -> (SigningKey, VerifyingKey) { + // deterministic keys for reproducible tests + let bytes = [0u8; 32]; + let sk = SigningKey::from_bytes(&bytes); + let vk = sk.verifying_key(); + (sk, vk) + } + + fn sample_authorization() -> Authorization { + let (authorizer_sk, _) = sample_keys(); + let (_, builder_vk) = sample_keys(); + Authorization::new( + alloy_rpc_types_engine::PayloadId::default(), + 1_700_000_321, + &authorizer_sk, + builder_vk, + ) + } + + fn sample_diff() -> ExecutionPayloadFlashblockDeltaV1 { + ExecutionPayloadFlashblockDeltaV1 { + state_root: B256::from([0x11; 32]), + receipts_root: B256::from([0x22; 32]), + logs_bloom: Bloom::default(), + gas_used: 21_000, + block_hash: B256::from([0x33; 32]), + transactions: vec![Bytes::from_static(b"\xDE\xAD\xBE\xEF")], + withdrawals: vec![Withdrawal::default()], + withdrawals_root: B256::from([0x44; 32]), + } + } + + fn sample_base() -> ExecutionPayloadBaseV1 { + ExecutionPayloadBaseV1 { + parent_beacon_block_root: B256::from([0x55; 32]), + parent_hash: B256::from([0x66; 32]), + fee_recipient: Address::default(), + prev_randao: B256::from([0x77; 32]), + block_number: 1_234, + gas_limit: 30_000_000, + timestamp: 1_700_000_999, + extra_data: Bytes::from_static(b"hi"), + base_fee_per_gas: U256::from(1_000_000_000u64), + } + } + + fn sample_flashblocks_payload() -> FlashblocksPayloadV1 { + FlashblocksPayloadV1 { + payload_id: PayloadId::default(), + index: 7, + diff: sample_diff(), + metadata: serde_json::json!({ "ok": true }), + base: Some(sample_base()), + } + } + + #[test] + fn authorization_roundtrip() { + let (authorizer_sk, authorizer_vk) = sample_keys(); + let (_, builder_vk) = sample_keys(); + + let auth = Authorization::new( + PayloadId::default(), + 1_700_000_123, + &authorizer_sk, + builder_vk, + ); + + // RLP encode-then-decode + let encoded = encode(&auth); + assert_eq!(encoded.len(), auth.length()); + + let mut slice = encoded.as_ref(); + let decoded = Authorization::decode(&mut slice).expect("decode succeeds"); + assert_eq!(auth, decoded); + assert!(slice.is_empty()); + + // signature verifies + decoded.verify(authorizer_vk).expect("sig valid"); + } + + #[test] + fn tampered_sig_is_rejected() { + let (authorizer_sk, authorizer_vk) = sample_keys(); + let (_, builder_vk) = sample_keys(); + + let mut auth = Authorization::new(PayloadId::default(), 42, &authorizer_sk, builder_vk); + + // flip one bit in the signature + let mut auth_sig_bytes = auth.authorizer_sig.to_bytes(); + auth_sig_bytes[0] ^= 0x01; + auth.authorizer_sig = + Signature::try_from(auth_sig_bytes.as_ref()).expect("valid signature bytes"); + assert!(auth.verify(authorizer_vk).is_err()); + } + + #[test] + fn authorized_roundtrip_and_verify() { + let (builder_sk, builder_vk) = sample_keys(); + let authorization = sample_authorization(); + let payload = sample_flashblocks_payload(); + + let authorized = Authorized::new(&builder_sk, authorization.clone(), payload.clone()); + + // RLP round-trip + let encoded = encode(&authorized); + assert_eq!(encoded.len(), authorized.length()); + + let mut slice = encoded.as_ref(); + let decoded = Authorized::::decode(&mut slice).expect("decode ok"); + assert_eq!(decoded, authorized); + assert!(slice.is_empty(), "decoder consumed all input"); + + decoded + .verify(authorization.builder_pub) + .expect("verify succeeds"); + + let hash = blake3::hash(&serde_json::to_vec(&payload).unwrap()); + builder_vk + .verify(hash.as_bytes(), &decoded.builder_sig) + .expect("builder sig valid"); + } + + #[test] + fn builder_sig_tamper_fails() { + let (builder_sk, _) = sample_keys(); + let authorization = sample_authorization(); + let payload = sample_flashblocks_payload(); + + let mut authorized = Authorized::new(&builder_sk, authorization, payload); + // flip one bit + let mut authorized_sig_bytes = authorized.builder_sig.to_bytes(); + authorized_sig_bytes[0] ^= 0x01; + authorized.builder_sig = + Signature::try_from(authorized_sig_bytes.as_ref()).expect("valid signature bytes"); + assert!( + authorized + .verify(authorized.authorization.builder_pub) + .is_err(), + "tampered sig must be rejected" + ); + } + + #[test] + fn p2p_msg_roundtrip() { + let (builder_sk, _) = sample_keys(); + let authorization = sample_authorization(); + let payload = sample_flashblocks_payload(); + let authorized = Authorized::new(&builder_sk, authorization, payload); + + let msg = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized.clone()); + + let encoded = encode(&msg); + assert_eq!(encoded.len(), msg.length(), "length() must match bytes"); + + let mut slice = encoded.as_ref(); + let decoded = FlashblocksP2PMsg::decode(&mut slice).expect("decode ok"); + + match decoded { + FlashblocksP2PMsg::FlashblocksPayloadV1(inner) => { + assert_eq!(inner, authorized, "inner payload round-trips"); + } + } + assert!(slice.is_empty(), "decoder consumed all input"); + } +} diff --git a/crates/rollup-boost/src/flashblocks/primitives.rs b/crates/rollup-boost/src/flashblocks/primitives.rs index c9263ad0..8ece9660 100644 --- a/crates/rollup-boost/src/flashblocks/primitives.rs +++ b/crates/rollup-boost/src/flashblocks/primitives.rs @@ -1,4 +1,5 @@ -use alloy_primitives::{Address, B256, Bloom, Bytes, U256}; +use alloy_primitives::{Address, B64, B256, Bloom, Bytes, U256}; +use alloy_rlp::{Decodable, Encodable, Header, RlpDecodable, RlpEncodable}; use alloy_rpc_types_engine::PayloadId; use alloy_rpc_types_eth::Withdrawal; use serde::{Deserialize, Serialize}; @@ -9,7 +10,9 @@ use serde_json::Value; /// such as state root, receipts, logs, and new transactions. Other immutable block fields /// like parent hash and block number are excluded since they remain constant throughout /// the block's construction. -#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize, Eq)] +#[derive( + Clone, Debug, PartialEq, Default, Deserialize, Serialize, Eq, RlpEncodable, RlpDecodable, +)] pub struct ExecutionPayloadFlashblockDeltaV1 { /// The state root of the block. pub state_root: B256, @@ -34,7 +37,9 @@ pub struct ExecutionPayloadFlashblockDeltaV1 { /// throughout block construction. This includes fundamental block properties like /// parent hash, block number, and other header fields that are determined at /// block creation and cannot be modified. -#[derive(Clone, Debug, PartialEq, Default, Deserialize, Serialize, Eq)] +#[derive( + Clone, Debug, PartialEq, Default, Deserialize, Serialize, Eq, RlpEncodable, RlpDecodable, +)] pub struct ExecutionPayloadBaseV1 { /// Ecotone parent beacon block root pub parent_beacon_block_root: B256, @@ -65,11 +70,222 @@ pub struct FlashblocksPayloadV1 { pub payload_id: PayloadId, /// The index of the flashblock in the block pub index: u64, - /// The base execution payload configuration - #[serde(skip_serializing_if = "Option::is_none")] - pub base: Option, /// The delta/diff containing modified portions of the execution payload pub diff: ExecutionPayloadFlashblockDeltaV1, /// Additional metadata associated with the flashblock pub metadata: Value, + /// The base execution payload configuration + #[serde(skip_serializing_if = "Option::is_none")] + pub base: Option, +} + +/// Manual RLP implementation because `PayloadId` and `serde_json::Value` are +/// outside of alloy-rlp’s blanket impls. +impl Encodable for FlashblocksPayloadV1 { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + // ---- compute payload length ------------------------------------------------- + let json_bytes = Bytes::from( + serde_json::to_vec(&self.metadata).expect("serialising `metadata` to JSON never fails"), + ); + + // encoded-len helper — empty string is one byte (`0x80`) + let empty_len = 1usize; + + let base_len = self.base.as_ref().map(|b| b.length()).unwrap_or(empty_len); + + let payload_len = self.payload_id.0.length() + + self.index.length() + + self.diff.length() + + json_bytes.length() + + base_len; + + Header { + list: true, + payload_length: payload_len, + } + .encode(out); + + // 1. `payload_id` – the inner `B64` already impls `Encodable` + self.payload_id.0.encode(out); + + // 2. `index` + self.index.encode(out); + + // 3. `diff` + self.diff.encode(out); + + // 4. `metadata` (as raw JSON bytes) + json_bytes.encode(out); + + // 5. `base` (`Option` as “value | empty string”) + if let Some(base) = &self.base { + base.encode(out); + } else { + // RLP encoding for empty value + out.put_u8(0x80); + } + } + + fn length(&self) -> usize { + let json_bytes = Bytes::from( + serde_json::to_vec(&self.metadata).expect("serialising `metadata` to JSON never fails"), + ); + + let empty_len = 1usize; + + let base_len = self.base.as_ref().map(|b| b.length()).unwrap_or(empty_len); + + // list header length + payload length + let payload_length = self.payload_id.0.length() + + self.index.length() + + self.diff.length() + + json_bytes.length() + + base_len; + + Header { + list: true, + payload_length, + } + .length() + + payload_length + } +} + +impl Decodable for FlashblocksPayloadV1 { + fn decode(buf: &mut &[u8]) -> Result { + let header = Header::decode(buf)?; + if !header.list { + return Err(alloy_rlp::Error::UnexpectedString); + } + + // Limit the decoding window to the list payload only. + let mut body = &buf[..header.payload_length as usize]; + + let payload_id = B64::decode(&mut body)?.into(); + let index = u64::decode(&mut body)?; + let diff = ExecutionPayloadFlashblockDeltaV1::decode(&mut body)?; + + // metadata – stored as raw JSON bytes + let meta_bytes = Bytes::decode(&mut body)?; + let metadata: Value = serde_json::from_slice(&meta_bytes) + .map_err(|_| alloy_rlp::Error::Custom("bad JSON"))?; + + // base (`Option`) + let base = if body.first() == Some(&0x80) { + None + } else { + Some(ExecutionPayloadBaseV1::decode(&mut body)?) + }; + + // advance the original buffer cursor + *buf = &buf[header.payload_length as usize..]; + + Ok(Self { + payload_id, + index, + diff, + metadata, + base, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_rlp::{Decodable, encode}; + + fn sample_diff() -> ExecutionPayloadFlashblockDeltaV1 { + ExecutionPayloadFlashblockDeltaV1 { + state_root: B256::from([1u8; 32]), + receipts_root: B256::from([2u8; 32]), + logs_bloom: Bloom::default(), + gas_used: 21_000, + block_hash: B256::from([3u8; 32]), + transactions: vec![Bytes::from(vec![0xde, 0xad, 0xbe, 0xef])], + withdrawals: vec![Withdrawal::default()], + withdrawals_root: B256::from([4u8; 32]), + } + } + + fn sample_base() -> ExecutionPayloadBaseV1 { + ExecutionPayloadBaseV1 { + parent_beacon_block_root: B256::from([5u8; 32]), + parent_hash: B256::from([6u8; 32]), + fee_recipient: Address::from([0u8; 20]), + prev_randao: B256::from([7u8; 32]), + block_number: 123, + gas_limit: 30_000_000, + timestamp: 1_700_000_000, + extra_data: Bytes::from(b"hello".to_vec()), + base_fee_per_gas: U256::from(1_000_000_000u64), + } + } + + #[test] + fn roundtrip_without_base() { + let original = FlashblocksPayloadV1 { + payload_id: PayloadId::default(), + index: 0, + diff: sample_diff(), + metadata: serde_json::json!({ "key": "value" }), + base: None, + }; + + let encoded = encode(&original); + assert_eq!( + encoded.len(), + original.length(), + "length() must match actually-encoded size" + ); + + let mut slice = encoded.as_ref(); + let decoded = FlashblocksPayloadV1::decode(&mut slice).expect("decode succeeds"); + assert_eq!(original, decoded, "round-trip must be loss-less"); + assert!( + slice.is_empty(), + "decoder should consume the entire input buffer" + ); + } + + #[test] + fn roundtrip_with_base() { + let original = FlashblocksPayloadV1 { + payload_id: PayloadId::default(), + index: 42, + diff: sample_diff(), + metadata: serde_json::json!({ "foo": 1, "bar": [1, 2, 3] }), + base: Some(sample_base()), + }; + + let encoded = encode(&original); + assert_eq!(encoded.len(), original.length()); + + let mut slice = encoded.as_ref(); + let decoded = FlashblocksPayloadV1::decode(&mut slice).expect("decode succeeds"); + assert_eq!(original, decoded); + assert!(slice.is_empty()); + } + + #[test] + fn invalid_rlp_is_rejected() { + let valid = FlashblocksPayloadV1 { + payload_id: PayloadId::default(), + index: 1, + diff: sample_diff(), + metadata: serde_json::json!({}), + base: None, + }; + + // Encode, then truncate the last byte to corrupt the payload. + let mut corrupted = encode(&valid); + corrupted.pop(); + + let mut slice = corrupted.as_ref(); + let result = FlashblocksPayloadV1::decode(&mut slice); + assert!( + result.is_err(), + "decoder must flag malformed / truncated input" + ); + } } diff --git a/crates/rollup-boost/src/server.rs b/crates/rollup-boost/src/server.rs index 1ae8f7c4..a1e579ce 100644 --- a/crates/rollup-boost/src/server.rs +++ b/crates/rollup-boost/src/server.rs @@ -1,5 +1,5 @@ use crate::debug_api::ExecutionMode; -use crate::{BlockSelectionPolicy, EngineApiExt, FlashblocksP2PError}; +use crate::{Authorization, BlockSelectionPolicy, EngineApiExt, FlashblocksP2PError}; use crate::{ client::rpc::RpcClient, debug_api::DebugServer, @@ -11,6 +11,7 @@ use crate::{ probe::{Health, Probes}, }; use alloy_primitives::{B256, Bytes, bytes}; +use alloy_rlp::{RlpDecodable, RlpEncodable}; use alloy_rpc_types_engine::{ ExecutionPayload, ExecutionPayloadV3, ForkchoiceState, ForkchoiceUpdated, PayloadId, PayloadStatus, @@ -287,46 +288,6 @@ where } } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Authorization { - pub payload_id: PayloadId, - pub timestamp: u64, - pub builder_pub: VerifyingKey, - pub authorizer_sig: Signature, -} - -impl Authorization { - pub fn new( - payload_id: PayloadId, - timestamp: u64, - authorizer_sk: &SigningKey, - builder_pub: VerifyingKey, - ) -> Self { - let mut msg = payload_id.0.to_vec(); - msg.extend_from_slice(×tamp.to_le_bytes()); - msg.extend_from_slice(builder_pub.as_bytes()); - let hash = blake3::hash(&msg); - let sig = authorizer_sk.sign(hash.as_bytes()); - - Self { - payload_id, - timestamp, - builder_pub, - authorizer_sig: sig, - } - } - - pub fn verify(&self, authorizer_pub: VerifyingKey) -> Result<(), FlashblocksP2PError> { - let mut msg = self.payload_id.0.to_vec(); - msg.extend_from_slice(&self.timestamp.to_le_bytes()); - msg.extend_from_slice(self.builder_pub.as_bytes()); - let hash = blake3::hash(&msg); - authorizer_pub - .verify(hash.as_bytes(), &self.authorizer_sig) - .map_err(|_| FlashblocksP2PError::InvalidAuthorizerSig) - } -} - #[rpc(server, client)] pub trait FlashblocksEngineApi { #[method(name = "engine_forkchoiceUpdatedFlashblocksV1")] From 6c60dddab33c01f506e3a648651db5345aa63bfd Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Tue, 15 Jul 2025 19:50:47 -0700 Subject: [PATCH 26/45] fix rlpx message header --- .../src/protocol/connection.rs | 25 ++----- crates/rollup-boost/Cargo.toml | 2 +- crates/rollup-boost/src/flashblocks/error.rs | 6 ++ crates/rollup-boost/src/flashblocks/p2p.rs | 67 +++++++------------ 4 files changed, 38 insertions(+), 62 deletions(-) diff --git a/crates/flashblocks-p2p/src/protocol/connection.rs b/crates/flashblocks-p2p/src/protocol/connection.rs index 2e73e09c..915d9185 100644 --- a/crates/flashblocks-p2p/src/protocol/connection.rs +++ b/crates/flashblocks-p2p/src/protocol/connection.rs @@ -1,5 +1,5 @@ use crate::protocol::handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}; -use alloy_rlp::{Decodable, Encodable, Header}; +use alloy_rlp::{Buf as _, BufMut, Decodable, Encodable, Header}; use alloy_primitives::bytes::BytesMut; use futures::{Stream, StreamExt}; @@ -12,7 +12,7 @@ use std::{ task::{Context, Poll, ready}, }; use tokio_stream::wrappers::BroadcastStream; -use tracing::trace; +use tracing::{info, trace}; pub struct FlashblocksConnection { pub handler: FlashblocksHandler, @@ -41,13 +41,8 @@ impl Stream for FlashblocksConnection { Ok(outbound) => { // TODO: handle the case where this peer is the one that sent the original trace!(peer_id = %this.peer_id, target = "flashblocks", "Broadcasting flashblocks message"); - let mut buf = BytesMut::with_capacity(outbound.length()); - outbound.encode(&mut buf); - trace!(peer_id = %this.peer_id, target = "flashblocks", - "Encoded flashblocks message with length: {}", buf.len()); - let new = FlashblocksP2PMsg::decode(&mut &buf[..]).unwrap(); - println!("Broadcasting flashblocks message: {:?}", new); - return Poll::Ready(Some(buf)); + + return Poll::Ready(Some(outbound.encode())); } Err(e) => { tracing::error!( @@ -59,17 +54,14 @@ impl Stream for FlashblocksConnection { } // Check if there are any messages from the peer. - trace!(peer_id = %this.peer_id, target = "flashblocks", - "Polling for messages from peer"); - let Some(msg) = ready!(this.conn.poll_next_unpin(cx)) else { - trace!(peer_id = %this.peer_id, target = "flashblocks", - "Connection closed, no more messages from peer"); + let Some(mut msg) = ready!(this.conn.poll_next_unpin(cx)) else { return Poll::Ready(None); }; + // Why aren't we getting here? trace!(peer_id = %this.peer_id, target = "flashblocks", "Received message from peer: {}", msg.len()); // TODO: handle max buffer size - let msg = match FlashblocksP2PMsg::decode(&mut &msg[..]) { + let msg = match FlashblocksP2PMsg::decode(&mut msg) { Ok(msg) => msg, Err(e) => { tracing::warn!( @@ -85,9 +77,6 @@ impl Stream for FlashblocksConnection { } }; - trace!(peer_id = %this.peer_id, target = "flashblocks", - "Received flashblocks message from peer", - ); match msg { FlashblocksP2PMsg::FlashblocksPayloadV1(authorized) => { this.handle_flashblocks_payload_v1(authorized); diff --git a/crates/rollup-boost/Cargo.toml b/crates/rollup-boost/Cargo.toml index 50b2d3a8..295623ec 100644 --- a/crates/rollup-boost/Cargo.toml +++ b/crates/rollup-boost/Cargo.toml @@ -69,6 +69,7 @@ tokio-util = { version = "0.7.13" } ed25519-dalek = { version = "2", features = ["serde"] } blake3 = "1" # fast hashing for payload IDs hex = "0.4" +bytes = "1.2" [dev-dependencies] rand = "0.9.0" @@ -80,7 +81,6 @@ anyhow = "1.0" assert_cmd = "2.0.10" predicates = "3.1.2" tokio-util = { version = "0.7.13" } -bytes = "1.2" reth-rpc-layer = { git = "https://github.com/paradigmxyz/reth.git", tag = "v1.4.7" } ctor = "0.4.1" reqwest = "0.12.15" diff --git a/crates/rollup-boost/src/flashblocks/error.rs b/crates/rollup-boost/src/flashblocks/error.rs index e7f86527..c94c4327 100644 --- a/crates/rollup-boost/src/flashblocks/error.rs +++ b/crates/rollup-boost/src/flashblocks/error.rs @@ -6,4 +6,10 @@ pub enum FlashblocksP2PError { InvalidAuthorizerSig, #[error("invalid builder signature")] InvalidBuilderSig, + #[error("input too short")] + InputTooShort, + #[error("unknown message type")] + UnknownMessageType, + #[error("invalid builder signature")] + Rlp(#[from] alloy_rlp::Error), } diff --git a/crates/rollup-boost/src/flashblocks/p2p.rs b/crates/rollup-boost/src/flashblocks/p2p.rs index d9dcddde..f6520a0e 100644 --- a/crates/rollup-boost/src/flashblocks/p2p.rs +++ b/crates/rollup-boost/src/flashblocks/p2p.rs @@ -1,8 +1,10 @@ use alloy_primitives::{B64, Bytes}; use alloy_rlp::{Decodable, Encodable, Header}; use alloy_rpc_types_engine::PayloadId; +use bytes::{Buf as _, BufMut as _, BytesMut}; use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; use serde::{Deserialize, Serialize}; +use tracing::info; use crate::{FlashblocksP2PError, FlashblocksPayloadV1}; @@ -232,52 +234,33 @@ where } } -impl Encodable for FlashblocksP2PMsg { - fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { +impl FlashblocksP2PMsg { + /// Creates a new `FlashblocksP2PError` with the given message ID and payload. + pub fn encode(&self) -> BytesMut { + let mut buf = BytesMut::new(); match self { - Self::FlashblocksPayloadV1(p) => { - let len = 1 + p.length(); - Header { - list: true, - payload_length: len, - } - .encode(out); - 0u8.encode(out); - p.encode(out); + FlashblocksP2PMsg::FlashblocksPayloadV1(payload) => { + buf.put_u8(0x00); + payload.encode(&mut buf); } } + buf } - fn length(&self) -> usize { - match self { - Self::FlashblocksPayloadV1(p) => { - let inner_len = 1 + p.length(); - Header { - list: true, - payload_length: inner_len, - } - .length() - + inner_len - } + /// Decodes a `FlashblocksP2PError` from the given message buffer. + pub fn decode(buf: &mut BytesMut) -> Result { + if buf.is_empty() { + return Err(FlashblocksP2PError::InputTooShort); } - } -} - -impl Decodable for FlashblocksP2PMsg { - fn decode(buf: &mut &[u8]) -> Result { - let header = Header::decode(buf)?; - let mut body = &buf[..header.payload_length as usize]; - - let tag = u8::decode(&mut body)?; - let variant = match tag { + let id = buf[0]; + buf.advance(1); + match id { 0x00 => { - Self::FlashblocksPayloadV1(Authorized::::decode(&mut body)?) + let payload = Authorized::::decode(&mut &buf[..])?; + Ok(FlashblocksP2PMsg::FlashblocksPayloadV1(payload)) } - _ => return Err(alloy_rlp::Error::Custom("unknown variant tag")), - }; - - *buf = &buf[header.payload_length as usize..]; - Ok(variant) + _ => Err(FlashblocksP2PError::UnknownMessageType), + } } } @@ -442,17 +425,15 @@ mod tests { let msg = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized.clone()); - let encoded = encode(&msg); - assert_eq!(encoded.len(), msg.length(), "length() must match bytes"); + let mut encoded = msg.encode(); - let mut slice = encoded.as_ref(); - let decoded = FlashblocksP2PMsg::decode(&mut slice).expect("decode ok"); + let decoded = FlashblocksP2PMsg::decode(&mut encoded).expect("decode ok"); match decoded { FlashblocksP2PMsg::FlashblocksPayloadV1(inner) => { assert_eq!(inner, authorized, "inner payload round-trips"); } } - assert!(slice.is_empty(), "decoder consumed all input"); + assert_eq!(encoded.remaining(), 0, "decoder consumed all input"); } } From 463c2f72d4779f35a5e8472af8e1312eb98c646f Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Tue, 15 Jul 2025 21:23:27 -0700 Subject: [PATCH 27/45] handle maximum flashblocks message size --- crates/flashblocks-node/tests/p2p.rs | 12 +++------ .../src/protocol/connection.rs | 19 +++++--------- .../flashblocks-p2p/src/protocol/handler.rs | 26 +++++++++++++++++-- crates/rollup-boost/src/flashblocks/p2p.rs | 7 +++-- 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index ab56f5e1..fc8fb5cc 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -8,7 +8,7 @@ use ed25519_dalek::SigningKey; use flashblocks_p2p::protocol::handler::FlashblocksHandler; use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, Metadata}; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; -use reth_network::{Peers, PeersInfo, protocol::IntoRlpxSubProtocol as _}; +use reth_network::{Peers, PeersInfo}; use reth_network_peers::{NodeRecord, PeerId}; use reth_node_builder::{Node, NodeBuilder, NodeConfig, NodeHandle}; use reth_node_core::{ @@ -112,15 +112,11 @@ async fn setup_node( node.network .add_rlpx_sub_protocol(custom_rlpx_handler.into_rlpx_sub_protocol()); - tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; - if let Some((peer_id, addr)) = trusted_peer { // If a trusted peer is provided, add it to the network node.network.add_trusted_peer(peer_id, addr); } - tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; - let http_api_addr = node .rpc_server_handle() .http_local_addr() @@ -297,8 +293,6 @@ async fn test_peering() -> eyre::Result<()> { .await?; let provider_1 = node_1.provider().await?; - tokio::time::sleep(tokio::time::Duration::from_millis(20000)).await; - let latest_block = provider_1 .get_block_by_number(alloy_eips::BlockNumberOrTag::Latest) .await? @@ -311,6 +305,8 @@ async fn test_peering() -> eyre::Result<()> { .await?; assert!(pending_block.is_none()); + tokio::time::sleep(tokio::time::Duration::from_millis(15000)).await; + let payload_0 = payload_0(); info!("Sending base payload"); let authorization = Authorization::new( @@ -322,7 +318,7 @@ async fn test_peering() -> eyre::Result<()> { let authorized = Authorized::new(&builder, authorization, payload_0.clone()); let proto_message = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized); node_1.outbound_tx.send(proto_message)?; - tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; + tokio::time::sleep(tokio::time::Duration::from_millis(10000)).await; // Query pending block after sending the base payload with an empty delta let pending_block = provider_0 diff --git a/crates/flashblocks-p2p/src/protocol/connection.rs b/crates/flashblocks-p2p/src/protocol/connection.rs index 915d9185..2fc991b3 100644 --- a/crates/flashblocks-p2p/src/protocol/connection.rs +++ b/crates/flashblocks-p2p/src/protocol/connection.rs @@ -1,6 +1,4 @@ use crate::protocol::handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}; -use alloy_rlp::{Buf as _, BufMut, Decodable, Encodable, Header}; - use alloy_primitives::bytes::BytesMut; use futures::{Stream, StreamExt}; use reth::payload::PayloadId; @@ -12,7 +10,7 @@ use std::{ task::{Context, Poll, ready}, }; use tokio_stream::wrappers::BroadcastStream; -use tracing::{info, trace}; +use tracing::trace; pub struct FlashblocksConnection { pub handler: FlashblocksHandler, @@ -20,7 +18,7 @@ pub struct FlashblocksConnection { pub peer_id: PeerId, /// Receiver for newly created or received and validated flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. - pub peer_rx: BroadcastStream, + pub peer_rx: BroadcastStream, /// Most recent payload received from this peer. pub payload_id: PayloadId, /// A list of flashblocks indices that we have already received from @@ -38,11 +36,10 @@ impl Stream for FlashblocksConnection { // Check if there are any flashblocks ready to broadcast to our peers. if let Poll::Ready(Some(res)) = this.peer_rx.poll_next_unpin(cx) { match res { - Ok(outbound) => { + Ok(bytes) => { // TODO: handle the case where this peer is the one that sent the original trace!(peer_id = %this.peer_id, target = "flashblocks", "Broadcasting flashblocks message"); - - return Poll::Ready(Some(outbound.encode())); + return Poll::Ready(Some(bytes)); } Err(e) => { tracing::error!( @@ -54,14 +51,12 @@ impl Stream for FlashblocksConnection { } // Check if there are any messages from the peer. - let Some(mut msg) = ready!(this.conn.poll_next_unpin(cx)) else { + let Some(buf) = ready!(this.conn.poll_next_unpin(cx)) else { return Poll::Ready(None); }; - // Why aren't we getting here? - trace!(peer_id = %this.peer_id, target = "flashblocks", - "Received message from peer: {}", msg.len()); + // TODO: handle max buffer size - let msg = match FlashblocksP2PMsg::decode(&mut msg) { + let msg = match FlashblocksP2PMsg::decode(&mut &buf[..]) { Ok(msg) => msg, Err(e) => { tracing::warn!( diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 040621f3..11107647 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -1,4 +1,5 @@ use crate::protocol::connection::FlashblocksConnection; +use alloy_rlp::BytesMut; use ed25519_dalek::VerifyingKey; use parking_lot::Mutex; use reth::payload::PayloadId; @@ -18,6 +19,9 @@ use reth_ethereum::network::{ }; use tokio_stream::wrappers::BroadcastStream; +/// Maximum frame size for flashblocks messages. +const MAX_FRAME: usize = 1 << 24; // 16 MiB + pub trait FlashblocksP2PNetworHandle: Clone + Unpin + Peers + std::fmt::Debug + 'static {} impl FlashblocksP2PNetworHandle for N {} @@ -44,7 +48,7 @@ pub struct FlashblocksP2PCtx { pub authorizer_vk: VerifyingKey, /// Sender for flashblocks payloads which will be broadcasted to all peers. /// May not be strictly ordered. - pub peer_tx: broadcast::Sender, + pub peer_tx: broadcast::Sender, /// Receiver of verified and strictly ordered flashbloacks payloads. /// For consumption by the rpc overlay. pub flashblock_tx: broadcast::Sender, @@ -124,7 +128,25 @@ impl FlashblocksP2PCtx { message.payload.index ); let message = FlashblocksP2PMsg::FlashblocksPayloadV1(message); - self.peer_tx.send(message).ok(); + let bytes = message.encode(); + if bytes.len() > MAX_FRAME { + tracing::error!( + target: "flashblocks", + size = bytes.len(), + max_size = MAX_FRAME, + "FlashblocksP2PMsg too large", + ); + return; + } + if bytes.len() > MAX_FRAME / 2 { + tracing::warn!( + target: "flashblocks", + size = bytes.len(), + max_size = MAX_FRAME, + "FlashblocksP2PMsg almost too large", + ); + } + self.peer_tx.send(bytes).ok(); // Broadcast any flashblocks in the cache that are in order while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { // Send the flashblock to the stream diff --git a/crates/rollup-boost/src/flashblocks/p2p.rs b/crates/rollup-boost/src/flashblocks/p2p.rs index f6520a0e..1b660c1a 100644 --- a/crates/rollup-boost/src/flashblocks/p2p.rs +++ b/crates/rollup-boost/src/flashblocks/p2p.rs @@ -4,7 +4,6 @@ use alloy_rpc_types_engine::PayloadId; use bytes::{Buf as _, BufMut as _, BytesMut}; use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; use serde::{Deserialize, Serialize}; -use tracing::info; use crate::{FlashblocksP2PError, FlashblocksPayloadV1}; @@ -248,7 +247,7 @@ impl FlashblocksP2PMsg { } /// Decodes a `FlashblocksP2PError` from the given message buffer. - pub fn decode(buf: &mut BytesMut) -> Result { + pub fn decode(buf: &mut &[u8]) -> Result { if buf.is_empty() { return Err(FlashblocksP2PError::InputTooShort); } @@ -425,9 +424,9 @@ mod tests { let msg = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized.clone()); - let mut encoded = msg.encode(); + let encoded = msg.encode(); - let decoded = FlashblocksP2PMsg::decode(&mut encoded).expect("decode ok"); + let decoded = FlashblocksP2PMsg::decode(&mut &encoded[..]).expect("decode ok"); match decoded { FlashblocksP2PMsg::FlashblocksPayloadV1(inner) => { From 8298f29e54cad73c257819312d9c10be4074b620 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 16 Jul 2025 10:15:16 -0700 Subject: [PATCH 28/45] cleanup p2p handler --- .../src/protocol/connection.rs | 1 - .../flashblocks-p2p/src/protocol/handler.rs | 67 +++++++++++++------ 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/crates/flashblocks-p2p/src/protocol/connection.rs b/crates/flashblocks-p2p/src/protocol/connection.rs index 2fc991b3..fba8b4a2 100644 --- a/crates/flashblocks-p2p/src/protocol/connection.rs +++ b/crates/flashblocks-p2p/src/protocol/connection.rs @@ -55,7 +55,6 @@ impl Stream for FlashblocksConnection { return Poll::Ready(None); }; - // TODO: handle max buffer size let msg = match FlashblocksP2PMsg::decode(&mut &buf[..]) { Ok(msg) => msg, Err(e) => { diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 11107647..a47082a7 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -19,15 +19,20 @@ use reth_ethereum::network::{ }; use tokio_stream::wrappers::BroadcastStream; -/// Maximum frame size for flashblocks messages. +/// Maximum frame size for rlpx messages. const MAX_FRAME: usize = 1 << 24; // 16 MiB +/// Maximum index for flashblocks payloads. +/// Not intended to ever be hit. Since we resize the flashblocks vector dynamically, +/// this is just a sanity check to prevent excessive memory usage. +const MAX_FLASHBLOCK_INDEX: usize = 100; + pub trait FlashblocksP2PNetworHandle: Clone + Unpin + Peers + std::fmt::Debug + 'static {} impl FlashblocksP2PNetworHandle for N {} /// Protocol state is an helper struct to store the protocol events. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct FlashblocksP2PState { /// The index of the next flashblock to emit over the flashblocks_stream. pub flashblock_index: usize, @@ -72,12 +77,7 @@ impl FlashblocksHandler { publish_tx: broadcast::Sender, ) -> Self { let peer_tx = broadcast::Sender::new(100); - let state = Arc::new(Mutex::new(FlashblocksP2PState { - flashblock_index: 0, - payload_timestamp: 0, - payload_id: PayloadId::default(), - flashblocks: vec![], - })); + let state = Arc::new(Mutex::new(FlashblocksP2PState::default())); let ctx = FlashblocksP2PCtx { network_handle: network_handle.clone(), authorizer_vk, @@ -99,9 +99,9 @@ impl FlashblocksHandler { Self { ctx, state } } - /// Returns the capability for the `flashblocks` protocol. + /// Returns the capability for the `flashblocks v1` p2p rotocol. pub fn capability() -> Capability { - Capability::new_static("flashblocks", 1) + Capability::new_static("flblk", 1) } } @@ -109,23 +109,35 @@ impl FlashblocksP2PCtx { /// Commit new and already verified flashblocks payloads to the state /// broadcast them to peers, and publish them to the stream. pub fn publish(&self, state: &mut FlashblocksP2PState, msg: FlashblocksP2PMsg) { - // If we've already seen this index, skip it - // Otherwise, add it to the list let FlashblocksP2PMsg::FlashblocksPayloadV1(message) = msg; - // TODO: perhaps check max index + + // Resize our array if needed + if message.payload.index as usize > MAX_FLASHBLOCK_INDEX { + tracing::error!( + target: "flashblocks", + index = message.payload.index, + max_index = MAX_FLASHBLOCK_INDEX, + "Received flashblocks payload with index exceeding maximum" + ); + return; + } let len = state.flashblocks.len(); state .flashblocks .resize_with(len.max(message.payload.index as usize + 1), || None); let flashblock = &mut state.flashblocks[message.payload.index as usize]; + + // If we've already seen this index, skip it + // Otherwise, add it to the list if flashblock.is_none() { // We haven't seen this index yet // Add the flashblock to our cache *flashblock = Some(message.clone().payload); - tracing::debug!( - "Received flashblocks payload with id: {}, index: {}", - message.payload.payload_id, - message.payload.index + tracing::trace!( + target = "flashblocks", + payload_id = %message.payload.payload_id, + flashblock_index = message.payload.index, + "queueing flashblock", ); let message = FlashblocksP2PMsg::FlashblocksPayloadV1(message); let bytes = message.encode(); @@ -140,7 +152,7 @@ impl FlashblocksP2PCtx { } if bytes.len() > MAX_FRAME / 2 { tracing::warn!( - target: "flashblocks", + target = "flashblocks", size = bytes.len(), max_size = MAX_FRAME, "FlashblocksP2PMsg almost too large", @@ -149,8 +161,13 @@ impl FlashblocksP2PCtx { self.peer_tx.send(bytes).ok(); // Broadcast any flashblocks in the cache that are in order while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { - // Send the flashblock to the stream - debug!(payload_id = %flashblock_event.payload_id, flashblock_index = %state.flashblock_index, "Publishing new flashblock"); + // Publish the flashblock + debug!( + target = "flashblocks", + payload_id = %flashblock_event.payload_id, + flashblock_index = %state.flashblock_index, + "publishing flashblock" + ); self.flashblock_tx.send(flashblock_event.clone()).ok(); // Update the index state.flashblock_index += 1; @@ -197,7 +214,15 @@ impl ConnectionHandler for FlashblocksHandler peer_id: PeerId, conn: ProtocolConnection, ) -> Self::Connection { - debug!(%peer_id, %direction, "New connection established with flashblocks peer"); + let capability = Self::capability(); + + debug!( + %peer_id, + %direction, + capability = %capability.name, + version = %capability.version, + "new flashblocks connection" + ); FlashblocksConnection { peer_rx: BroadcastStream::new(self.ctx.peer_tx.subscribe()), From fd198aa4cd1a17bd636ec02f3c62f05828491216 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 16 Jul 2025 10:47:33 -0700 Subject: [PATCH 29/45] Don't resend flashblocks to the originating peer --- .../src/protocol/connection.rs | 61 ++++++++++++------- .../flashblocks-p2p/src/protocol/handler.rs | 15 +++-- 2 files changed, 49 insertions(+), 27 deletions(-) diff --git a/crates/flashblocks-p2p/src/protocol/connection.rs b/crates/flashblocks-p2p/src/protocol/connection.rs index fba8b4a2..5e873a92 100644 --- a/crates/flashblocks-p2p/src/protocol/connection.rs +++ b/crates/flashblocks-p2p/src/protocol/connection.rs @@ -18,7 +18,8 @@ pub struct FlashblocksConnection { pub peer_id: PeerId, /// Receiver for newly created or received and validated flashblocks payloads /// which will be broadcasted to all peers. May not be strictly ordered. - pub peer_rx: BroadcastStream, + /// We send bytes over this stream to avoid repeatedly having to serialize the payloads. + pub peer_rx: BroadcastStream<(PayloadId, usize, BytesMut)>, /// Most recent payload received from this peer. pub payload_id: PayloadId, /// A list of flashblocks indices that we have already received from @@ -36,15 +37,26 @@ impl Stream for FlashblocksConnection { // Check if there are any flashblocks ready to broadcast to our peers. if let Poll::Ready(Some(res)) = this.peer_rx.poll_next_unpin(cx) { match res { - Ok(bytes) => { - // TODO: handle the case where this peer is the one that sent the original - trace!(peer_id = %this.peer_id, target = "flashblocks", "Broadcasting flashblocks message"); - return Poll::Ready(Some(bytes)); + Ok((payload_id, flashblock_index, bytes)) => { + // Check if this flashblock actually originated from this peer. + if this.payload_id != payload_id + || this.received.get(flashblock_index) != Some(&true) + { + trace!( + target = "flashblocks", + peer_id = %this.peer_id, + %payload_id, + %flashblock_index, + "Broadcasting flashblock message to peer" + ); + return Poll::Ready(Some(bytes)); + } } - Err(e) => { + Err(error) => { tracing::error!( - "Failed to receive flashblocks message from broadcast stream: {}", - e + target = "flashblocks", + %error, + "Failed to receive flashblocks message from peer_rx" ); } } @@ -57,11 +69,12 @@ impl Stream for FlashblocksConnection { let msg = match FlashblocksP2PMsg::decode(&mut &buf[..]) { Ok(msg) => msg, - Err(e) => { + Err(error) => { tracing::warn!( - "Failed to decode flashblocks message from peer {}: {}", - this.peer_id, - e + target = "flashblocks", + peer_id = %this.peer_id, + %error, + "Failed to decode flashblocks message from peer", ); this.handler .ctx @@ -84,11 +97,12 @@ impl FlashblocksConnection { fn handle_flashblocks_payload_v1(&mut self, message: Authorized) { let mut state = self.handler.state.lock(); - if let Err(e) = message.verify(self.handler.ctx.authorizer_vk) { + if let Err(error) = message.verify(self.handler.ctx.authorizer_vk) { tracing::warn!( - "Failed to verify flashblocks payload: {:?}, error: {}", - message, - e + target = "flashblocks", + peer_id = %self.peer_id, + %error, + "Failed to verify flashblock", ); self.handler .ctx @@ -99,8 +113,10 @@ impl FlashblocksConnection { if message.authorization.timestamp < state.payload_timestamp { tracing::warn!( - "Received flashblocks payload with outdated timestamp: {}", - message.authorization.timestamp + target = "flashblocks", + peer_id = %self.peer_id, + timestamp = message.authorization.timestamp, + "Received flashblock with outdated timestamp", ); self.handler .ctx @@ -129,10 +145,11 @@ impl FlashblocksConnection { // We've already seen this index from this peer. // They could be trying to DOS us. tracing::warn!( - "Received duplicate flashblocks payload with id: {}, index: {}, from peer: {}", - message.payload.payload_id, - message.payload.index, - self.peer_id + target = "flashblocks", + peer_id = %self.peer_id, + payload_id = %message.payload.payload_id, + index = message.payload.index, + "Received duplicate flashblock from peer", ); self.handler .ctx diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index a47082a7..03ad2ba7 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -53,7 +53,7 @@ pub struct FlashblocksP2PCtx { pub authorizer_vk: VerifyingKey, /// Sender for flashblocks payloads which will be broadcasted to all peers. /// May not be strictly ordered. - pub peer_tx: broadcast::Sender, + pub peer_tx: broadcast::Sender<(PayloadId, usize, BytesMut)>, /// Receiver of verified and strictly ordered flashbloacks payloads. /// For consumption by the rpc overlay. pub flashblock_tx: broadcast::Sender, @@ -109,7 +109,7 @@ impl FlashblocksP2PCtx { /// Commit new and already verified flashblocks payloads to the state /// broadcast them to peers, and publish them to the stream. pub fn publish(&self, state: &mut FlashblocksP2PState, msg: FlashblocksP2PMsg) { - let FlashblocksP2PMsg::FlashblocksPayloadV1(message) = msg; + let FlashblocksP2PMsg::FlashblocksPayloadV1(ref message) = msg; // Resize our array if needed if message.payload.index as usize > MAX_FLASHBLOCK_INDEX { @@ -139,8 +139,7 @@ impl FlashblocksP2PCtx { flashblock_index = message.payload.index, "queueing flashblock", ); - let message = FlashblocksP2PMsg::FlashblocksPayloadV1(message); - let bytes = message.encode(); + let bytes = msg.encode(); if bytes.len() > MAX_FRAME { tracing::error!( target: "flashblocks", @@ -158,7 +157,13 @@ impl FlashblocksP2PCtx { "FlashblocksP2PMsg almost too large", ); } - self.peer_tx.send(bytes).ok(); + self.peer_tx + .send(( + message.payload.payload_id, + message.payload.index as usize, + bytes, + )) + .ok(); // Broadcast any flashblocks in the cache that are in order while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { // Publish the flashblock From fd06a6c090ffa4cb969cdcb53bd66d1a8eaa2a1b Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 16 Jul 2025 11:15:55 -0700 Subject: [PATCH 30/45] clear up comments --- crates/flashblocks-p2p/src/protocol/connection.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/flashblocks-p2p/src/protocol/connection.rs b/crates/flashblocks-p2p/src/protocol/connection.rs index 5e873a92..8668120f 100644 --- a/crates/flashblocks-p2p/src/protocol/connection.rs +++ b/crates/flashblocks-p2p/src/protocol/connection.rs @@ -125,7 +125,7 @@ impl FlashblocksConnection { return; } - // Check if this is a new payload + // Check if this is a globally new payload if message.authorization.timestamp > state.payload_timestamp { state.flashblock_index = 0; state.payload_timestamp = message.authorization.timestamp; @@ -133,11 +133,13 @@ impl FlashblocksConnection { state.flashblocks.fill(None); } - // Check if this peer is spamming us with the same payload + // Check if this is a new payload from this peer if self.payload_id != message.payload.payload_id { self.payload_id = message.payload.payload_id; self.received.fill(false); } + + // Check if this peer is spamming us with the same payload index let len = self.received.len(); self.received .resize_with(len.max(message.payload.index as usize + 1), || false); From 495ab62026ba6a72e89786cc37513ff73035615e Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 16 Jul 2025 11:31:57 -0700 Subject: [PATCH 31/45] remove stray if let --- crates/rollup-boost/src/cli.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/rollup-boost/src/cli.rs b/crates/rollup-boost/src/cli.rs index 1dd7e114..95834ac1 100644 --- a/crates/rollup-boost/src/cli.rs +++ b/crates/rollup-boost/src/cli.rs @@ -112,7 +112,6 @@ impl RollupBoostArgs { } else { bail!("Missing L2 Client JWT secret"); }; - if let Some(flashblocks_args) = &self.flashblocks {} let l2_client = RpcClient::new( l2_client_args.l2_url.clone(), From 65125aa31583ec5a6b61d9348ef7338a4514e869 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 16 Jul 2025 15:42:49 -0700 Subject: [PATCH 32/45] reexport ed25519 --- crates/rollup-boost/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/rollup-boost/src/lib.rs b/crates/rollup-boost/src/lib.rs index 5d79271e..99e231cb 100644 --- a/crates/rollup-boost/src/lib.rs +++ b/crates/rollup-boost/src/lib.rs @@ -44,3 +44,7 @@ pub use engine_api::*; mod version; pub use version::*; + +pub mod ed25519_dalek { + pub use ed25519_dalek::*; +} From 7f8299298ed8cdb38e5bcab2f4f23f5d0138ccf6 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 16 Jul 2025 17:11:44 -0700 Subject: [PATCH 33/45] use mpsc for publish channel --- crates/flashblocks-node/src/main.rs | 6 +++--- crates/flashblocks-node/tests/p2p.rs | 14 +++++++------- crates/flashblocks-p2p/src/net/mod.rs | 12 ++++++------ crates/flashblocks-p2p/src/protocol/handler.rs | 6 +++--- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/flashblocks-node/src/main.rs b/crates/flashblocks-node/src/main.rs index 5ef91073..4c270a72 100644 --- a/crates/flashblocks-node/src/main.rs +++ b/crates/flashblocks-node/src/main.rs @@ -8,7 +8,7 @@ use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; use reth_optimism_node::{OpNode, args::RollupArgs}; use rollup_boost::parse_vk; -use tokio::sync::{broadcast }; +use tokio::sync::{broadcast, mpsc }; use tracing::info; #[derive(Debug, Clone, PartialEq, Eq, clap::Args)] @@ -36,7 +36,7 @@ pub fn main() { let rollup_args = args.rollup_args; let chain_spec = builder.config().chain.clone(); let (inbound_tx, inbound_rx) = broadcast::channel(100); - let (outbound_tx, _outbound_rx) = broadcast::channel(100); + let (_publish_tx, publish_rx) = mpsc::unbounded_channel(); let flashblocks_overlay = FlashblocksOverlay::new(chain_spec, inbound_rx); @@ -63,7 +63,7 @@ pub fn main() { handle.node.network.clone(), VerifyingKey::default(), inbound_tx, - outbound_tx, + publish_rx, ); handle diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index fc8fb5cc..22334361 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -25,12 +25,12 @@ use rollup_boost::{ FlashblocksP2PMsg, FlashblocksPayloadV1, }; use std::{any::Any, collections::HashMap, net::SocketAddr, str::FromStr, sync::Arc}; -use tokio::sync::broadcast; +use tokio::sync::{broadcast, mpsc}; use tracing::info; pub struct NodeContext { inbound_tx: broadcast::Sender, - outbound_tx: broadcast::Sender, + publish_tx: mpsc::UnboundedSender, pub local_node_record: NodeRecord, http_api_addr: SocketAddr, _node_exit_future: NodeExitFuture, @@ -51,7 +51,7 @@ async fn setup_node( authorizer: SigningKey, trusted_peer: Option<(PeerId, SocketAddr)>, ) -> eyre::Result { - let (outbound_tx, _outbound_rx) = broadcast::channel(100); + let (publish_tx, publish_rx) = mpsc::unbounded_channel(); let (inbound_tx, inbound_rx) = broadcast::channel(100); let genesis: Genesis = serde_json::from_str(include_str!("assets/genesis.json")).unwrap(); @@ -106,7 +106,7 @@ async fn setup_node( node.network.clone(), authorizer.verifying_key(), inbound_tx.clone(), - outbound_tx.clone(), + publish_rx, ); node.network @@ -127,7 +127,7 @@ async fn setup_node( Ok(NodeContext { inbound_tx, - outbound_tx, + publish_tx, local_node_record, http_api_addr, _node_exit_future: node_exit_future, @@ -317,7 +317,7 @@ async fn test_peering() -> eyre::Result<()> { ); let authorized = Authorized::new(&builder, authorization, payload_0.clone()); let proto_message = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized); - node_1.outbound_tx.send(proto_message)?; + node_1.publish_tx.send(proto_message)?; tokio::time::sleep(tokio::time::Duration::from_millis(10000)).await; // Query pending block after sending the base payload with an empty delta @@ -340,7 +340,7 @@ async fn test_peering() -> eyre::Result<()> { let authorized = Authorized::new(&builder, authorization, payload_1.clone()); let proto_message = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized); - node_1.outbound_tx.send(proto_message)?; + node_1.publish_tx.send(proto_message)?; tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; // Query pending block after sending the second payload with two transactions diff --git a/crates/flashblocks-p2p/src/net/mod.rs b/crates/flashblocks-p2p/src/net/mod.rs index d61f2551..eec0dff7 100644 --- a/crates/flashblocks-p2p/src/net/mod.rs +++ b/crates/flashblocks-p2p/src/net/mod.rs @@ -11,16 +11,16 @@ use reth_node_builder::{ }; use reth_transaction_pool::{PoolTransaction, TransactionPool}; use rollup_boost::{FlashblocksP2PMsg, FlashblocksPayloadV1}; -use tokio::sync::broadcast; +use tokio::sync::{broadcast, mpsc}; use crate::protocol::handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}; -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct FlashblocksNetworkBuilder { inner: T, authorizer_vk: VerifyingKey, flashblocks_receiver_tx: broadcast::Sender, - flashblock_sender_tx: broadcast::Sender, + publish_rx: mpsc::UnboundedReceiver, } impl FlashblocksNetworkBuilder { @@ -29,13 +29,13 @@ impl FlashblocksNetworkBuilder { inner: T, authorizer_vk: VerifyingKey, flashblocks_receiver_tx: broadcast::Sender, - flashblock_sender_tx: broadcast::Sender, + publish_rx: mpsc::UnboundedReceiver, ) -> Self { Self { inner, authorizer_vk, flashblocks_receiver_tx, - flashblock_sender_tx, + publish_rx, } } } @@ -63,7 +63,7 @@ where handle.clone(), self.authorizer_vk, self.flashblocks_receiver_tx, - self.flashblock_sender_tx, + self.publish_rx, ); handle.add_rlpx_sub_protocol(handler.into_rlpx_sub_protocol()); diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 03ad2ba7..60e2048d 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -9,7 +9,7 @@ use reth_network::Peers; use rollup_boost::{FlashblocksP2PMsg, FlashblocksPayloadV1}; use std::net::SocketAddr; use std::sync::Arc; -use tokio::sync::broadcast; +use tokio::sync::{broadcast, mpsc}; use tracing::debug; use reth_ethereum::network::{ @@ -74,7 +74,7 @@ impl FlashblocksHandler { network_handle: N, authorizer_vk: VerifyingKey, flashblock_tx: broadcast::Sender, - publish_tx: broadcast::Sender, + mut publish_rx: mpsc::UnboundedReceiver, ) -> Self { let peer_tx = broadcast::Sender::new(100); let state = Arc::new(Mutex::new(FlashblocksP2PState::default())); @@ -89,7 +89,7 @@ impl FlashblocksHandler { let ctx_clone = ctx.clone(); tokio::spawn({ async move { - while let Ok(msg) = publish_tx.subscribe().recv().await { + while let Some(msg) = publish_rx.recv().await { let mut state = state_clone.lock(); ctx_clone.publish(&mut state, msg); } From ba3654830657de7088dc4d249a3e26cb762f69ef Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 16 Jul 2025 19:15:56 -0700 Subject: [PATCH 34/45] feat: reth 1.5.1 --- Cargo.toml | 20 ++++++++-------- crates/flashblocks-node/Cargo.toml | 38 +++++++++++++++--------------- crates/flashblocks-p2p/Cargo.toml | 15 ------------ crates/flashblocks-rpc/Cargo.toml | 38 +++++++++++++++--------------- 4 files changed, 48 insertions(+), 63 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cd0acd34..b1006071 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,16 +33,16 @@ url = "2.2.0" sha2 = { version = "0.10", default-features = false } # Reth deps -reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-eth-wire = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-network = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-network-peers = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-eth-wire = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-network = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-network-peers = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } # Alloy libraries alloy-rpc-types-engine = "1.0.13" diff --git a/crates/flashblocks-node/Cargo.toml b/crates/flashblocks-node/Cargo.toml index 46ac8902..2a561176 100644 --- a/crates/flashblocks-node/Cargo.toml +++ b/crates/flashblocks-node/Cargo.toml @@ -9,27 +9,27 @@ rollup-boost.workspace = true flashblocks-p2p.workspace = true flashblocks-rpc.workspace = true -reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0", features = [ +reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1", features = [ "test-utils", ] } -reth-e2e-test-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-tasks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-e2e-test-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-tasks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-ethereum = { workspace = true, features = ["node", "network", "cli"] } reth-eth-wire = { workspace = true } diff --git a/crates/flashblocks-p2p/Cargo.toml b/crates/flashblocks-p2p/Cargo.toml index 1ac17331..a5055a44 100644 --- a/crates/flashblocks-p2p/Cargo.toml +++ b/crates/flashblocks-p2p/Cargo.toml @@ -13,21 +13,6 @@ reth-node-api = { workspace = true } reth-node-builder = { workspace = true } reth-provider = { workspace = true } reth-transaction-pool = { workspace = true } -# reth-node-builder.workspace = true -# reth-optimism-chainspec.workspace = true -# reth-optimism-node.workspace = true -# reth-optimism-primitives.workspace = true -# reth-optimism-payload-builder.workspace = true -# reth-optimism-rpc.workspace = true -# reth-optimism-forks.workspace = true -# reth-provider.workspace = true -# reth-trie-db.workspace = true -# reth-transaction-pool.workspace = true -# reth-node-api.workspace = true -# alloy-primitives.workspace = true -# op-alloy-consensus.workspace = true -# alloy-rpc-types-eth.workspace = true - ed25519-dalek = { version = "2", features = ["serde"] } blake3 = "1" # fast hashing for payload IDs diff --git a/crates/flashblocks-rpc/Cargo.toml b/crates/flashblocks-rpc/Cargo.toml index 0e30b946..8561ed80 100644 --- a/crates/flashblocks-rpc/Cargo.toml +++ b/crates/flashblocks-rpc/Cargo.toml @@ -8,27 +8,27 @@ license = "MIT" rollup-boost.workspace = true flashblocks-p2p.workspace = true -reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0", features = [ +reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1", features = [ "test-utils", ] } -reth-e2e-test-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-tasks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } -reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.0" } +reth-e2e-test-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-tasks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } +reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-ethereum = { workspace = true, features = ["node", "network", "cli"] } reth-eth-wire = { workspace = true } From a7b4973180adeca0cde3ab490a938c062eb93eda Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 16 Jul 2025 19:19:51 -0700 Subject: [PATCH 35/45] chore: cargo update --- Cargo.lock | 819 +++++++++++++++++++++++++++-------------------------- 1 file changed, 424 insertions(+), 395 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0679f3aa..91beb4de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,9 +97,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19a9cc9d81ace3da457883b0bdf76776e55f1b84219a9e9d55c27ad308548d3f" +checksum = "5674914c2cfdb866c21cb0c09d82374ee39a1395cf512e7515f4c014083b3fff" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b77018eec2154eb158869f9f2914a3ea577adf87b11be2764d4795d5ccccf7" +checksum = "ca3b746060277f3d7f9c36903bb39b593a741cb7afcb0044164c28f0e9b673f0" dependencies = [ "alloy-eips", "alloy-primitives", @@ -136,9 +136,9 @@ dependencies = [ [[package]] name = "alloy-consensus-any" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bf8b058ff364d6e94bcd2979d7da1862e94d2987065a4eb41fa9eac36e028a" +checksum = "bf98679329fa708fa809ea596db6d974da892b068ad45e48ac1956f582edf946" dependencies = [ "alloy-consensus", "alloy-eips", @@ -212,9 +212,9 @@ dependencies = [ [[package]] name = "alloy-eips" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d134f3ac4926124eaf521a1031d11ea98816df3d39fc446fcfd6b36884603f" +checksum = "f562a81278a3ed83290e68361f2d1c75d018ae3b8589a314faf9303883e18ec9" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -235,9 +235,9 @@ dependencies = [ [[package]] name = "alloy-evm" -version = "0.12.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff5aae4c6dc600734b206b175f3200085ee82dcdaa388760358830a984ca9869" +checksum = "ef2d6e0448bfd057a4438226b3d2fd547a0530fa4226217dfb1682d09f108bd4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -255,22 +255,23 @@ dependencies = [ [[package]] name = "alloy-genesis" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb1c2792605e648bdd1fddcfed8ce0d39d3db495c71d2240cb53df8aee8aea1f" +checksum = "dc41384e9ab8c9b2fb387c52774d9d432656a28edcda1c2d4083e96051524518" dependencies = [ "alloy-eips", "alloy-primitives", "alloy-serde", "alloy-trie", "serde", + "serde_with", ] [[package]] name = "alloy-hardforks" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ce138b29a2f8e7ed97c064af8359dfa6559c12cba5e821ae4eb93081a56557e" +checksum = "819a3620fe125e0fff365363315ee5e24c23169173b19747dfd6deba33db8990" dependencies = [ "alloy-chains", "alloy-eip2124", @@ -294,9 +295,9 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31cfdacfeb6b6b40bf6becf92e69e575c68c9f80311c3961d019e29c0b8d6be2" +checksum = "12c454fcfcd5d26ed3b8cae5933cbee9da5f0b05df19b46d4bd4446d1f082565" dependencies = [ "alloy-primitives", "alloy-sol-types", @@ -309,9 +310,9 @@ dependencies = [ [[package]] name = "alloy-network" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de68a3f09cd9ab029cf87d08630e1336ca9a530969689fd151d505fa888a2603" +checksum = "42d6d39eabe5c7b3d8f23ac47b0b683b99faa4359797114636c66e0743103d05" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -335,9 +336,9 @@ dependencies = [ [[package]] name = "alloy-network-primitives" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc2689c8addfc43461544d07a6f5f3a3e1f5f4efae61206cb5783dc383cfc8f" +checksum = "3704fa8b7ba9ba3f378d99b3d628c8bc8c2fc431b709947930f154e22a8368b6" dependencies = [ "alloy-consensus", "alloy-eips", @@ -348,9 +349,9 @@ dependencies = [ [[package]] name = "alloy-op-evm" -version = "0.12.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "588a87b77b30452991151667522d2f2f724cec9c2ec6602e4187bc97f66d8095" +checksum = "98354b9c3d50de701a63693d5b6a37e468a93b970b2224f934dd745c727ef998" dependencies = [ "alloy-consensus", "alloy-eips", @@ -365,9 +366,9 @@ dependencies = [ [[package]] name = "alloy-op-hardforks" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a9a510692bef4871797062ca09ec7873c45dc68c7f3f72291165320f53606a3" +checksum = "2090f21bb6df43e147d976e754bc9a007ca851badbfc6685377aa679b5f151d9" dependencies = [ "alloy-chains", "alloy-hardforks", @@ -407,9 +408,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ced931220f547d30313530ad315654b7862ef52631c90ab857d792865f84a7d" +checksum = "08800e8cbe70c19e2eb7cf3d7ff4b28bdd9b3933f8e1c8136c7d910617ba03bf" dependencies = [ "alloy-chains", "alloy-consensus", @@ -450,9 +451,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e37d6cf286fd30bacac525ab1491f9d1030d39ecce237821f2a5d5922eb9a37" +checksum = "ae68457a2c2ead6bd7d7acb5bf5f1623324b1962d4f8e7b0250657a3c3ab0a0b" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -493,9 +494,9 @@ dependencies = [ [[package]] name = "alloy-rpc-client" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1d1eac6e48b772c7290f0f79211a0e822a38b057535b514cc119abd857d5b6" +checksum = "162301b5a57d4d8f000bf30f4dcb82f9f468f3e5e846eeb8598dd39e7886932c" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -504,7 +505,6 @@ dependencies = [ "alloy-transport-http", "alloy-transport-ipc", "alloy-transport-ws", - "async-stream", "futures", "pin-project", "reqwest", @@ -514,16 +514,15 @@ dependencies = [ "tokio-stream", "tower 0.5.2", "tracing", - "tracing-futures", "url", "wasmtimer", ] [[package]] name = "alloy-rpc-types" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8589c6ae318fcc9624d42e9166f7f82b630d9ad13e180c52addf20b93a8af266" +checksum = "6cd8ca94ae7e2b32cc3895d9981f3772aab0b4756aa60e9ed0bcfee50f0e1328" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -534,9 +533,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-admin" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0182187bcbe47f3a737f5eced007b7788d4ed37aba19d43fd3df123169b3b05e" +checksum = "e7bff682e76f3f72e9ddc75e54a1bd1db5ce53cbdf2cce2d63a3a981437f78f5" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -546,9 +545,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "754d5062b594ed300a3bb0df615acb7bacdbd7bd1cd1a6e5b59fb936c5025a13" +checksum = "9f3ff6a778ebda3deaed9af17930d678611afe1effa895c4260b61009c314f82" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -558,9 +557,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02cfd7ecb21a1bfe68ac6b551172e4d41f828bcc33a2e1563a65d482d4efc1cf" +checksum = "076b47e834b367d8618c52dd0a0d6a711ddf66154636df394805300af4923b8a" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -569,9 +568,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c1ddf8fb2e41fa49316185d7826ed034f55819e0017e65dc6715f911b8a1ee" +checksum = "48f39da9b760e78fc3f347fba4da257aa6328fb33f73682b26cc0a6874798f7d" dependencies = [ "alloy-eips", "alloy-primitives", @@ -587,9 +586,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-debug" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c81ae89a04859751bac72e5e73459bceb3e6a4d2541f2f1374e35be358fd171" +checksum = "94a2a86ad7b7d718c15e79d0779bd255561b6b22968dc5ed2e7c0fbc43bb55fe" dependencies = [ "alloy-primitives", "serde", @@ -597,9 +596,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662b720c498883427ffb9f5e38c7f02b56ac5c0cdd60b457e88ce6b6a20b9ce9" +checksum = "4ba838417c42e8f1fe5eb4f4bbfacb7b5d4b9e615b8d2e831b921e04bf0bed62" dependencies = [ "alloy-consensus", "alloy-eips", @@ -617,9 +616,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb082c325bdfd05a7c71f52cd1060e62491fbf6edf55962720bdc380847b0784" +checksum = "2c2f847e635ec0be819d06e2ada4bcc4e4204026a83c4bfd78ae8d550e027ae7" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -633,14 +632,15 @@ dependencies = [ "itertools 0.14.0", "serde", "serde_json", + "serde_with", "thiserror 2.0.12", ] [[package]] name = "alloy-rpc-types-mev" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c1b50012f55de4a6d58ee9512944089fa61a835e6fe3669844075bb6e0312e" +checksum = "fb1c9b23cedf70aeb99ea9f16b78cdf902f524e227922fb340e3eb899ebe96dc" dependencies = [ "alloy-consensus", "alloy-eips", @@ -653,9 +653,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf52c884c7114c5d1f1f2735634ba0f6579911427281fb02cbd5cb8147723ca" +checksum = "6fc58180302a94c934d455eeedb3ecb99cdc93da1dbddcdbbdb79dd6fe618b2a" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -667,9 +667,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-txpool" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4fd0df1af2ed62d02e7acbc408a162a06f30cb91550c2ec34b11c760cdc0ba" +checksum = "0f9f089d78bb94148e0fcfda087d4ce5fd35a7002847b5e90610c0fcb140f7b4" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -679,9 +679,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f26c17270c2ac1bd555c4304fe067639f0ddafdd3c8d07a200b2bb5a326e03" +checksum = "ae699248d02ade9db493bbdae61822277dc14ae0f82a5a4153203b60e34422a6" dependencies = [ "alloy-primitives", "arbitrary", @@ -691,9 +691,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d9fd649d6ed5b8d7e5014e01758efb937e8407124b182a7f711bf487a1a2697" +checksum = "3cf7d793c813515e2b627b19a15693960b3ed06670f9f66759396d06ebe5747b" dependencies = [ "alloy-primitives", "async-trait", @@ -706,9 +706,9 @@ dependencies = [ [[package]] name = "alloy-signer-local" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c288c5b38be486bb84986701608f5d815183de990e884bb747f004622783e125" +checksum = "51a424bc5a11df0d898ce0fd15906b88ebe2a6e4f17a514b51bc93946bb756bd" dependencies = [ "alloy-consensus", "alloy-network", @@ -794,9 +794,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b790b89e31e183ae36ac0a1419942e21e94d745066f5281417c3e4299ea39e" +checksum = "4f317d20f047b3de4d9728c556e2e9a92c9a507702d2016424cd8be13a74ca5e" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -817,9 +817,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f643645a33a681d09ac1ca2112014c2ca09c68aad301da4400484d59c746bc70" +checksum = "ff084ac7b1f318c87b579d221f11b748341d68b9ddaa4ffca5e62ed2b8cfefb4" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -832,9 +832,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c2d843199d0bdb4cbed8f1b6f2da7f68bcb9c5da7f57e789009e4e7e76d1bec" +checksum = "edb099cdad8ed2e6a80811cdf9bbf715ebf4e34c981b4a6e2d1f9daacbf8b218" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -852,9 +852,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d27aae8c7a6403d3d3e874ad2eeeadbf46267b614bac2d4d82786b9b8496464" +checksum = "0e915e1250dc129ad48d264573ccd08e4716fdda564a772fd217875b8459aff9" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -890,9 +890,9 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.16" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ef40a046b9bf141afc440cef596c79292708aade57c450dc74e843270fd8e7" +checksum = "1154c8187a5ff985c95a8b2daa2fedcf778b17d7668e5e50e556c4ff9c881154" dependencies = [ "alloy-primitives", "darling", @@ -1330,9 +1330,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40f6024f3f856663b45fd0c9b6f2024034a702f453549449e0d84a305900dad4" +checksum = "ddb939d66e4ae03cee6091612804ba446b12878410cfa17f785f4dd67d4014e8" dependencies = [ "brotli", "flate2", @@ -1434,9 +1434,9 @@ checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "aws-lc-rs" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" +checksum = "08b5d4e069cbc868041a64bd68dc8cb39a0d79585cd6c5a24caa8c2d622121be" dependencies = [ "aws-lc-sys", "zeroize", @@ -1444,9 +1444,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" +checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" dependencies = [ "bindgen 0.69.5", "cc", @@ -2186,9 +2186,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "castaway" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" dependencies = [ "rustversion", ] @@ -2269,9 +2269,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.40" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" dependencies = [ "clap_builder", "clap_derive", @@ -2279,9 +2279,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" dependencies = [ "anstream", "anstyle", @@ -2291,9 +2291,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.40" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck", "proc-macro2", @@ -2553,9 +2553,9 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -2681,9 +2681,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.3" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +checksum = "373b7c5dbd637569a2cca66e8d66b8c446a1e7bf064ea321d265d7b3dfe7c97e" dependencies = [ "cfg-if", "cpufeatures", @@ -3120,9 +3120,9 @@ dependencies = [ [[package]] name = "ed25519-dalek" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" dependencies = [ "curve25519-dalek", "ed25519", @@ -3421,9 +3421,9 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.9" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "filetime" @@ -4342,9 +4342,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" dependencies = [ "base64 0.22.1", "bytes", @@ -4796,6 +4796,17 @@ dependencies = [ "memoffset", ] +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "ipconfig" version = "0.3.2" @@ -5577,9 +5588,9 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" dependencies = [ "libc", ] @@ -5900,12 +5911,11 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "notify" -version = "8.0.0" +version = "8.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fee8403b3d66ac7b26aee6e40a897d85dc5ce26f44da36b8b73e987cc52e943" +checksum = "3163f59cd3fa0e9ef8c32f242966a7b9994fd7378366099593e0e73077cd8c97" dependencies = [ "bitflags 2.9.1", - "filetime", "fsevent-sys", "inotify", "kqueue", @@ -5914,7 +5924,7 @@ dependencies = [ "mio", "notify-types", "walkdir", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -6066,12 +6076,13 @@ dependencies = [ [[package]] name = "nybbles" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d51b0175c49668a033fe7cc69080110d9833b291566cdf332905f3ad9c68a0" +checksum = "675b3a54e5b12af997abc8b6638b0aee51a28caedab70d4967e0d5db3a3f1d06" dependencies = [ "alloy-rlp", "arbitrary", + "cfg-if", "proptest", "ruint", "serde", @@ -6211,9 +6222,9 @@ dependencies = [ [[package]] name = "op-revm" -version = "7.0.1" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b97d2b54651fcd2955b454e86b2336c031e17925a127f4c44e2b63b2eeda923" +checksum = "ee9ba9cab294a5ed02afd1a1060220762b3c52911acab635db33822e93f7276d" dependencies = [ "auto_impl", "once_cell", @@ -7270,9 +7281,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "regress" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ef7fa9ed0256d64a688a3747d0fef7a88851c18a5e1d57f115f38ec2e09366" +checksum = "145bb27393fe455dd64d6cbc8d059adfa392590a45eadf079c01b11857e7b010" dependencies = [ "hashbrown 0.15.4", "memchr", @@ -7280,9 +7291,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.21" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8cea6b35bcceb099f30173754403d2eba0a5dc18cea3630fccd88251909288" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64 0.22.1", "bytes", @@ -7335,8 +7346,8 @@ checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" [[package]] name = "reth" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-rpc-types", "aquamarine", @@ -7381,8 +7392,8 @@ dependencies = [ [[package]] name = "reth-basic-payload-builder" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7405,8 +7416,8 @@ dependencies = [ [[package]] name = "reth-chain-state" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7436,8 +7447,8 @@ dependencies = [ [[package]] name = "reth-chainspec" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-chains", "alloy-consensus", @@ -7456,8 +7467,8 @@ dependencies = [ [[package]] name = "reth-cli" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-genesis", "clap", @@ -7470,8 +7481,8 @@ dependencies = [ [[package]] name = "reth-cli-commands" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "ahash", "alloy-chains", @@ -7541,8 +7552,8 @@ dependencies = [ [[package]] name = "reth-cli-runner" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "reth-tasks", "tokio", @@ -7551,8 +7562,8 @@ dependencies = [ [[package]] name = "reth-cli-util" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7569,8 +7580,8 @@ dependencies = [ [[package]] name = "reth-codecs" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7589,8 +7600,8 @@ dependencies = [ [[package]] name = "reth-codecs-derive" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "convert_case", "proc-macro2", @@ -7600,8 +7611,8 @@ dependencies = [ [[package]] name = "reth-config" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "eyre", "humantime-serde", @@ -7615,8 +7626,8 @@ dependencies = [ [[package]] name = "reth-consensus" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -7628,8 +7639,8 @@ dependencies = [ [[package]] name = "reth-consensus-common" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7640,8 +7651,8 @@ dependencies = [ [[package]] name = "reth-consensus-debug-client" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7659,13 +7670,14 @@ dependencies = [ "reth-tracing", "ringbuffer", "serde", + "serde_json", "tokio", ] [[package]] name = "reth-db" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "derive_more", @@ -7690,8 +7702,8 @@ dependencies = [ [[package]] name = "reth-db-api" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-genesis", @@ -7718,8 +7730,8 @@ dependencies = [ [[package]] name = "reth-db-common" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-genesis", @@ -7747,8 +7759,8 @@ dependencies = [ [[package]] name = "reth-db-models" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-primitives", @@ -7762,8 +7774,8 @@ dependencies = [ [[package]] name = "reth-discv4" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -7788,8 +7800,8 @@ dependencies = [ [[package]] name = "reth-discv5" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -7812,8 +7824,8 @@ dependencies = [ [[package]] name = "reth-dns-discovery" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "data-encoding", @@ -7836,8 +7848,8 @@ dependencies = [ [[package]] name = "reth-downloaders" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7871,13 +7883,15 @@ dependencies = [ [[package]] name = "reth-e2e-test-utils" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", "alloy-network", "alloy-primitives", + "alloy-provider", + "alloy-rlp", "alloy-rpc-types-engine", "alloy-rpc-types-eth", "alloy-signer", @@ -7887,9 +7901,15 @@ dependencies = [ "futures-util", "jsonrpsee 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)", "reth-chainspec", + "reth-cli-commands", + "reth-config", + "reth-consensus", "reth-db", + "reth-db-common", "reth-engine-local", + "reth-ethereum-consensus", "reth-ethereum-primitives", + "reth-evm", "reth-network-api", "reth-network-peers", "reth-node-api", @@ -7899,18 +7919,23 @@ dependencies = [ "reth-payload-builder", "reth-payload-builder-primitives", "reth-payload-primitives", + "reth-primitives", + "reth-primitives-traits", "reth-provider", + "reth-prune-types", "reth-rpc-api", "reth-rpc-builder", "reth-rpc-eth-api", - "reth-rpc-layer 1.5.0", + "reth-rpc-layer 1.5.1", "reth-rpc-server-types", "reth-stages-types", + "reth-static-file", "reth-tasks", "reth-tokio-util", "reth-tracing", "revm", "serde_json", + "tempfile", "tokio", "tokio-stream", "tracing", @@ -7919,8 +7944,8 @@ dependencies = [ [[package]] name = "reth-ecies" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "aes", "alloy-primitives", @@ -7950,8 +7975,8 @@ dependencies = [ [[package]] name = "reth-engine-local" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -7974,8 +7999,8 @@ dependencies = [ [[package]] name = "reth-engine-primitives" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -7999,8 +8024,8 @@ dependencies = [ [[package]] name = "reth-engine-service" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "futures", "pin-project", @@ -8022,8 +8047,8 @@ dependencies = [ [[package]] name = "reth-engine-tree" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8074,8 +8099,8 @@ dependencies = [ [[package]] name = "reth-engine-util" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-rpc-types-engine", @@ -8101,8 +8126,8 @@ dependencies = [ [[package]] name = "reth-era" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8117,8 +8142,8 @@ dependencies = [ [[package]] name = "reth-era-downloader" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "bytes", @@ -8132,8 +8157,8 @@ dependencies = [ [[package]] name = "reth-era-utils" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -8156,8 +8181,8 @@ dependencies = [ [[package]] name = "reth-errors" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "reth-consensus", "reth-execution-errors", @@ -8167,8 +8192,8 @@ dependencies = [ [[package]] name = "reth-eth-wire" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-chains", "alloy-primitives", @@ -8195,8 +8220,8 @@ dependencies = [ [[package]] name = "reth-eth-wire-types" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-chains", "alloy-consensus", @@ -8216,8 +8241,8 @@ dependencies = [ [[package]] name = "reth-ethereum" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", @@ -8254,8 +8279,8 @@ dependencies = [ [[package]] name = "reth-ethereum-cli" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8313,8 +8338,8 @@ dependencies = [ [[package]] name = "reth-ethereum-consensus" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8329,8 +8354,8 @@ dependencies = [ [[package]] name = "reth-ethereum-engine-primitives" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8347,8 +8372,8 @@ dependencies = [ [[package]] name = "reth-ethereum-forks" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eip2124", "alloy-hardforks", @@ -8360,8 +8385,8 @@ dependencies = [ [[package]] name = "reth-ethereum-payload-builder" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8387,8 +8412,8 @@ dependencies = [ [[package]] name = "reth-ethereum-primitives" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8405,8 +8430,8 @@ dependencies = [ [[package]] name = "reth-etl" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "rayon", "reth-db-api", @@ -8415,8 +8440,8 @@ dependencies = [ [[package]] name = "reth-evm" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8438,8 +8463,8 @@ dependencies = [ [[package]] name = "reth-evm-ethereum" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8457,8 +8482,8 @@ dependencies = [ [[package]] name = "reth-execution-errors" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-evm", "alloy-primitives", @@ -8470,8 +8495,8 @@ dependencies = [ [[package]] name = "reth-execution-types" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8488,8 +8513,8 @@ dependencies = [ [[package]] name = "reth-exex" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8526,8 +8551,8 @@ dependencies = [ [[package]] name = "reth-exex-types" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-primitives", @@ -8540,8 +8565,8 @@ dependencies = [ [[package]] name = "reth-fs-util" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "serde", "serde_json", @@ -8550,8 +8575,8 @@ dependencies = [ [[package]] name = "reth-invalid-block-hooks" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -8578,8 +8603,8 @@ dependencies = [ [[package]] name = "reth-ipc" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "bytes", "futures", @@ -8598,8 +8623,8 @@ dependencies = [ [[package]] name = "reth-libmdbx" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "bitflags 2.9.1", "byteorder", @@ -8615,8 +8640,8 @@ dependencies = [ [[package]] name = "reth-mdbx-sys" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "bindgen 0.70.1", "cc", @@ -8624,8 +8649,8 @@ dependencies = [ [[package]] name = "reth-metrics" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "futures", "metrics", @@ -8636,16 +8661,16 @@ dependencies = [ [[package]] name = "reth-net-banlist" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", ] [[package]] name = "reth-net-nat" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "futures-util", "if-addrs", @@ -8658,8 +8683,8 @@ dependencies = [ [[package]] name = "reth-network" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8713,8 +8738,8 @@ dependencies = [ [[package]] name = "reth-network-api" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "alloy-rpc-types-admin", @@ -8736,8 +8761,8 @@ dependencies = [ [[package]] name = "reth-network-p2p" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8759,8 +8784,8 @@ dependencies = [ [[package]] name = "reth-network-peers" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -8774,8 +8799,8 @@ dependencies = [ [[package]] name = "reth-network-types" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eip2124", "humantime-serde", @@ -8788,8 +8813,8 @@ dependencies = [ [[package]] name = "reth-nippy-jar" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "anyhow", "bincode 1.3.3", @@ -8805,8 +8830,8 @@ dependencies = [ [[package]] name = "reth-node-api" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-rpc-types-engine", "eyre", @@ -8829,8 +8854,8 @@ dependencies = [ [[package]] name = "reth-node-builder" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8878,7 +8903,7 @@ dependencies = [ "reth-rpc-builder", "reth-rpc-engine-api", "reth-rpc-eth-types", - "reth-rpc-layer 1.5.0", + "reth-rpc-layer 1.5.1", "reth-stages", "reth-static-file", "reth-tasks", @@ -8894,8 +8919,8 @@ dependencies = [ [[package]] name = "reth-node-core" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -8945,8 +8970,8 @@ dependencies = [ [[package]] name = "reth-node-ethereum" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-rpc-types-engine", @@ -8981,8 +9006,8 @@ dependencies = [ [[package]] name = "reth-node-events" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9005,8 +9030,8 @@ dependencies = [ [[package]] name = "reth-node-metrics" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "eyre", "http", @@ -9026,8 +9051,8 @@ dependencies = [ [[package]] name = "reth-node-types" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "reth-chainspec", "reth-db-api", @@ -9039,8 +9064,8 @@ dependencies = [ [[package]] name = "reth-optimism-chainspec" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-chains", "alloy-consensus", @@ -9066,8 +9091,8 @@ dependencies = [ [[package]] name = "reth-optimism-cli" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9113,8 +9138,8 @@ dependencies = [ [[package]] name = "reth-optimism-consensus" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9138,8 +9163,8 @@ dependencies = [ [[package]] name = "reth-optimism-evm" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9163,8 +9188,8 @@ dependencies = [ [[package]] name = "reth-optimism-forks" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-op-hardforks", "alloy-primitives", @@ -9174,8 +9199,8 @@ dependencies = [ [[package]] name = "reth-optimism-node" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9221,8 +9246,8 @@ dependencies = [ [[package]] name = "reth-optimism-payload-builder" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9260,8 +9285,8 @@ dependencies = [ [[package]] name = "reth-optimism-primitives" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9280,8 +9305,8 @@ dependencies = [ [[package]] name = "reth-optimism-rpc" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9340,8 +9365,8 @@ dependencies = [ [[package]] name = "reth-optimism-storage" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9356,8 +9381,8 @@ dependencies = [ [[package]] name = "reth-optimism-txpool" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9392,8 +9417,8 @@ dependencies = [ [[package]] name = "reth-payload-builder" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9413,8 +9438,8 @@ dependencies = [ [[package]] name = "reth-payload-builder-primitives" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "pin-project", "reth-payload-primitives", @@ -9425,8 +9450,8 @@ dependencies = [ [[package]] name = "reth-payload-primitives" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-primitives", @@ -9444,8 +9469,8 @@ dependencies = [ [[package]] name = "reth-payload-util" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9454,8 +9479,8 @@ dependencies = [ [[package]] name = "reth-payload-validator" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-rpc-types-engine", @@ -9464,8 +9489,8 @@ dependencies = [ [[package]] name = "reth-primitives" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "c-kzg", @@ -9478,8 +9503,8 @@ dependencies = [ [[package]] name = "reth-primitives-traits" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9511,8 +9536,8 @@ dependencies = [ [[package]] name = "reth-provider" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9556,8 +9581,8 @@ dependencies = [ [[package]] name = "reth-prune" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -9584,8 +9609,8 @@ dependencies = [ [[package]] name = "reth-prune-types" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "arbitrary", @@ -9598,8 +9623,8 @@ dependencies = [ [[package]] name = "reth-ress-protocol" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9617,8 +9642,8 @@ dependencies = [ [[package]] name = "reth-ress-provider" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -9644,8 +9669,8 @@ dependencies = [ [[package]] name = "reth-revm" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "reth-primitives-traits", @@ -9657,8 +9682,8 @@ dependencies = [ [[package]] name = "reth-rpc" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -9733,8 +9758,8 @@ dependencies = [ [[package]] name = "reth-rpc-api" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-genesis", @@ -9761,8 +9786,8 @@ dependencies = [ [[package]] name = "reth-rpc-builder" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-network", "alloy-provider", @@ -9783,7 +9808,7 @@ dependencies = [ "reth-rpc-api", "reth-rpc-eth-api", "reth-rpc-eth-types", - "reth-rpc-layer 1.5.0", + "reth-rpc-layer 1.5.1", "reth-rpc-server-types", "reth-storage-api", "reth-tasks", @@ -9799,8 +9824,8 @@ dependencies = [ [[package]] name = "reth-rpc-convert" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-json-rpc", @@ -9821,8 +9846,8 @@ dependencies = [ [[package]] name = "reth-rpc-engine-api" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-primitives", @@ -9851,12 +9876,13 @@ dependencies = [ [[package]] name = "reth-rpc-eth-api" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-dyn-abi", "alloy-eips", + "alloy-evm", "alloy-json-rpc", "alloy-network", "alloy-primitives", @@ -9895,11 +9921,12 @@ dependencies = [ [[package]] name = "reth-rpc-eth-types" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", + "alloy-evm", "alloy-primitives", "alloy-rpc-types-eth", "alloy-sol-types", @@ -9951,8 +9978,8 @@ dependencies = [ [[package]] name = "reth-rpc-layer" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-rpc-types-engine", "http", @@ -9965,8 +9992,8 @@ dependencies = [ [[package]] name = "reth-rpc-server-types" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-primitives", @@ -9981,8 +10008,8 @@ dependencies = [ [[package]] name = "reth-stages" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10031,8 +10058,8 @@ dependencies = [ [[package]] name = "reth-stages-api" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-primitives", @@ -10058,8 +10085,8 @@ dependencies = [ [[package]] name = "reth-stages-types" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "arbitrary", @@ -10072,8 +10099,8 @@ dependencies = [ [[package]] name = "reth-static-file" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "parking_lot", @@ -10092,8 +10119,8 @@ dependencies = [ [[package]] name = "reth-static-file-types" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "clap", @@ -10104,8 +10131,8 @@ dependencies = [ [[package]] name = "reth-storage-api" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10128,8 +10155,8 @@ dependencies = [ [[package]] name = "reth-storage-errors" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-eips", "alloy-primitives", @@ -10144,8 +10171,8 @@ dependencies = [ [[package]] name = "reth-tasks" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "auto_impl", "dyn-clone", @@ -10162,8 +10189,8 @@ dependencies = [ [[package]] name = "reth-testing-utils" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10178,8 +10205,8 @@ dependencies = [ [[package]] name = "reth-tokio-util" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "tokio", "tokio-stream", @@ -10188,8 +10215,8 @@ dependencies = [ [[package]] name = "reth-tracing" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "clap", "eyre", @@ -10203,8 +10230,8 @@ dependencies = [ [[package]] name = "reth-transaction-pool" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10242,8 +10269,8 @@ dependencies = [ [[package]] name = "reth-trie" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-eips", @@ -10267,8 +10294,8 @@ dependencies = [ [[package]] name = "reth-trie-common" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-consensus", "alloy-primitives", @@ -10293,8 +10320,8 @@ dependencies = [ [[package]] name = "reth-trie-db" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "reth-db-api", @@ -10306,8 +10333,8 @@ dependencies = [ [[package]] name = "reth-trie-parallel" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -10331,8 +10358,8 @@ dependencies = [ [[package]] name = "reth-trie-sparse" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -10349,17 +10376,17 @@ dependencies = [ [[package]] name = "reth-zstd-compressors" -version = "1.5.0" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.0#61e38f9af154fe91e776d8f5e449d20a1571e8cf" +version = "1.5.1" +source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "zstd", ] [[package]] name = "revm" -version = "26.0.1" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2a493c73054a0f6635bad6e840cdbef34838e6e6186974833c901dff7dd709" +checksum = "70a84455f03d3480d4ed2e7271c15f2ec95b758e86d57cb8d258a8ff1c22e9a4" dependencies = [ "revm-bytecode", "revm-context", @@ -10376,9 +10403,9 @@ dependencies = [ [[package]] name = "revm-bytecode" -version = "5.0.0" +version = "6.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b395ee2212d44fcde20e9425916fee685b5440c3f8e01fabae8b0f07a2fd7f08" +checksum = "7a685758a4f375ae9392b571014b9779cfa63f0d8eb91afb4626ddd958b23615" dependencies = [ "bitvec", "once_cell", @@ -10389,9 +10416,9 @@ dependencies = [ [[package]] name = "revm-context" -version = "7.0.1" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b97b69d05651509b809eb7215a6563dc64be76a941666c40aabe597ab544d38" +checksum = "a990abf66b47895ca3e915d5f3652bb7c6a4cff6e5351fdf0fc2795171fd411c" dependencies = [ "cfg-if", "derive-where", @@ -10405,9 +10432,9 @@ dependencies = [ [[package]] name = "revm-context-interface" -version = "7.0.1" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f8f4f06a1c43bf8e6148509aa06a6c4d28421541944842b9b11ea1a6e53468f" +checksum = "a303a93102fceccec628265efd550ce49f2817b38ac3a492c53f7d524f18a1ca" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -10421,9 +10448,9 @@ dependencies = [ [[package]] name = "revm-database" -version = "6.0.0" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763eb5867a109a85f8e47f548b9d88c9143c0e443ec056742052f059fa32f4f1" +checksum = "7db360729b61cc347f9c2f12adb9b5e14413aea58778cf9a3b7676c6a4afa115" dependencies = [ "alloy-eips", "revm-bytecode", @@ -10435,11 +10462,12 @@ dependencies = [ [[package]] name = "revm-database-interface" -version = "6.0.0" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5ecd19a5b75b862841113b9abdd864ad4b22e633810e11e6d620e8207e361d" +checksum = "b8500194cad0b9b1f0567d72370795fd1a5e0de9ec719b1607fa1566a23f039a" dependencies = [ "auto_impl", + "either", "revm-primitives", "revm-state", "serde", @@ -10447,9 +10475,9 @@ dependencies = [ [[package]] name = "revm-handler" -version = "7.0.1" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b61f992beaa7a5fc3f5fcf79f1093624fa1557dc42d36baa42114c2d836b59" +checksum = "03c35a17a38203976f97109e20eccf6732447ce6c9c42973bae42732b2e957ff" dependencies = [ "auto_impl", "derive-where", @@ -10466,9 +10494,9 @@ dependencies = [ [[package]] name = "revm-inspector" -version = "7.0.1" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7e4400a109a2264f4bf290888ac6d02432b6d5d070492b9dcf134b0c7d51354" +checksum = "e69abf6a076741bd5cd87b7d6c1b48be2821acc58932f284572323e81a8d4179" dependencies = [ "auto_impl", "either", @@ -10484,9 +10512,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.25.0" +version = "0.26.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aabdffc06bdb434d9163e2d63b6fae843559afd300ea3fbeb113b8a0d8ec728" +checksum = "c7b99a2332cf8eed9e9a22fffbf76dfadc99d2c45de6ae6431a1eb9f657dd97a" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -10504,9 +10532,9 @@ dependencies = [ [[package]] name = "revm-interpreter" -version = "22.0.1" +version = "23.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2481ef059708772cec0ce6bc4c84b796a40111612efb73b01adf1caed7ff9ac" +checksum = "d95c4a9a1662d10b689b66b536ddc2eb1e89f5debfcabc1a2d7b8417a2fa47cd" dependencies = [ "revm-bytecode", "revm-context-interface", @@ -10516,15 +10544,16 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "23.0.0" +version = "24.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d581e78c8f132832bd00854fb5bf37efd95a52582003da35c25cd2cbfc63849" +checksum = "b68d54a4733ac36bd29ee645c3c2e5e782fb63f199088d49e2c48c64a9fedc15" dependencies = [ "ark-bls12-381", "ark-bn254", "ark-ec", "ark-ff 0.5.0", "ark-serialize 0.5.0", + "arrayref", "aurora-engine-modexp", "blst", "c-kzg", @@ -10553,9 +10582,9 @@ dependencies = [ [[package]] name = "revm-state" -version = "6.0.0" +version = "7.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d6274928dd78f907103740b10800d3c0db6caeca391e75a159c168a1e5c78f8" +checksum = "106fec5c634420118c7d07a6c37110186ae7f23025ceac3a5dbe182eea548363" dependencies = [ "bitflags 2.9.1", "revm-bytecode", @@ -10839,22 +10868,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags 2.9.1", "errno", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustls" -version = "0.23.28" +version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" +checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" dependencies = [ "aws-lc-rs", "log", @@ -10926,9 +10955,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "aws-lc-rs", "ring", @@ -10998,9 +11027,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1375ba8ef45a6f15d83fa8748f1079428295d403d6ea991d09ab100155fbc06d" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" dependencies = [ "dyn-clone", "ref-cast", @@ -11249,7 +11278,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.10.0", "schemars 0.9.0", - "schemars 1.0.3", + "schemars 1.0.4", "serde", "serde_derive", "serde_json", @@ -11751,7 +11780,7 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", + "rustix 1.0.8", "windows-sys 0.59.0", ] @@ -11974,17 +12003,19 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.1" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -12286,8 +12317,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" dependencies = [ - "futures", - "futures-task", "pin-project", "tracing", ] @@ -13465,9 +13494,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -13544,7 +13573,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af3a19837351dc82ba89f8a125e22a3c475f05aba604acc023d62b2739ae2909" dependencies = [ "libc", - "rustix 1.0.7", + "rustix 1.0.8", ] [[package]] From 7e3632482196e04d0a6095f10e1540010713052d Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Wed, 16 Jul 2025 19:41:47 -0700 Subject: [PATCH 36/45] chore: udeps --- Cargo.lock | 202 +---------------------------- Cargo.toml | 3 - crates/flashblocks-node/Cargo.toml | 1 - crates/flashblocks-p2p/Cargo.toml | 1 - crates/flashblocks-rpc/Cargo.toml | 5 - crates/websocket-proxy/Cargo.toml | 2 +- 6 files changed, 3 insertions(+), 211 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 91beb4de..aebb756d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -715,8 +715,6 @@ dependencies = [ "alloy-primitives", "alloy-signer", "async-trait", - "coins-bip32", - "coins-bip39", "k256", "rand 0.8.5", "thiserror 2.0.12", @@ -1631,12 +1629,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - [[package]] name = "bimap" version = "0.6.3" @@ -1652,26 +1644,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bincode" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "bincode_derive", - "serde", - "unty", -] - -[[package]] -name = "bincode_derive" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" -dependencies = [ - "virtue", -] - [[package]] name = "bindgen" version = "0.69.5" @@ -2049,7 +2021,6 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2 0.10.9", "tinyvec", ] @@ -2316,57 +2287,6 @@ dependencies = [ "cc", ] -[[package]] -name = "coins-bip32" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2073678591747aed4000dd468b97b14d7007f7936851d3f2f01846899f5ebf08" -dependencies = [ - "bs58", - "coins-core", - "digest 0.10.7", - "hmac", - "k256", - "serde", - "sha2 0.10.9", - "thiserror 1.0.69", -] - -[[package]] -name = "coins-bip39" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b169b26623ff17e9db37a539fe4f15342080df39f129ef7631df7683d6d9d4" -dependencies = [ - "bitvec", - "coins-bip32", - "hmac", - "once_cell", - "pbkdf2", - "rand 0.8.5", - "sha2 0.10.9", - "thiserror 1.0.69", -] - -[[package]] -name = "coins-core" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b962ad8545e43a28e14e87377812ba9ae748dd4fd963f4c10e9fcc6d13475b" -dependencies = [ - "base64 0.21.7", - "bech32", - "bs58", - "const-hex", - "digest 0.10.7", - "generic-array", - "ripemd", - "serde", - "sha2 0.10.9", - "sha3", - "thiserror 1.0.69", -] - [[package]] name = "colorchoice" version = "1.0.4" @@ -3449,34 +3369,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "flashblocks-api" -version = "0.1.0" -dependencies = [ - "alloy-primitives", - "bincode 2.0.1", - "blake3", - "clap", - "ed25519-dalek", - "eyre", - "futures", - "reth", - "reth-eth-wire", - "reth-ethereum", - "reth-network", - "reth-node-api", - "reth-node-builder", - "reth-provider", - "reth-transaction-pool", - "rollup-boost", - "serde", - "serde_json", - "thiserror 2.0.12", - "tokio", - "tokio-stream", - "tracing", -] - [[package]] name = "flashblocks-node" version = "0.1.0" @@ -3505,7 +3397,6 @@ dependencies = [ "op-alloy-network", "op-alloy-rpc-types", "reth-db", - "reth-e2e-test-utils", "reth-eth-wire", "reth-ethereum", "reth-network", @@ -3542,7 +3433,6 @@ version = "0.1.0" dependencies = [ "alloy-primitives", "alloy-rlp", - "bincode 2.0.1", "blake3", "clap", "ed25519-dalek", @@ -3584,7 +3474,6 @@ dependencies = [ "clap", "ed25519-dalek", "eyre", - "flashblocks-p2p", "futures-util", "jsonrpsee 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)", "metrics", @@ -3593,18 +3482,14 @@ dependencies = [ "op-alloy-network", "op-alloy-rpc-types", "reth-db", - "reth-e2e-test-utils", "reth-eth-wire", - "reth-ethereum", "reth-network", "reth-node-api", "reth-node-builder", "reth-node-core", "reth-optimism-chainspec", - "reth-optimism-cli", "reth-optimism-evm", "reth-optimism-forks", - "reth-optimism-node", "reth-optimism-primitives", "reth-optimism-rpc", "reth-primitives", @@ -6501,16 +6386,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pbkdf2" -version = "0.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" -dependencies = [ - "digest 0.10.7", - "hmac", -] - [[package]] name = "pem" version = "3.0.5" @@ -7881,67 +7756,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "reth-e2e-test-utils" -version = "1.5.1" -source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-network", - "alloy-primitives", - "alloy-provider", - "alloy-rlp", - "alloy-rpc-types-engine", - "alloy-rpc-types-eth", - "alloy-signer", - "alloy-signer-local", - "derive_more", - "eyre", - "futures-util", - "jsonrpsee 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)", - "reth-chainspec", - "reth-cli-commands", - "reth-config", - "reth-consensus", - "reth-db", - "reth-db-common", - "reth-engine-local", - "reth-ethereum-consensus", - "reth-ethereum-primitives", - "reth-evm", - "reth-network-api", - "reth-network-peers", - "reth-node-api", - "reth-node-builder", - "reth-node-core", - "reth-node-ethereum", - "reth-payload-builder", - "reth-payload-builder-primitives", - "reth-payload-primitives", - "reth-primitives", - "reth-primitives-traits", - "reth-provider", - "reth-prune-types", - "reth-rpc-api", - "reth-rpc-builder", - "reth-rpc-eth-api", - "reth-rpc-layer 1.5.1", - "reth-rpc-server-types", - "reth-stages-types", - "reth-static-file", - "reth-tasks", - "reth-tokio-util", - "reth-tracing", - "revm", - "serde_json", - "tempfile", - "tokio", - "tokio-stream", - "tracing", - "url", -] - [[package]] name = "reth-ecies" version = "1.5.1" @@ -8817,7 +8631,7 @@ version = "1.5.1" source = "git+https://github.com/paradigmxyz/reth?tag=v1.5.1#dbe7ee9c21392f360ff01f6307480f5d7dd73a3a" dependencies = [ "anyhow", - "bincode 1.3.3", + "bincode", "derive_more", "lz4_flex", "memmap2", @@ -10014,7 +9828,7 @@ dependencies = [ "alloy-consensus", "alloy-eips", "alloy-primitives", - "bincode 1.3.3", + "bincode", "blake3", "eyre", "futures-util", @@ -12591,12 +12405,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "url" version = "2.5.4" @@ -12703,12 +12511,6 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" -[[package]] -name = "virtue" -version = "0.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" - [[package]] name = "visibility" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index b1006071..6ad2c140 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,11 @@ [workspace] resolver = "3" -edition = "2024" -license = "MIT" members = [ "crates/rollup-boost", "crates/websocket-proxy", "crates/flashblocks-rpc", "crates/flashblocks-p2p", - "crates/flashblocks-api", "crates/flashblocks-node", ] diff --git a/crates/flashblocks-node/Cargo.toml b/crates/flashblocks-node/Cargo.toml index 2a561176..9e14846f 100644 --- a/crates/flashblocks-node/Cargo.toml +++ b/crates/flashblocks-node/Cargo.toml @@ -23,7 +23,6 @@ reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1", features = [ "test-utils", ] } -reth-e2e-test-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-tasks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } diff --git a/crates/flashblocks-p2p/Cargo.toml b/crates/flashblocks-p2p/Cargo.toml index a5055a44..75167162 100644 --- a/crates/flashblocks-p2p/Cargo.toml +++ b/crates/flashblocks-p2p/Cargo.toml @@ -17,7 +17,6 @@ reth-transaction-pool = { workspace = true } ed25519-dalek = { version = "2", features = ["serde"] } blake3 = "1" # fast hashing for payload IDs serde = { version = "1", features = ["derive"] } -bincode = "2" # stable, deterministic encoding tokio = { workspace = true } tokio-stream = { workspace = true } diff --git a/crates/flashblocks-rpc/Cargo.toml b/crates/flashblocks-rpc/Cargo.toml index 8561ed80..51bde4ec 100644 --- a/crates/flashblocks-rpc/Cargo.toml +++ b/crates/flashblocks-rpc/Cargo.toml @@ -6,10 +6,7 @@ license = "MIT" [dependencies] rollup-boost.workspace = true -flashblocks-p2p.workspace = true -reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } -reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } @@ -22,7 +19,6 @@ reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1", features = [ "test-utils", ] } -reth-e2e-test-utils = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-tasks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-node-core = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } @@ -30,7 +26,6 @@ reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-tracing = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } reth-rpc-server-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.5.1" } -reth-ethereum = { workspace = true, features = ["node", "network", "cli"] } reth-eth-wire = { workspace = true } reth-network = { workspace = true } ed25519-dalek = { version = "2", features = ["serde"] } diff --git a/crates/websocket-proxy/Cargo.toml b/crates/websocket-proxy/Cargo.toml index 14785f79..8c4c9e13 100644 --- a/crates/websocket-proxy/Cargo.toml +++ b/crates/websocket-proxy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "flashblocks-websocket-proxy" version = "0.1.0" -edition = "2021" +edition = "2024" rust-version = "1.85" license = "MIT" From ad658fe8c2da8310829e27352c5142cc07565956 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 17 Jul 2025 15:10:23 -0700 Subject: [PATCH 37/45] feat: flashblock size metric --- Cargo.lock | 1 + crates/flashblocks-p2p/Cargo.toml | 1 + crates/flashblocks-p2p/src/protocol/handler.rs | 7 +++++-- crates/rollup-boost/src/flashblocks/p2p.rs | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aebb756d..27bc4b4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3438,6 +3438,7 @@ dependencies = [ "ed25519-dalek", "eyre", "futures", + "metrics", "parking_lot", "reth", "reth-eth-wire", diff --git a/crates/flashblocks-p2p/Cargo.toml b/crates/flashblocks-p2p/Cargo.toml index 75167162..f9b2b9fa 100644 --- a/crates/flashblocks-p2p/Cargo.toml +++ b/crates/flashblocks-p2p/Cargo.toml @@ -18,6 +18,7 @@ ed25519-dalek = { version = "2", features = ["serde"] } blake3 = "1" # fast hashing for payload IDs serde = { version = "1", features = ["derive"] } +metrics.workspace = true tokio = { workspace = true } tokio-stream = { workspace = true } eyre.workspace = true diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 60e2048d..8163a16e 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -140,7 +140,10 @@ impl FlashblocksP2PCtx { "queueing flashblock", ); let bytes = msg.encode(); - if bytes.len() > MAX_FRAME { + let len = bytes.len(); + metrics::histogram!("flashblock_size").record(len as f64); + + if len > MAX_FRAME { tracing::error!( target: "flashblocks", size = bytes.len(), @@ -149,7 +152,7 @@ impl FlashblocksP2PCtx { ); return; } - if bytes.len() > MAX_FRAME / 2 { + if len > MAX_FRAME / 2 { tracing::warn!( target = "flashblocks", size = bytes.len(), diff --git a/crates/rollup-boost/src/flashblocks/p2p.rs b/crates/rollup-boost/src/flashblocks/p2p.rs index 1b660c1a..3f260f67 100644 --- a/crates/rollup-boost/src/flashblocks/p2p.rs +++ b/crates/rollup-boost/src/flashblocks/p2p.rs @@ -22,8 +22,8 @@ pub struct Authorized { pub builder_sig: Signature, } -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Eq)] #[repr(u8)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Eq)] pub enum FlashblocksP2PMsg { FlashblocksPayloadV1(Authorized) = 0x00, } From 69f50d3564aaac3e7381abb235af9d380ea4ba89 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 17 Jul 2025 15:14:39 -0700 Subject: [PATCH 38/45] fix: target in events --- crates/flashblocks-p2p/src/protocol/connection.rs | 12 ++++++------ crates/flashblocks-p2p/src/protocol/handler.rs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/flashblocks-p2p/src/protocol/connection.rs b/crates/flashblocks-p2p/src/protocol/connection.rs index 8668120f..81611aef 100644 --- a/crates/flashblocks-p2p/src/protocol/connection.rs +++ b/crates/flashblocks-p2p/src/protocol/connection.rs @@ -43,7 +43,7 @@ impl Stream for FlashblocksConnection { || this.received.get(flashblock_index) != Some(&true) { trace!( - target = "flashblocks", + target: "flashblocks", peer_id = %this.peer_id, %payload_id, %flashblock_index, @@ -54,7 +54,7 @@ impl Stream for FlashblocksConnection { } Err(error) => { tracing::error!( - target = "flashblocks", + target: "flashblocks", %error, "Failed to receive flashblocks message from peer_rx" ); @@ -71,7 +71,7 @@ impl Stream for FlashblocksConnection { Ok(msg) => msg, Err(error) => { tracing::warn!( - target = "flashblocks", + target: "flashblocks", peer_id = %this.peer_id, %error, "Failed to decode flashblocks message from peer", @@ -99,7 +99,7 @@ impl FlashblocksConnection { if let Err(error) = message.verify(self.handler.ctx.authorizer_vk) { tracing::warn!( - target = "flashblocks", + target: "flashblocks", peer_id = %self.peer_id, %error, "Failed to verify flashblock", @@ -113,7 +113,7 @@ impl FlashblocksConnection { if message.authorization.timestamp < state.payload_timestamp { tracing::warn!( - target = "flashblocks", + target: "flashblocks", peer_id = %self.peer_id, timestamp = message.authorization.timestamp, "Received flashblock with outdated timestamp", @@ -147,7 +147,7 @@ impl FlashblocksConnection { // We've already seen this index from this peer. // They could be trying to DOS us. tracing::warn!( - target = "flashblocks", + target: "flashblocks", peer_id = %self.peer_id, payload_id = %message.payload.payload_id, index = message.payload.index, diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 8163a16e..a0cbe605 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -134,7 +134,7 @@ impl FlashblocksP2PCtx { // Add the flashblock to our cache *flashblock = Some(message.clone().payload); tracing::trace!( - target = "flashblocks", + target: "flashblocks", payload_id = %message.payload.payload_id, flashblock_index = message.payload.index, "queueing flashblock", @@ -154,7 +154,7 @@ impl FlashblocksP2PCtx { } if len > MAX_FRAME / 2 { tracing::warn!( - target = "flashblocks", + target: "flashblocks", size = bytes.len(), max_size = MAX_FRAME, "FlashblocksP2PMsg almost too large", @@ -171,7 +171,7 @@ impl FlashblocksP2PCtx { while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { // Publish the flashblock debug!( - target = "flashblocks", + target: "flashblocks", payload_id = %flashblock_event.payload_id, flashblock_index = %state.flashblock_index, "publishing flashblock" From b40a313ab8ade4503a94a2d5d738caeb2c9d4247 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Thu, 17 Jul 2025 22:37:38 -0700 Subject: [PATCH 39/45] feat: flashblocks node args --- crates/flashblocks-node/src/{ => bin}/main.rs | 33 +++++----------- crates/flashblocks-node/src/lib.rs | 19 ++++++++++ crates/flashblocks-node/tests/p2p.rs | 6 +-- crates/flashblocks-p2p/src/net/mod.rs | 38 ++++++++++++------- 4 files changed, 57 insertions(+), 39 deletions(-) rename crates/flashblocks-node/src/{ => bin}/main.rs (74%) create mode 100644 crates/flashblocks-node/src/lib.rs diff --git a/crates/flashblocks-node/src/main.rs b/crates/flashblocks-node/src/bin/main.rs similarity index 74% rename from crates/flashblocks-node/src/main.rs rename to crates/flashblocks-node/src/bin/main.rs index 4c270a72..6f372c7d 100644 --- a/crates/flashblocks-node/src/main.rs +++ b/crates/flashblocks-node/src/bin/main.rs @@ -1,33 +1,23 @@ #![allow(missing_docs, rustdoc::missing_crate_level_docs)] - use clap::Parser; use ed25519_dalek::VerifyingKey; +use flashblocks_node::FlashblocksNodeArgs; use flashblocks_p2p::protocol::handler::FlashblocksHandler; -use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay }; +use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay}; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; use reth_optimism_node::{OpNode, args::RollupArgs}; -use rollup_boost::parse_vk; -use tokio::sync::{broadcast, mpsc }; +use tokio::sync::{broadcast, mpsc}; use tracing::info; -#[derive(Debug, Clone, PartialEq, Eq, clap::Args)] +#[derive(Debug, Clone, clap::Args)] #[command(next_help_heading = "Rollup")] struct FlashblocksRollupArgs { #[command(flatten)] - rollup_args: RollupArgs, - - #[arg(long = "flashblocks.enabled", default_value = "false")] - flashblocks_enabled: bool, - - #[arg(long = "flashblocks.websocket-url", value_name = "WEBSOCKET_URL")] - websocket_url: url::Url, + pub rollup_args: RollupArgs, - #[arg(long, - env = "FLASHBLOCKS_BUILDER_VK", value_parser = parse_vk, - required = false, - )] - pub flashblocks_builder_vk: VerifyingKey, + #[command(flatten)] + pub flashblock_args: Option, } pub fn main() { @@ -38,16 +28,14 @@ pub fn main() { let (inbound_tx, inbound_rx) = broadcast::channel(100); let (_publish_tx, publish_rx) = mpsc::unbounded_channel(); - let flashblocks_overlay = - FlashblocksOverlay::new(chain_spec, inbound_rx); + let flashblocks_overlay = FlashblocksOverlay::new(chain_spec, inbound_rx); flashblocks_overlay.clone().start()?; info!(target: "reth::cli", "Launching Flashblocks RPC overlay node"); let handle = builder .node(OpNode::new(rollup_args)) .extend_rpc_modules(move |ctx| { - if args.flashblocks_enabled { - + if args.flashblock_args.is_some() { let eth_api = ctx.registry.eth_api().clone(); let api_ext = FlashblocksApiExt::new(eth_api.clone(), flashblocks_overlay); @@ -58,13 +46,12 @@ pub fn main() { .launch_with_debug_capabilities() .await?; - let custom_rlpx_handler = FlashblocksHandler::new( handle.node.network.clone(), VerifyingKey::default(), inbound_tx, publish_rx, - ); + ); handle .node diff --git a/crates/flashblocks-node/src/lib.rs b/crates/flashblocks-node/src/lib.rs new file mode 100644 index 00000000..bf5e7730 --- /dev/null +++ b/crates/flashblocks-node/src/lib.rs @@ -0,0 +1,19 @@ +use clap::Args; +use ed25519_dalek::VerifyingKey; +use rollup_boost::parse_vk; + +#[derive(Args, Clone, Debug)] +#[group(requires = "flashblocks")] +pub struct FlashblocksNodeArgs { + /// Enable Flashblocks client + #[arg(long, env, required = false)] + pub flashblocks: bool, + + #[arg( + long = "flashblocks.authorizor_vk", + env = "FLASHBLOCKS_AUTHORIZOR_VK", + value_parser = parse_vk, + required = false, + )] + pub authorizor_vk: VerifyingKey, +} diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index 22334361..abbd3e27 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -29,7 +29,7 @@ use tokio::sync::{broadcast, mpsc}; use tracing::info; pub struct NodeContext { - inbound_tx: broadcast::Sender, + flashblocks_tx: broadcast::Sender, publish_tx: mpsc::UnboundedSender, pub local_node_record: NodeRecord, http_api_addr: SocketAddr, @@ -114,7 +114,7 @@ async fn setup_node( if let Some((peer_id, addr)) = trusted_peer { // If a trusted peer is provided, add it to the network - node.network.add_trusted_peer(peer_id, addr); + node.network.add_peer(peer_id, addr); } let http_api_addr = node @@ -126,7 +126,7 @@ async fn setup_node( let local_node_record = network_handle.local_node_record(); Ok(NodeContext { - inbound_tx, + flashblocks_tx: inbound_tx, publish_tx, local_node_record, http_api_addr, diff --git a/crates/flashblocks-p2p/src/net/mod.rs b/crates/flashblocks-p2p/src/net/mod.rs index eec0dff7..0f09a828 100644 --- a/crates/flashblocks-p2p/src/net/mod.rs +++ b/crates/flashblocks-p2p/src/net/mod.rs @@ -16,15 +16,19 @@ use tokio::sync::{broadcast, mpsc}; use crate::protocol::handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}; #[derive(Debug)] -pub struct FlashblocksNetworkBuilder { - inner: T, +struct FlashblocksNetworkBuilderCtx { authorizer_vk: VerifyingKey, flashblocks_receiver_tx: broadcast::Sender, publish_rx: mpsc::UnboundedReceiver, } +#[derive(Debug)] +pub struct FlashblocksNetworkBuilder { + inner: T, + ctx: Option, +} + impl FlashblocksNetworkBuilder { - /// Creates a new `FlashblocksNetworkBuilder` with the given inner builder and events channel. pub fn new( inner: T, authorizer_vk: VerifyingKey, @@ -33,11 +37,17 @@ impl FlashblocksNetworkBuilder { ) -> Self { Self { inner, - authorizer_vk, - flashblocks_receiver_tx, - publish_rx, + ctx: Some(FlashblocksNetworkBuilderCtx { + authorizer_vk, + flashblocks_receiver_tx, + publish_rx, + }), } } + + pub fn disabled(inner: T) -> Self { + Self { inner, ctx: None } + } } impl NetworkBuilder for FlashblocksNetworkBuilder @@ -59,13 +69,15 @@ where pool: Pool, ) -> eyre::Result { let handle = self.inner.build_network(ctx, pool).await?; - let handler = FlashblocksHandler::::new( - handle.clone(), - self.authorizer_vk, - self.flashblocks_receiver_tx, - self.publish_rx, - ); - handle.add_rlpx_sub_protocol(handler.into_rlpx_sub_protocol()); + if let Some(ctx) = self.ctx { + let handler = FlashblocksHandler::::new( + handle.clone(), + ctx.authorizer_vk, + ctx.flashblocks_receiver_tx, + ctx.publish_rx, + ); + handle.add_rlpx_sub_protocol(handler.into_rlpx_sub_protocol()); + } Ok(handle) } From 2f3a2100e077a521df32716d480816e863034f1a Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Fri, 18 Jul 2025 17:26:33 -0700 Subject: [PATCH 40/45] feat: check peer reputation --- crates/flashblocks-node/tests/p2p.rs | 24 +++++++++++++++++++-- crates/rollup-boost/src/cli.rs | 2 +- crates/rollup-boost/src/flashblocks/args.rs | 15 +++++++------ 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index abbd3e27..4ee8a17b 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -7,8 +7,10 @@ use alloy_rpc_types_engine::PayloadId; use ed25519_dalek::SigningKey; use flashblocks_p2p::protocol::handler::FlashblocksHandler; use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverlay, Metadata}; +use op_alloy_consensus::{OpPooledTransaction, OpTxEnvelope}; +use reth_eth_wire::BasicNetworkPrimitives; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; -use reth_network::{Peers, PeersInfo}; +use reth_network::{NetworkHandle, Peers, PeersInfo, events::NetworkPeersEvents}; use reth_network_peers::{NodeRecord, PeerId}; use reth_node_builder::{Node, NodeBuilder, NodeConfig, NodeHandle}; use reth_node_core::{ @@ -17,7 +19,7 @@ use reth_node_core::{ }; use reth_optimism_chainspec::OpChainSpecBuilder; use reth_optimism_node::{OpNode, args::RollupArgs}; -use reth_optimism_primitives::OpReceipt; +use reth_optimism_primitives::{OpPrimitives, OpReceipt}; use reth_provider::providers::BlockchainProvider; use reth_tasks::{TaskExecutor, TaskManager}; use rollup_boost::{ @@ -28,6 +30,14 @@ use std::{any::Any, collections::HashMap, net::SocketAddr, str::FromStr, sync::A use tokio::sync::{broadcast, mpsc}; use tracing::info; +type Network = NetworkHandle< + BasicNetworkPrimitives< + OpPrimitives, + OpPooledTransaction, + reth_network::types::NewBlock>, + >, +>; + pub struct NodeContext { flashblocks_tx: broadcast::Sender, publish_tx: mpsc::UnboundedSender, @@ -35,6 +45,7 @@ pub struct NodeContext { http_api_addr: SocketAddr, _node_exit_future: NodeExitFuture, _node: Box, + network_handle: Network, } impl NodeContext { @@ -132,6 +143,7 @@ async fn setup_node( http_api_addr, _node_exit_future: node_exit_future, _node: Box::new(node), + network_handle, }) } @@ -319,6 +331,11 @@ async fn test_peering() -> eyre::Result<()> { let proto_message = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized); node_1.publish_tx.send(proto_message)?; tokio::time::sleep(tokio::time::Duration::from_millis(10000)).await; + let peers = node_0.network_handle.get_all_peers().await?; + let peer_1 = &peers[1].remote_id; + + let rep_1 = node_0.network_handle.reputation_by_id(*peer_1).await?; + info!(?rep_1, "Peer reputation"); // Query pending block after sending the base payload with an empty delta let pending_block = provider_0 @@ -343,6 +360,9 @@ async fn test_peering() -> eyre::Result<()> { node_1.publish_tx.send(proto_message)?; tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; + let rep_1 = node_0.network_handle.reputation_by_id(*peer_1).await?; + info!(?rep_1, "Peer reputation"); + // Query pending block after sending the second payload with two transactions let block = provider_0 .get_block_by_number(alloy_eips::BlockNumberOrTag::Pending) diff --git a/crates/rollup-boost/src/cli.rs b/crates/rollup-boost/src/cli.rs index 95834ac1..c0be9470 100644 --- a/crates/rollup-boost/src/cli.rs +++ b/crates/rollup-boost/src/cli.rs @@ -138,7 +138,7 @@ impl RollupBoostArgs { PayloadSource::Builder, self.flashblocks .as_ref() - .map(|fb| fb.flashblocks_authorization_sk.clone()), + .map(|fb| fb.flashblocks_authorizer_sk.clone()), self.flashblocks .as_ref() .map(|fb| fb.flashblocks_builder_vk.clone()), diff --git a/crates/rollup-boost/src/flashblocks/args.rs b/crates/rollup-boost/src/flashblocks/args.rs index 913662b8..b6773219 100644 --- a/crates/rollup-boost/src/flashblocks/args.rs +++ b/crates/rollup-boost/src/flashblocks/args.rs @@ -1,6 +1,5 @@ -use clap::{Args, Parser}; +use clap::Args; use ed25519_dalek::{SigningKey, VerifyingKey}; -use eyre::Context; use url::Url; use hex::FromHex; @@ -46,13 +45,15 @@ pub struct FlashblocksArgs { #[arg( long, - env = "FLASHBLOCKS_AUTHORIZATION_SK", value_parser = parse_sk, + env = "FLASHBLOCKS_AUTHORIZER_SK", + value_parser = parse_sk, required = false, )] - pub flashblocks_authorization_sk: SigningKey, + pub flashblocks_authorizer_sk: SigningKey, #[arg(long, - env = "FLASHBLOCKS_BUILDER_VK", value_parser = parse_vk, + env = "FLASHBLOCKS_BUILDER_VK", + value_parser = parse_vk, required = false, )] pub flashblocks_builder_vk: VerifyingKey, @@ -60,11 +61,11 @@ pub struct FlashblocksArgs { pub fn parse_sk(s: &str) -> eyre::Result { let bytes = - <[u8; 32]>::from_hex(s.trim()).context("failed parsing flashblocks_authorization_sk")?; + <[u8; 32]>::from_hex(s.trim())?; Ok(SigningKey::from_bytes(&bytes)) } pub fn parse_vk(s: &str) -> eyre::Result { - let bytes = <[u8; 32]>::from_hex(s.trim()).context("failed parsing flashblocks_builder_vk")?; + let bytes = <[u8; 32]>::from_hex(s.trim())?; Ok(VerifyingKey::from_bytes(&bytes)?) } From 05b6eba006ddfa6b26ddbbd090c4d22b476cfc56 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Mon, 21 Jul 2025 12:57:40 -0700 Subject: [PATCH 41/45] switch to broadcast --- crates/flashblocks-p2p/src/net/mod.rs | 6 +++--- crates/flashblocks-p2p/src/protocol/handler.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/flashblocks-p2p/src/net/mod.rs b/crates/flashblocks-p2p/src/net/mod.rs index 0f09a828..ba2b8fa7 100644 --- a/crates/flashblocks-p2p/src/net/mod.rs +++ b/crates/flashblocks-p2p/src/net/mod.rs @@ -11,7 +11,7 @@ use reth_node_builder::{ }; use reth_transaction_pool::{PoolTransaction, TransactionPool}; use rollup_boost::{FlashblocksP2PMsg, FlashblocksPayloadV1}; -use tokio::sync::{broadcast, mpsc}; +use tokio::sync::broadcast; use crate::protocol::handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}; @@ -19,7 +19,7 @@ use crate::protocol::handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}; struct FlashblocksNetworkBuilderCtx { authorizer_vk: VerifyingKey, flashblocks_receiver_tx: broadcast::Sender, - publish_rx: mpsc::UnboundedReceiver, + publish_rx: broadcast::Receiver, } #[derive(Debug)] @@ -33,7 +33,7 @@ impl FlashblocksNetworkBuilder { inner: T, authorizer_vk: VerifyingKey, flashblocks_receiver_tx: broadcast::Sender, - publish_rx: mpsc::UnboundedReceiver, + publish_rx: broadcast::Receiver, ) -> Self { Self { inner, diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index a0cbe605..50d8de6c 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -9,7 +9,7 @@ use reth_network::Peers; use rollup_boost::{FlashblocksP2PMsg, FlashblocksPayloadV1}; use std::net::SocketAddr; use std::sync::Arc; -use tokio::sync::{broadcast, mpsc}; +use tokio::sync::broadcast; use tracing::debug; use reth_ethereum::network::{ @@ -74,7 +74,7 @@ impl FlashblocksHandler { network_handle: N, authorizer_vk: VerifyingKey, flashblock_tx: broadcast::Sender, - mut publish_rx: mpsc::UnboundedReceiver, + mut publish_rx: broadcast::Receiver, ) -> Self { let peer_tx = broadcast::Sender::new(100); let state = Arc::new(Mutex::new(FlashblocksP2PState::default())); @@ -89,7 +89,7 @@ impl FlashblocksHandler { let ctx_clone = ctx.clone(); tokio::spawn({ async move { - while let Some(msg) = publish_rx.recv().await { + while let Ok(msg) = publish_rx.recv().await { let mut state = state_clone.lock(); ctx_clone.publish(&mut state, msg); } From bf980dd8b46d1a3cf59683aa9fec097a95041265 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Mon, 21 Jul 2025 14:57:00 -0700 Subject: [PATCH 42/45] feat: switch to broadcast for publish chan --- crates/flashblocks-node/src/bin/main.rs | 4 ++-- crates/flashblocks-node/tests/p2p.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/flashblocks-node/src/bin/main.rs b/crates/flashblocks-node/src/bin/main.rs index 6f372c7d..d7278321 100644 --- a/crates/flashblocks-node/src/bin/main.rs +++ b/crates/flashblocks-node/src/bin/main.rs @@ -7,7 +7,7 @@ use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverla use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; use reth_optimism_cli::{Cli, chainspec::OpChainSpecParser}; use reth_optimism_node::{OpNode, args::RollupArgs}; -use tokio::sync::{broadcast, mpsc}; +use tokio::sync::broadcast; use tracing::info; #[derive(Debug, Clone, clap::Args)] @@ -26,7 +26,7 @@ pub fn main() { let rollup_args = args.rollup_args; let chain_spec = builder.config().chain.clone(); let (inbound_tx, inbound_rx) = broadcast::channel(100); - let (_publish_tx, publish_rx) = mpsc::unbounded_channel(); + let (_publish_tx, publish_rx) = broadcast::channel(100); let flashblocks_overlay = FlashblocksOverlay::new(chain_spec, inbound_rx); flashblocks_overlay.clone().start()?; diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index 4ee8a17b..0384d397 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -10,7 +10,7 @@ use flashblocks_rpc::{EthApiOverrideServer, FlashblocksApiExt, FlashblocksOverla use op_alloy_consensus::{OpPooledTransaction, OpTxEnvelope}; use reth_eth_wire::BasicNetworkPrimitives; use reth_ethereum::network::{NetworkProtocols, protocol::IntoRlpxSubProtocol}; -use reth_network::{NetworkHandle, Peers, PeersInfo, events::NetworkPeersEvents}; +use reth_network::{NetworkHandle, Peers, PeersInfo}; use reth_network_peers::{NodeRecord, PeerId}; use reth_node_builder::{Node, NodeBuilder, NodeConfig, NodeHandle}; use reth_node_core::{ @@ -40,7 +40,7 @@ type Network = NetworkHandle< pub struct NodeContext { flashblocks_tx: broadcast::Sender, - publish_tx: mpsc::UnboundedSender, + publish_tx: broadcast::Sender, pub local_node_record: NodeRecord, http_api_addr: SocketAddr, _node_exit_future: NodeExitFuture, @@ -62,7 +62,7 @@ async fn setup_node( authorizer: SigningKey, trusted_peer: Option<(PeerId, SocketAddr)>, ) -> eyre::Result { - let (publish_tx, publish_rx) = mpsc::unbounded_channel(); + let (publish_tx, publish_rx) = broadcast::channel(100); let (inbound_tx, inbound_rx) = broadcast::channel(100); let genesis: Genesis = serde_json::from_str(include_str!("assets/genesis.json")).unwrap(); From 0abf5e82676756ce06ac69c9e8de5831c4faef56 Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Mon, 21 Jul 2025 14:57:14 -0700 Subject: [PATCH 43/45] target: flashblocks::p2p --- crates/flashblocks-p2p/src/protocol/connection.rs | 12 ++++++------ crates/flashblocks-p2p/src/protocol/handler.rs | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/flashblocks-p2p/src/protocol/connection.rs b/crates/flashblocks-p2p/src/protocol/connection.rs index 81611aef..53b656e2 100644 --- a/crates/flashblocks-p2p/src/protocol/connection.rs +++ b/crates/flashblocks-p2p/src/protocol/connection.rs @@ -43,7 +43,7 @@ impl Stream for FlashblocksConnection { || this.received.get(flashblock_index) != Some(&true) { trace!( - target: "flashblocks", + target: "flashblocks::p2p", peer_id = %this.peer_id, %payload_id, %flashblock_index, @@ -54,7 +54,7 @@ impl Stream for FlashblocksConnection { } Err(error) => { tracing::error!( - target: "flashblocks", + target: "flashblocks::p2p", %error, "Failed to receive flashblocks message from peer_rx" ); @@ -71,7 +71,7 @@ impl Stream for FlashblocksConnection { Ok(msg) => msg, Err(error) => { tracing::warn!( - target: "flashblocks", + target: "flashblocks::p2p", peer_id = %this.peer_id, %error, "Failed to decode flashblocks message from peer", @@ -99,7 +99,7 @@ impl FlashblocksConnection { if let Err(error) = message.verify(self.handler.ctx.authorizer_vk) { tracing::warn!( - target: "flashblocks", + target: "flashblocks::p2p", peer_id = %self.peer_id, %error, "Failed to verify flashblock", @@ -113,7 +113,7 @@ impl FlashblocksConnection { if message.authorization.timestamp < state.payload_timestamp { tracing::warn!( - target: "flashblocks", + target: "flashblocks::p2p", peer_id = %self.peer_id, timestamp = message.authorization.timestamp, "Received flashblock with outdated timestamp", @@ -147,7 +147,7 @@ impl FlashblocksConnection { // We've already seen this index from this peer. // They could be trying to DOS us. tracing::warn!( - target: "flashblocks", + target: "flashblocks::p2p", peer_id = %self.peer_id, payload_id = %message.payload.payload_id, index = message.payload.index, diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 50d8de6c..4e2c762d 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -114,7 +114,7 @@ impl FlashblocksP2PCtx { // Resize our array if needed if message.payload.index as usize > MAX_FLASHBLOCK_INDEX { tracing::error!( - target: "flashblocks", + target: "flashblocks::p2p", index = message.payload.index, max_index = MAX_FLASHBLOCK_INDEX, "Received flashblocks payload with index exceeding maximum" @@ -134,7 +134,7 @@ impl FlashblocksP2PCtx { // Add the flashblock to our cache *flashblock = Some(message.clone().payload); tracing::trace!( - target: "flashblocks", + target: "flashblocks::p2p", payload_id = %message.payload.payload_id, flashblock_index = message.payload.index, "queueing flashblock", @@ -145,7 +145,7 @@ impl FlashblocksP2PCtx { if len > MAX_FRAME { tracing::error!( - target: "flashblocks", + target: "flashblocks::p2p", size = bytes.len(), max_size = MAX_FRAME, "FlashblocksP2PMsg too large", @@ -154,7 +154,7 @@ impl FlashblocksP2PCtx { } if len > MAX_FRAME / 2 { tracing::warn!( - target: "flashblocks", + target: "flashblocks::p2p", size = bytes.len(), max_size = MAX_FRAME, "FlashblocksP2PMsg almost too large", @@ -171,7 +171,7 @@ impl FlashblocksP2PCtx { while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { // Publish the flashblock debug!( - target: "flashblocks", + target: "flashblocks::p2p", payload_id = %flashblock_event.payload_id, flashblock_index = %state.flashblock_index, "publishing flashblock" From d1f3ed140e72e75cbbad40ad2565afec8fa78f6c Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Tue, 22 Jul 2025 14:40:43 -0700 Subject: [PATCH 44/45] feat: Authorized Msg work --- crates/flashblocks-node/tests/p2p.rs | 20 +- crates/flashblocks-p2p/src/net/mod.rs | 6 +- .../src/protocol/connection.rs | 77 ++--- .../flashblocks-p2p/src/protocol/handler.rs | 36 +-- crates/rollup-boost/src/flashblocks/p2p.rs | 265 +++++++++++++++--- 5 files changed, 300 insertions(+), 104 deletions(-) diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index 0384d397..a29f1a30 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -23,11 +23,11 @@ use reth_optimism_primitives::{OpPrimitives, OpReceipt}; use reth_provider::providers::BlockchainProvider; use reth_tasks::{TaskExecutor, TaskManager}; use rollup_boost::{ - Authorization, Authorized, ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, - FlashblocksP2PMsg, FlashblocksPayloadV1, + Authorization, AuthorizedPayload, ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1, + FlashblocksPayloadV1, }; use std::{any::Any, collections::HashMap, net::SocketAddr, str::FromStr, sync::Arc}; -use tokio::sync::{broadcast, mpsc}; +use tokio::sync::broadcast; use tracing::info; type Network = NetworkHandle< @@ -40,7 +40,7 @@ type Network = NetworkHandle< pub struct NodeContext { flashblocks_tx: broadcast::Sender, - publish_tx: broadcast::Sender, + publish_tx: broadcast::Sender>, pub local_node_record: NodeRecord, http_api_addr: SocketAddr, _node_exit_future: NodeExitFuture, @@ -327,9 +327,9 @@ async fn test_peering() -> eyre::Result<()> { &authorizer, builder.verifying_key(), ); - let authorized = Authorized::new(&builder, authorization, payload_0.clone()); - let proto_message = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized); - node_1.publish_tx.send(proto_message)?; + let msg = payload_0.clone(); + let authorized = AuthorizedPayload::new(&builder, authorization, msg); + node_1.publish_tx.send(authorized)?; tokio::time::sleep(tokio::time::Duration::from_millis(10000)).await; let peers = node_0.network_handle.get_all_peers().await?; let peer_1 = &peers[1].remote_id; @@ -354,10 +354,8 @@ async fn test_peering() -> eyre::Result<()> { &authorizer, builder.verifying_key(), ); - let authorized = Authorized::new(&builder, authorization, payload_1.clone()); - let proto_message = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized); - - node_1.publish_tx.send(proto_message)?; + let authorized = AuthorizedPayload::new(&builder, authorization, payload_1.clone()); + node_1.publish_tx.send(authorized)?; tokio::time::sleep(tokio::time::Duration::from_millis(5000)).await; let rep_1 = node_0.network_handle.reputation_by_id(*peer_1).await?; diff --git a/crates/flashblocks-p2p/src/net/mod.rs b/crates/flashblocks-p2p/src/net/mod.rs index ba2b8fa7..0d8b08b4 100644 --- a/crates/flashblocks-p2p/src/net/mod.rs +++ b/crates/flashblocks-p2p/src/net/mod.rs @@ -10,7 +10,7 @@ use reth_node_builder::{ node::{FullNodeTypes, NodeTypes}, }; use reth_transaction_pool::{PoolTransaction, TransactionPool}; -use rollup_boost::{FlashblocksP2PMsg, FlashblocksPayloadV1}; +use rollup_boost::{AuthorizedPayload, FlashblocksPayloadV1}; use tokio::sync::broadcast; use crate::protocol::handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}; @@ -19,7 +19,7 @@ use crate::protocol::handler::{FlashblocksHandler, FlashblocksP2PNetworHandle}; struct FlashblocksNetworkBuilderCtx { authorizer_vk: VerifyingKey, flashblocks_receiver_tx: broadcast::Sender, - publish_rx: broadcast::Receiver, + publish_rx: broadcast::Receiver>, } #[derive(Debug)] @@ -33,7 +33,7 @@ impl FlashblocksNetworkBuilder { inner: T, authorizer_vk: VerifyingKey, flashblocks_receiver_tx: broadcast::Sender, - publish_rx: broadcast::Receiver, + publish_rx: broadcast::Receiver>, ) -> Self { Self { inner, diff --git a/crates/flashblocks-p2p/src/protocol/connection.rs b/crates/flashblocks-p2p/src/protocol/connection.rs index 53b656e2..26ab10b5 100644 --- a/crates/flashblocks-p2p/src/protocol/connection.rs +++ b/crates/flashblocks-p2p/src/protocol/connection.rs @@ -4,7 +4,7 @@ use futures::{Stream, StreamExt}; use reth::payload::PayloadId; use reth_ethereum::network::{api::PeerId, eth_wire::multiplex::ProtocolConnection}; use reth_network::types::ReputationChangeKind; -use rollup_boost::{Authorized, FlashblocksP2PMsg, FlashblocksPayloadV1}; +use rollup_boost::{AuthorizedPayload, FlashblocksP2PMsg, FlashblocksPayloadV1}; use std::{ pin::Pin, task::{Context, Poll, ready}, @@ -85,8 +85,30 @@ impl Stream for FlashblocksConnection { }; match msg { - FlashblocksP2PMsg::FlashblocksPayloadV1(authorized) => { - this.handle_flashblocks_payload_v1(authorized); + FlashblocksP2PMsg::Authorized(authorized) => { + if let Err(error) = authorized.verify(this.handler.ctx.authorizer_vk) { + tracing::warn!( + target: "flashblocks::p2p", + peer_id = %this.peer_id, + %error, + "Failed to verify flashblock", + ); + this.handler + .ctx + .network_handle + .reputation_change(this.peer_id, ReputationChangeKind::BadMessage); + continue; + } + + match &authorized.msg { + rollup_boost::AuthorizedMsg::FlashblocksPayloadV1(_) => { + this.handle_flashblocks_payload_v1(authorized.into_unchecked()); + } + rollup_boost::AuthorizedMsg::InnitiateBuildReq(_) => todo!(), + rollup_boost::AuthorizedMsg::InnitiateBuildRes(_initiate_build_res) => { + todo!() + } + } } } } @@ -94,28 +116,19 @@ impl Stream for FlashblocksConnection { } impl FlashblocksConnection { - fn handle_flashblocks_payload_v1(&mut self, message: Authorized) { + fn handle_flashblocks_payload_v1( + &mut self, + authorized: AuthorizedPayload, + ) { let mut state = self.handler.state.lock(); + let authorization = &authorized.authorized.authorization; + let msg = authorized.msg(); - if let Err(error) = message.verify(self.handler.ctx.authorizer_vk) { - tracing::warn!( - target: "flashblocks::p2p", - peer_id = %self.peer_id, - %error, - "Failed to verify flashblock", - ); - self.handler - .ctx - .network_handle - .reputation_change(self.peer_id, ReputationChangeKind::BadMessage); - return; - } - - if message.authorization.timestamp < state.payload_timestamp { + if authorization.timestamp < state.payload_timestamp { tracing::warn!( target: "flashblocks::p2p", peer_id = %self.peer_id, - timestamp = message.authorization.timestamp, + timestamp = authorization.timestamp, "Received flashblock with outdated timestamp", ); self.handler @@ -126,31 +139,31 @@ impl FlashblocksConnection { } // Check if this is a globally new payload - if message.authorization.timestamp > state.payload_timestamp { + if authorization.timestamp > state.payload_timestamp { state.flashblock_index = 0; - state.payload_timestamp = message.authorization.timestamp; - state.payload_id = message.payload.payload_id; + state.payload_timestamp = authorization.timestamp; + state.payload_id = msg.payload_id; state.flashblocks.fill(None); } // Check if this is a new payload from this peer - if self.payload_id != message.payload.payload_id { - self.payload_id = message.payload.payload_id; + if self.payload_id != msg.payload_id { + self.payload_id = msg.payload_id; self.received.fill(false); } // Check if this peer is spamming us with the same payload index let len = self.received.len(); self.received - .resize_with(len.max(message.payload.index as usize + 1), || false); - if self.received[message.payload.index as usize] { + .resize_with(len.max(msg.index as usize + 1), || false); + if self.received[msg.index as usize] { // We've already seen this index from this peer. // They could be trying to DOS us. tracing::warn!( target: "flashblocks::p2p", peer_id = %self.peer_id, - payload_id = %message.payload.payload_id, - index = message.payload.index, + payload_id = %msg.payload_id, + index = msg.index, "Received duplicate flashblock from peer", ); self.handler @@ -159,10 +172,8 @@ impl FlashblocksConnection { .reputation_change(self.peer_id, ReputationChangeKind::AlreadySeenTransaction); return; } - self.received[message.payload.index as usize] = true; + self.received[msg.index as usize] = true; - self.handler - .ctx - .publish(&mut state, FlashblocksP2PMsg::FlashblocksPayloadV1(message)); + self.handler.ctx.publish(&mut state, authorized); } } diff --git a/crates/flashblocks-p2p/src/protocol/handler.rs b/crates/flashblocks-p2p/src/protocol/handler.rs index 4e2c762d..d8de69ec 100644 --- a/crates/flashblocks-p2p/src/protocol/handler.rs +++ b/crates/flashblocks-p2p/src/protocol/handler.rs @@ -6,7 +6,7 @@ use reth::payload::PayloadId; use reth_eth_wire::Capability; use reth_ethereum::network::{api::PeerId, protocol::ProtocolHandler}; use reth_network::Peers; -use rollup_boost::{FlashblocksP2PMsg, FlashblocksPayloadV1}; +use rollup_boost::{AuthorizedPayload, FlashblocksP2PMsg, FlashblocksPayloadV1}; use std::net::SocketAddr; use std::sync::Arc; use tokio::sync::broadcast; @@ -74,7 +74,7 @@ impl FlashblocksHandler { network_handle: N, authorizer_vk: VerifyingKey, flashblock_tx: broadcast::Sender, - mut publish_rx: broadcast::Receiver, + mut publish_rx: broadcast::Receiver>, ) -> Self { let peer_tx = broadcast::Sender::new(100); let state = Arc::new(Mutex::new(FlashblocksP2PState::default())); @@ -108,14 +108,18 @@ impl FlashblocksHandler { impl FlashblocksP2PCtx { /// Commit new and already verified flashblocks payloads to the state /// broadcast them to peers, and publish them to the stream. - pub fn publish(&self, state: &mut FlashblocksP2PState, msg: FlashblocksP2PMsg) { - let FlashblocksP2PMsg::FlashblocksPayloadV1(ref message) = msg; + pub fn publish( + &self, + state: &mut FlashblocksP2PState, + authorized_payload: AuthorizedPayload, + ) { + let payload = authorized_payload.msg(); // Resize our array if needed - if message.payload.index as usize > MAX_FLASHBLOCK_INDEX { + if payload.index as usize > MAX_FLASHBLOCK_INDEX { tracing::error!( target: "flashblocks::p2p", - index = message.payload.index, + index = payload.index, max_index = MAX_FLASHBLOCK_INDEX, "Received flashblocks payload with index exceeding maximum" ); @@ -124,22 +128,24 @@ impl FlashblocksP2PCtx { let len = state.flashblocks.len(); state .flashblocks - .resize_with(len.max(message.payload.index as usize + 1), || None); - let flashblock = &mut state.flashblocks[message.payload.index as usize]; + .resize_with(len.max(payload.index as usize + 1), || None); + let flashblock = &mut state.flashblocks[payload.index as usize]; // If we've already seen this index, skip it // Otherwise, add it to the list if flashblock.is_none() { // We haven't seen this index yet // Add the flashblock to our cache - *flashblock = Some(message.clone().payload); + *flashblock = Some(payload.clone()); tracing::trace!( target: "flashblocks::p2p", - payload_id = %message.payload.payload_id, - flashblock_index = message.payload.index, + payload_id = %payload.payload_id, + flashblock_index = payload.index, "queueing flashblock", ); - let bytes = msg.encode(); + + let p2p_msg = FlashblocksP2PMsg::Authorized(authorized_payload.authorized.clone()); + let bytes = p2p_msg.encode(); let len = bytes.len(); metrics::histogram!("flashblock_size").record(len as f64); @@ -161,11 +167,7 @@ impl FlashblocksP2PCtx { ); } self.peer_tx - .send(( - message.payload.payload_id, - message.payload.index as usize, - bytes, - )) + .send((payload.payload_id, payload.index as usize, bytes)) .ok(); // Broadcast any flashblocks in the cache that are in order while let Some(Some(flashblock_event)) = state.flashblocks.get(state.flashblock_index) { diff --git a/crates/rollup-boost/src/flashblocks/p2p.rs b/crates/rollup-boost/src/flashblocks/p2p.rs index 3f260f67..1b3ae5fc 100644 --- a/crates/rollup-boost/src/flashblocks/p2p.rs +++ b/crates/rollup-boost/src/flashblocks/p2p.rs @@ -1,3 +1,5 @@ +use std::marker::PhantomData; + use alloy_primitives::{B64, Bytes}; use alloy_rlp::{Decodable, Encodable, Header}; use alloy_rpc_types_engine::PayloadId; @@ -15,17 +17,47 @@ pub struct Authorization { pub authorizer_sig: Signature, } -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] -pub struct Authorized { - pub payload: T, - pub authorization: Authorization, - pub builder_sig: Signature, +#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, Eq)] +pub struct InitiateBuildReq; + +#[repr(u8)] +#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize, Eq)] +pub enum InitiateBuildRes { + Granted = 0x00, + Denied = 0x01, } +/// A message that can be sent over the Flashblocks P2P network. #[repr(u8)] #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Eq)] pub enum FlashblocksP2PMsg { - FlashblocksPayloadV1(Authorized) = 0x00, + Authorized(Authorized) = 0x00, +} + +#[repr(u8)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, Eq)] +pub enum AuthorizedMsg { + FlashblocksPayloadV1(FlashblocksPayloadV1) = 0x00, + InnitiateBuildReq(InitiateBuildReq) = 0x01, + InnitiateBuildRes(InitiateBuildRes) = 0x02, +} + +impl From for AuthorizedMsg { + fn from(payload: FlashblocksPayloadV1) -> Self { + Self::FlashblocksPayloadV1(payload) + } +} + +impl From for AuthorizedMsg { + fn from(req: InitiateBuildReq) -> Self { + Self::InnitiateBuildReq(req) + } +} + +impl From for AuthorizedMsg { + fn from(res: InitiateBuildRes) -> Self { + Self::InnitiateBuildRes(res) + } } impl Authorization { @@ -141,38 +173,92 @@ impl Decodable for Authorization { } } -impl Authorized { - pub fn new(builder_sk: &SigningKey, authorization: Authorization, payload: T) -> Self { - let hash = blake3::hash(&serde_json::to_vec(&payload).unwrap()); - let builder_sig = builder_sk.sign(hash.as_bytes()); +/// A signed and authorized message that can be sent over the Flashblocks P2P network. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct AuthorizedPayload { + pub authorized: Authorized, + /// The underlying message type + pub _marker: PhantomData, +} + +impl AuthorizedPayload +where + T: Into, +{ + pub fn new(actor_sk: &SigningKey, authorization: Authorization, msg: T) -> Self { + let msg = msg.into(); + let authorized = Authorized::new(actor_sk, authorization, msg); Self { - payload, + authorized, + _marker: PhantomData, + } + } +} + +/// A signed and authorized message that can be sent over the Flashblocks P2P network. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct Authorized { + /// The msg that is being authorized and signed over. + pub msg: AuthorizedMsg, + /// The authorization that grants permission to send this message. + pub authorization: Authorization, + /// The signature of the actor, made over the hash of the message and authorization. + pub actor_sig: Signature, +} + +impl Authorized { + pub fn new(actor_sk: &SigningKey, authorization: Authorization, msg: AuthorizedMsg) -> Self { + let mut encoded = Vec::new(); + msg.encode(&mut encoded); + authorization.encode(&mut encoded); + + let hash = blake3::hash(&encoded); + let actor_sig = actor_sk.sign(hash.as_bytes()); + + Self { + msg, authorization, - builder_sig, + actor_sig, } } pub fn verify(&self, authorizer_pub: VerifyingKey) -> Result<(), FlashblocksP2PError> { self.authorization.verify(authorizer_pub)?; - let hash = blake3::hash(&serde_json::to_vec(&self.payload).unwrap()); + let mut encoded = Vec::new(); + self.msg.encode(&mut encoded); + self.authorization.encode(&mut encoded); + let hash = blake3::hash(&encoded); self.authorization .builder_pub - .verify(hash.as_bytes(), &self.builder_sig) + .verify(hash.as_bytes(), &self.actor_sig) .map_err(|_| FlashblocksP2PError::InvalidBuilderSig) } + + pub fn into_unchecked(self) -> AuthorizedPayload { + AuthorizedPayload:: { + authorized: self, + _marker: PhantomData, + } + } } -impl Encodable for Authorized +impl AuthorizedPayload where - T: Encodable + Serialize, + AuthorizedMsg: AsRef, { + pub fn msg(&self) -> &T { + self.authorized.msg.as_ref() + } +} + +impl Encodable for Authorized { fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { // encode once so we know the length beforehand - let sig_bytes = Bytes::copy_from_slice(&self.builder_sig.to_bytes()); - let payload_len = self.payload.length() + self.authorization.length() + sig_bytes.length(); + let sig_bytes = Bytes::copy_from_slice(&self.actor_sig.to_bytes()); + let payload_len = self.msg.length() + self.authorization.length() + sig_bytes.length(); Header { list: true, @@ -181,7 +267,7 @@ where .encode(out); // 1. payload - self.payload.encode(out); + self.msg.encode(out); // 2. authorization self.authorization.encode(out); // 3. builder signature @@ -189,8 +275,8 @@ where } fn length(&self) -> usize { - let sig_bytes = Bytes::copy_from_slice(&self.builder_sig.to_bytes()); - let payload_len = self.payload.length() + self.authorization.length() + sig_bytes.length(); + let sig_bytes = Bytes::copy_from_slice(&self.actor_sig.to_bytes()); + let payload_len = self.msg.length() + self.authorization.length() + sig_bytes.length(); Header { list: true, @@ -201,10 +287,7 @@ where } } -impl Decodable for Authorized -where - T: Decodable + Serialize, -{ +impl Decodable for Authorized { fn decode(buf: &mut &[u8]) -> Result { let header = Header::decode(buf)?; if !header.list { @@ -214,7 +297,7 @@ where let mut body = &buf[..header.payload_length as usize]; // 1. payload - let payload = T::decode(&mut body)?; + let payload = AuthorizedMsg::decode(&mut body)?; // 2. authorization let authorization = Authorization::decode(&mut body)?; // 3. builder signature @@ -226,9 +309,9 @@ where *buf = &buf[header.payload_length as usize..]; Ok(Self { - payload, + msg: payload, authorization, - builder_sig, + actor_sig: builder_sig, }) } } @@ -238,7 +321,7 @@ impl FlashblocksP2PMsg { pub fn encode(&self) -> BytesMut { let mut buf = BytesMut::new(); match self { - FlashblocksP2PMsg::FlashblocksPayloadV1(payload) => { + FlashblocksP2PMsg::Authorized(payload) => { buf.put_u8(0x00); payload.encode(&mut buf); } @@ -255,14 +338,113 @@ impl FlashblocksP2PMsg { buf.advance(1); match id { 0x00 => { - let payload = Authorized::::decode(&mut &buf[..])?; - Ok(FlashblocksP2PMsg::FlashblocksPayloadV1(payload)) + let payload = Authorized::decode(&mut &buf[..])?; + Ok(FlashblocksP2PMsg::Authorized(payload)) } _ => Err(FlashblocksP2PError::UnknownMessageType), } } } +impl AsRef for AuthorizedMsg { + fn as_ref(&self) -> &FlashblocksPayloadV1 { + match self { + Self::FlashblocksPayloadV1(p) => p, + _ => panic!("not a FlashblocksPayloadV1 message"), + } + } +} + +impl Encodable for AuthorizedMsg { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + match self { + Self::FlashblocksPayloadV1(p) => { + Header { + list: true, + payload_length: 1 + p.length(), + } + .encode(out); + 0u32.encode(out); + p.encode(out); + } + Self::InnitiateBuildReq(_) => { + Header { + list: true, + payload_length: 1, + } + .encode(out); + 1u32.encode(out); + } + Self::InnitiateBuildRes(r) => { + Header { + list: true, + payload_length: 1 + r.length(), + } + .encode(out); + 2u32.encode(out); + r.encode(out); + } + }; + } + + fn length(&self) -> usize { + let body_len = match self { + Self::FlashblocksPayloadV1(p) => 1 + p.length(), + Self::InnitiateBuildReq(_) => 1, + Self::InnitiateBuildRes(r) => 1 + r.length(), + }; + + Header { + list: true, + payload_length: body_len, + } + .length() + + body_len + } +} + +impl Decodable for AuthorizedMsg { + fn decode(buf: &mut &[u8]) -> Result { + let hdr = Header::decode(buf)?; + if !hdr.list { + return Err(alloy_rlp::Error::Custom( + "AuthorizedMsg must be an RLP list", + )); + } + + let tag = u8::decode(buf)?; + let value = match tag { + 0 => Self::FlashblocksPayloadV1(FlashblocksPayloadV1::decode(buf)?), + 1 => Self::InnitiateBuildReq(InitiateBuildReq), + 2 => Self::InnitiateBuildRes(InitiateBuildRes::decode(buf)?), + _ => return Err(alloy_rlp::Error::Custom("unknown tag")), + }; + + Ok(value) + } +} + +impl Encodable for InitiateBuildRes { + fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { + (*self as u32).encode(out); + } + + fn length(&self) -> usize { + 1 + } +} + +impl Decodable for InitiateBuildRes { + fn decode(buf: &mut &[u8]) -> Result { + let tag = u8::decode(buf)?; + match tag { + 0x00 => Ok(InitiateBuildRes::Granted), + 0x01 => Ok(InitiateBuildRes::Denied), + _ => Err(alloy_rlp::Error::Custom("unknown tag")), + } + } +} + #[cfg(test)] mod tests { use crate::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; @@ -373,15 +555,16 @@ mod tests { let (builder_sk, builder_vk) = sample_keys(); let authorization = sample_authorization(); let payload = sample_flashblocks_payload(); + let msg = AuthorizedMsg::FlashblocksPayloadV1(payload.clone()); - let authorized = Authorized::new(&builder_sk, authorization.clone(), payload.clone()); + let authorized = Authorized::new(&builder_sk, authorization.clone(), msg); // RLP round-trip let encoded = encode(&authorized); assert_eq!(encoded.len(), authorized.length()); let mut slice = encoded.as_ref(); - let decoded = Authorized::::decode(&mut slice).expect("decode ok"); + let decoded = Authorized::decode(&mut slice).expect("decode ok"); assert_eq!(decoded, authorized); assert!(slice.is_empty(), "decoder consumed all input"); @@ -391,7 +574,7 @@ mod tests { let hash = blake3::hash(&serde_json::to_vec(&payload).unwrap()); builder_vk - .verify(hash.as_bytes(), &decoded.builder_sig) + .verify(hash.as_bytes(), &decoded.actor_sig) .expect("builder sig valid"); } @@ -400,12 +583,13 @@ mod tests { let (builder_sk, _) = sample_keys(); let authorization = sample_authorization(); let payload = sample_flashblocks_payload(); + let msg = AuthorizedMsg::FlashblocksPayloadV1(payload.clone()); - let mut authorized = Authorized::new(&builder_sk, authorization, payload); + let mut authorized = Authorized::new(&builder_sk, authorization, msg); // flip one bit - let mut authorized_sig_bytes = authorized.builder_sig.to_bytes(); + let mut authorized_sig_bytes = authorized.actor_sig.to_bytes(); authorized_sig_bytes[0] ^= 0x01; - authorized.builder_sig = + authorized.actor_sig = Signature::try_from(authorized_sig_bytes.as_ref()).expect("valid signature bytes"); assert!( authorized @@ -420,16 +604,17 @@ mod tests { let (builder_sk, _) = sample_keys(); let authorization = sample_authorization(); let payload = sample_flashblocks_payload(); - let authorized = Authorized::new(&builder_sk, authorization, payload); + let msg = AuthorizedMsg::FlashblocksPayloadV1(payload.clone()); + let authorized = Authorized::new(&builder_sk, authorization, msg); - let msg = FlashblocksP2PMsg::FlashblocksPayloadV1(authorized.clone()); + let msg = FlashblocksP2PMsg::Authorized(authorized.clone()); let encoded = msg.encode(); let decoded = FlashblocksP2PMsg::decode(&mut &encoded[..]).expect("decode ok"); match decoded { - FlashblocksP2PMsg::FlashblocksPayloadV1(inner) => { + FlashblocksP2PMsg::Authorized(inner) => { assert_eq!(inner, authorized, "inner payload round-trips"); } } From 36559b83afc275f77aa9dffdfa428430e54360ab Mon Sep 17 00:00:00 2001 From: Eric Woolsey Date: Tue, 22 Jul 2025 15:04:33 -0700 Subject: [PATCH 45/45] feat: improve rlp testing --- crates/flashblocks-node/tests/p2p.rs | 2 +- crates/rollup-boost/src/flashblocks/error.rs | 2 +- crates/rollup-boost/src/flashblocks/p2p.rs | 183 +++++++++++-------- 3 files changed, 111 insertions(+), 76 deletions(-) diff --git a/crates/flashblocks-node/tests/p2p.rs b/crates/flashblocks-node/tests/p2p.rs index a29f1a30..88d54a12 100644 --- a/crates/flashblocks-node/tests/p2p.rs +++ b/crates/flashblocks-node/tests/p2p.rs @@ -332,7 +332,7 @@ async fn test_peering() -> eyre::Result<()> { node_1.publish_tx.send(authorized)?; tokio::time::sleep(tokio::time::Duration::from_millis(10000)).await; let peers = node_0.network_handle.get_all_peers().await?; - let peer_1 = &peers[1].remote_id; + let peer_1 = &peers[0].remote_id; let rep_1 = node_0.network_handle.reputation_by_id(*peer_1).await?; info!(?rep_1, "Peer reputation"); diff --git a/crates/rollup-boost/src/flashblocks/error.rs b/crates/rollup-boost/src/flashblocks/error.rs index c94c4327..33b2fc8d 100644 --- a/crates/rollup-boost/src/flashblocks/error.rs +++ b/crates/rollup-boost/src/flashblocks/error.rs @@ -1,6 +1,6 @@ use thiserror::Error; -#[derive(Error, Debug)] +#[derive(Error, Debug, Eq, PartialEq)] pub enum FlashblocksP2PError { #[error("invalid authorizer signature")] InvalidAuthorizerSig, diff --git a/crates/rollup-boost/src/flashblocks/p2p.rs b/crates/rollup-boost/src/flashblocks/p2p.rs index 1b3ae5fc..b33a0570 100644 --- a/crates/rollup-boost/src/flashblocks/p2p.rs +++ b/crates/rollup-boost/src/flashblocks/p2p.rs @@ -338,7 +338,7 @@ impl FlashblocksP2PMsg { buf.advance(1); match id { 0x00 => { - let payload = Authorized::decode(&mut &buf[..])?; + let payload = Authorized::decode(buf)?; Ok(FlashblocksP2PMsg::Authorized(payload)) } _ => Err(FlashblocksP2PError::UnknownMessageType), @@ -447,29 +447,32 @@ impl Decodable for InitiateBuildRes { #[cfg(test)] mod tests { - use crate::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; - use super::*; + use crate::{ExecutionPayloadBaseV1, ExecutionPayloadFlashblockDeltaV1}; use alloy_primitives::{Address, B256, Bloom, U256}; - use alloy_rlp::{Decodable, encode}; + use alloy_rlp::{Decodable, Encodable, encode}; use alloy_rpc_types_eth::Withdrawal; + use bytes::{BufMut, BytesMut}; - fn sample_keys() -> (SigningKey, VerifyingKey) { - // deterministic keys for reproducible tests - let bytes = [0u8; 32]; + fn key_pair(seed: u8) -> (SigningKey, VerifyingKey) { + let bytes = [seed; 32]; let sk = SigningKey::from_bytes(&bytes); let vk = sk.verifying_key(); (sk, vk) } - fn sample_authorization() -> Authorization { - let (authorizer_sk, _) = sample_keys(); - let (_, builder_vk) = sample_keys(); - Authorization::new( - alloy_rpc_types_engine::PayloadId::default(), - 1_700_000_321, - &authorizer_sk, - builder_vk, + fn sample_authorization() -> (Authorization, VerifyingKey) { + let (authorizer_sk, authorizer_vk) = key_pair(1); + let (_, builder_vk) = key_pair(2); + + ( + Authorization::new( + PayloadId::default(), + 1_700_000_001, + &authorizer_sk, + builder_vk, + ), + authorizer_vk, ) } @@ -503,7 +506,7 @@ mod tests { fn sample_flashblocks_payload() -> FlashblocksPayloadV1 { FlashblocksPayloadV1 { payload_id: PayloadId::default(), - index: 7, + index: 42, diff: sample_diff(), metadata: serde_json::json!({ "ok": true }), base: Some(sample_base()), @@ -511,9 +514,9 @@ mod tests { } #[test] - fn authorization_roundtrip() { - let (authorizer_sk, authorizer_vk) = sample_keys(); - let (_, builder_vk) = sample_keys(); + fn authorization_rlp_roundtrip_and_verify() { + let (authorizer_sk, authorizer_vk) = key_pair(1); + let (_, builder_vk) = key_pair(2); let auth = Authorization::new( PayloadId::default(), @@ -522,102 +525,134 @@ mod tests { builder_vk, ); - // RLP encode-then-decode let encoded = encode(&auth); - assert_eq!(encoded.len(), auth.length()); + assert_eq!(encoded.len(), auth.length(), "length impl correct"); let mut slice = encoded.as_ref(); - let decoded = Authorization::decode(&mut slice).expect("decode succeeds"); - assert_eq!(auth, decoded); - assert!(slice.is_empty()); + let decoded = Authorization::decode(&mut slice).expect("decoding succeeds"); + assert!(slice.is_empty(), "decoder consumed all bytes"); + assert_eq!(decoded, auth, "round-trip preserves value"); - // signature verifies - decoded.verify(authorizer_vk).expect("sig valid"); + // Signature is valid + decoded.verify(authorizer_vk).expect("signature verifies"); } #[test] - fn tampered_sig_is_rejected() { - let (authorizer_sk, authorizer_vk) = sample_keys(); - let (_, builder_vk) = sample_keys(); + fn authorization_signature_tamper_is_detected() { + let (authorizer_sk, authorizer_vk) = key_pair(1); + let (_, builder_vk) = key_pair(2); let mut auth = Authorization::new(PayloadId::default(), 42, &authorizer_sk, builder_vk); - // flip one bit in the signature - let mut auth_sig_bytes = auth.authorizer_sig.to_bytes(); - auth_sig_bytes[0] ^= 0x01; - auth.authorizer_sig = - Signature::try_from(auth_sig_bytes.as_ref()).expect("valid signature bytes"); + let mut sig_bytes = auth.authorizer_sig.to_bytes(); + sig_bytes[0] ^= 1; + auth.authorizer_sig = Signature::try_from(sig_bytes.as_ref()).unwrap(); + assert!(auth.verify(authorizer_vk).is_err()); } #[test] - fn authorized_roundtrip_and_verify() { - let (builder_sk, builder_vk) = sample_keys(); - let authorization = sample_authorization(); + fn authorized_rlp_roundtrip_and_verify() { + let (builder_sk, _builder_vk) = key_pair(2); + let (authorization, authorizer_vk) = sample_authorization(); + let payload = sample_flashblocks_payload(); - let msg = AuthorizedMsg::FlashblocksPayloadV1(payload.clone()); + let msg = AuthorizedMsg::FlashblocksPayloadV1(payload); let authorized = Authorized::new(&builder_sk, authorization.clone(), msg); - // RLP round-trip + // Encode → decode let encoded = encode(&authorized); assert_eq!(encoded.len(), authorized.length()); let mut slice = encoded.as_ref(); - let decoded = Authorized::decode(&mut slice).expect("decode ok"); + let decoded = Authorized::decode(&mut slice).expect("decoding succeeds"); + assert!(slice.is_empty()); assert_eq!(decoded, authorized); - assert!(slice.is_empty(), "decoder consumed all input"); decoded - .verify(authorization.builder_pub) - .expect("verify succeeds"); - - let hash = blake3::hash(&serde_json::to_vec(&payload).unwrap()); - builder_vk - .verify(hash.as_bytes(), &decoded.actor_sig) - .expect("builder sig valid"); + .verify(authorizer_vk) + .expect("composite verification succeeds"); } #[test] - fn builder_sig_tamper_fails() { - let (builder_sk, _) = sample_keys(); - let authorization = sample_authorization(); + fn authorized_builder_signature_tamper_is_detected() { + let (builder_sk, _) = key_pair(2); + let (authorization, authorizer_vk) = sample_authorization(); let payload = sample_flashblocks_payload(); - let msg = AuthorizedMsg::FlashblocksPayloadV1(payload.clone()); + let msg = AuthorizedMsg::FlashblocksPayloadV1(payload); let mut authorized = Authorized::new(&builder_sk, authorization, msg); - // flip one bit - let mut authorized_sig_bytes = authorized.actor_sig.to_bytes(); - authorized_sig_bytes[0] ^= 0x01; - authorized.actor_sig = - Signature::try_from(authorized_sig_bytes.as_ref()).expect("valid signature bytes"); - assert!( - authorized - .verify(authorized.authorization.builder_pub) - .is_err(), - "tampered sig must be rejected" - ); + + let mut sig_bytes = authorized.actor_sig.to_bytes(); + sig_bytes[0] ^= 1; + authorized.actor_sig = Signature::try_from(sig_bytes.as_ref()).unwrap(); + + assert!(authorized.verify(authorizer_vk).is_err()); + } + + #[test] + fn authorized_msg_variants_rlp_roundtrip() { + let variants = [ + AuthorizedMsg::FlashblocksPayloadV1(sample_flashblocks_payload()), + AuthorizedMsg::InnitiateBuildReq(InitiateBuildReq), + AuthorizedMsg::InnitiateBuildRes(InitiateBuildRes::Granted), + AuthorizedMsg::InnitiateBuildRes(InitiateBuildRes::Denied), + ]; + + for msg in variants { + let encoded = encode(&msg); + assert_eq!(encoded.len(), msg.length()); + + let mut slice = encoded.as_ref(); + let decoded = AuthorizedMsg::decode(&mut slice).expect("decodes"); + assert!(slice.is_empty()); + assert_eq!(decoded, msg); + } + } + + #[test] + fn initiate_build_res_rlp_roundtrip() { + for res in [InitiateBuildRes::Granted, InitiateBuildRes::Denied] { + let encoded = encode(res); + assert_eq!(encoded.len(), res.length()); + + let mut slice = encoded.as_ref(); + let decoded = InitiateBuildRes::decode(&mut slice).expect("decodes"); + assert_eq!(decoded, res); + assert!(slice.is_empty()); + } } #[test] fn p2p_msg_roundtrip() { - let (builder_sk, _) = sample_keys(); - let authorization = sample_authorization(); + let (builder_sk, _) = key_pair(2); + let (authorization, _authorizer_vk) = sample_authorization(); let payload = sample_flashblocks_payload(); - let msg = AuthorizedMsg::FlashblocksPayloadV1(payload.clone()); - let authorized = Authorized::new(&builder_sk, authorization, msg); + let msg = AuthorizedMsg::FlashblocksPayloadV1(payload); - let msg = FlashblocksP2PMsg::Authorized(authorized.clone()); + let authorized = Authorized::new(&builder_sk, authorization, msg); + let p2p = FlashblocksP2PMsg::Authorized(authorized.clone()); - let encoded = msg.encode(); + let encoded = p2p.encode(); - let decoded = FlashblocksP2PMsg::decode(&mut &encoded[..]).expect("decode ok"); + let mut view: &[u8] = &encoded; + let decoded = FlashblocksP2PMsg::decode(&mut view).expect("decoding succeeds"); + assert!(view.is_empty(), "all bytes consumed"); match decoded { - FlashblocksP2PMsg::Authorized(inner) => { - assert_eq!(inner, authorized, "inner payload round-trips"); - } + FlashblocksP2PMsg::Authorized(inner) => assert_eq!(inner, authorized), } - assert_eq!(encoded.remaining(), 0, "decoder consumed all input"); + } + + #[test] + fn p2p_msg_unknown_type_errors() { + let mut buf = BytesMut::new(); + buf.put_u8(0xFF); // unknown discriminator + + let mut slice: &[u8] = &buf; + let err = + FlashblocksP2PMsg::decode(&mut slice).expect_err("should fail on unknown message type"); + assert_eq!(err, FlashblocksP2PError::UnknownMessageType); } }