diff --git a/Cargo.lock b/Cargo.lock index ac164e52..1481cfc1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,9 +97,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.30" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f374d3c6d729268bbe2d0e0ff992bb97898b2df756691a62ee1d5f0506bc39" +checksum = "9247f0a399ef71aeb68f497b2b8fb348014f742b50d3b83b1e00dfe1b7d64b3d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -165,7 +165,7 @@ dependencies = [ "itoa", "serde", "serde_json", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -216,9 +216,9 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3231de68d5d6e75332b7489cfcc7f4dfabeba94d990a10e4b923af0e6623540" +checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -384,7 +384,7 @@ dependencies = [ "derive_more", "fixed-cache", "foldhash 0.2.0", - "getrandom 0.4.1", + "getrandom 0.4.2", "hashbrown 0.16.1", "indexmap 2.13.0", "itoa", @@ -432,7 +432,7 @@ dependencies = [ "either", "futures", "futures-utils-wasm", - "lru 0.16.3", + "lru", "parking_lot", "pin-project", "reqwest", @@ -486,7 +486,7 @@ checksum = "ce8849c74c9ca0f5a03da1c865e3eb6f768df816e67dd3721a398a8a7e398011" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -735,7 +735,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -752,7 +752,7 @@ dependencies = [ "proc-macro2", "quote", "sha3", - "syn 2.0.114", + "syn 2.0.117", "syn-solidity", ] @@ -768,7 +768,7 @@ dependencies = [ "macro-string", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "syn-solidity", ] @@ -779,7 +779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6df77fea9d6a2a75c0ef8d2acbdfd92286cc599983d3175ccdc170d3433d249" dependencies = [ "serde", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -872,14 +872,13 @@ dependencies = [ [[package]] name = "alloy-trie" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d7fd448ab0a017de542de1dcca7a58e7019fe0e7a34ed3f9543ebddf6aceffa" +checksum = "3f14b5d9b2c2173980202c6ff470d96e7c5e202c65a9f67884ad565226df7fbb" dependencies = [ "alloy-primitives", "alloy-rlp", "arbitrary", - "arrayvec", "derive_arbitrary", "derive_more", "nybbles", @@ -900,7 +899,7 @@ dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -914,9 +913,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -929,15 +928,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -964,9 +963,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "aquamarine" @@ -979,7 +978,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1121,7 +1120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1159,7 +1158,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1248,7 +1247,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1298,15 +1297,15 @@ dependencies = [ [[package]] name = "asn1_der" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" +checksum = "4858a9d740c5007a9069007c3b4e91152d0506f13c1b31dd49051fd537656156" [[package]] name = "async-compression" -version = "0.4.37" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40" +checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" dependencies = [ "compression-codecs", "compression-core", @@ -1333,7 +1332,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1344,7 +1343,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1358,6 +1357,15 @@ dependencies = [ "rustc_version 0.4.1", ] +[[package]] +name = "atomic-polyfill" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" +dependencies = [ + "critical-section", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -1382,7 +1390,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1449,7 +1457,7 @@ checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] name = "bera-reth" -version = "1.3.2" +version = "1.3.3" dependencies = [ "alloy-consensus", "alloy-eips", @@ -1461,6 +1469,7 @@ dependencies = [ "alloy-rlp", "alloy-rpc-client", "alloy-rpc-types", + "alloy-rpc-types-engine", "alloy-rpc-types-eth", "alloy-rpc-types-trace", "alloy-serde", @@ -1468,19 +1477,24 @@ dependencies = [ "alloy-sol-macro", "alloy-sol-types", "async-trait", + "blst", + "brotli 7.0.0", "bytes", "clap", "derive_more", "eyre", + "futures-util", + "hex", "jsonrpsee", "jsonrpsee-core", "jsonrpsee-proc-macros", + "metrics", "modular-bitfield", "reth", "reth-basic-payload-builder", + "reth-chain-state", "reth-chainspec", "reth-cli", - "reth-cli-commands", "reth-cli-util", "reth-codecs", "reth-consensus-common", @@ -1496,6 +1510,8 @@ dependencies = [ "reth-ethereum-primitives", "reth-evm", "reth-evm-ethereum", + "reth-execution-types", + "reth-metrics", "reth-network-peers", "reth-node-api", "reth-node-builder", @@ -1504,6 +1520,7 @@ dependencies = [ "reth-payload-primitives", "reth-payload-validator", "reth-primitives-traits", + "reth-revm", "reth-rpc", "reth-rpc-builder", "reth-rpc-convert", @@ -1511,16 +1528,21 @@ dependencies = [ "reth-rpc-eth-api", "reth-rpc-eth-types", "reth-storage-api", + "reth-tasks", "reth-transaction-pool", "reth-trie-common", "revm-inspectors", + "ringbuffer 0.15.0", "serde", "serde_json", "sha2", "test-fuzz", "thiserror 2.0.18", "tokio", + "tokio-tungstenite 0.26.2", + "tokio-util", "tracing", + "url", "vergen", "vergen-git2", ] @@ -1540,33 +1562,13 @@ 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.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cexpr", "clang-sys", "itertools 0.13.0", @@ -1575,7 +1577,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1617,9 +1619,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" dependencies = [ "serde_core", ] @@ -1673,7 +1675,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc119a5ad34c3f459062a96907f53358989b173d104258891bb74f95d93747e8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "boa_interner", "boa_macros", "boa_string", @@ -1690,7 +1692,7 @@ checksum = "e637ec52ea66d76b0ca86180c259d6c7bb6e6a6e14b2f36b85099306d8b00cc3" dependencies = [ "aligned-vec", "arrayvec", - "bitflags 2.10.0", + "bitflags 2.11.0", "boa_ast", "boa_gc", "boa_interner", @@ -1772,7 +1774,7 @@ dependencies = [ "cow-utils", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -1782,7 +1784,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02f99bf5b684f0de946378fcfe5f38c3a0fbd51cbf83a0f39ff773a0e218541f" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "boa_ast", "boa_interner", "boa_macros", @@ -1810,25 +1812,26 @@ dependencies = [ [[package]] name = "borsh" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1da5ab77c1437701eeff7c88d968729e7766172279eab0676857b3d63af7a6f" +checksum = "cfd1e3f8955a5d7de9fab72fc8373fade9fb8a703968cb200ae3dc6cf08e185a" dependencies = [ "borsh-derive", + "bytes", "cfg_aliases", ] [[package]] name = "borsh-derive" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0686c856aa6aac0c4498f936d7d6a02df690f614c03e4d906d1018062b5c5e2c" +checksum = "bfcfdc083699101d5a7965e49925975f2f55060f94f9a05e7187be95d530ca59" dependencies = [ "once_cell", "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1840,6 +1843,17 @@ dependencies = [ "debug-helper", ] +[[package]] +name = "brotli" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor 4.0.3", +] + [[package]] name = "brotli" version = "8.0.2" @@ -1848,7 +1862,17 @@ checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", - "brotli-decompressor", + "brotli-decompressor 5.0.0", +] + +[[package]] +name = "brotli-decompressor" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a334ef7c9e23abf0ce748e8cd309037da93e606ad52eb372e4ce327a0dcfbdfd" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", ] [[package]] @@ -1873,9 +1897,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byte-slice-cast" @@ -1900,7 +1924,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -1930,9 +1954,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "2.1.5" +version = "2.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e00bf4b112b07b505472dbefd19e37e53307e2bfed5a79e0cc161d58ccd0e687" +checksum = "6648ed1e4ea8e8a1a4a2c78e1cda29a3fd500bc622899c340d8525ea9a76b24a" dependencies = [ "arbitrary", "blst", @@ -2011,9 +2035,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.55" +version = "1.2.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", "jobserver", @@ -2050,9 +2074,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.43" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -2085,9 +2109,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.56" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75ca66430e33a14957acc24c5077b503e7d374151b2b4b3a10c83b4ceb4be0e" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", "clap_derive", @@ -2095,9 +2119,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.56" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793207c7fa6300a0608d1080b858e5fdbe713cdc1c8db9fb17777d8a13e63df0" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -2107,21 +2131,30 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "clap_lex" -version = "0.7.7" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror 2.0.18", +] [[package]] name = "coins-bip32" @@ -2176,9 +2209,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" @@ -2217,11 +2250,11 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.36" +version = "0.4.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a" +checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" dependencies = [ - "brotli", + "brotli 8.0.2", "compression-core", "flate2", "memchr", @@ -2246,9 +2279,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.17.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" +checksum = "531185e432bb31db1ecda541e9e7ab21468d4d844ad7505e0546a49b4945d49b" dependencies = [ "cfg-if", "cpufeatures", @@ -2297,16 +2330,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation" version = "0.10.1" @@ -2417,7 +2440,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "crossterm_winapi", "derive_more", "document-features", @@ -2500,7 +2523,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2544,7 +2567,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2559,7 +2582,7 @@ dependencies = [ "quote", "serde", "strsim", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2572,7 +2595,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2583,7 +2606,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2594,7 +2617,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2605,7 +2628,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2647,7 +2670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" dependencies = [ "data-encoding", - "syn 1.0.109", + "syn 2.0.117", ] [[package]] @@ -2679,9 +2702,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", "serde_core", @@ -2700,13 +2723,13 @@ dependencies = [ [[package]] name = "derive-where" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" +checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2717,7 +2740,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2738,7 +2761,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2748,7 +2771,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2770,7 +2793,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version 0.4.1", - "syn 2.0.114", + "syn 2.0.117", "unicode-xid", ] @@ -2845,9 +2868,9 @@ dependencies = [ [[package]] name = "discv5" -version = "0.10.2" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f170f4f6ed0e1df52bf43b403899f0081917ecf1500bfe312505cc3b515a8899" +checksum = "4c7999df38d0bd8f688212e1a4fae31fd2fea6d218649b9cd7c40bf3ec1318fc" dependencies = [ "aes", "aes-gcm", @@ -2863,13 +2886,12 @@ dependencies = [ "hkdf", "lazy_static", "libp2p-identity", - "lru 0.12.5", "more-asserts", "multiaddr", "parking_lot", "rand 0.8.5", "smallvec", - "socket2 0.5.10", + "socket2", "tokio", "tracing", "uint 0.10.0", @@ -2884,14 +2906,14 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "doctest-file" -version = "1.0.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aac81fa3e28d21450aa4d2ac065992ba96a1d7303efbce51a95f4fd175b67562" +checksum = "c2db04e74f0a9a93103b50e90b96024c9b2bdca8bce6a632ec71b88736d3d359" [[package]] name = "document-features" @@ -2931,7 +2953,7 @@ checksum = "1ec431cd708430d5029356535259c5d645d60edd3d39c54e5eea9782d46caa7d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -2983,7 +3005,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3015,6 +3037,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "enr" version = "0.13.0" @@ -3044,7 +3078,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3064,7 +3098,7 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3084,7 +3118,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3166,7 +3200,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3178,7 +3212,7 @@ dependencies = [ "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3309,7 +3343,7 @@ checksum = "6dc7a9cb3326bafb80642c5ce99b39a2c0702d4bfa8ee8a3e773791a6cbe2407" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -3320,9 +3354,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -3397,9 +3431,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -3412,9 +3446,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -3435,15 +3469,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -3452,9 +3486,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -3471,26 +3505,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-timer" @@ -3504,9 +3538,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -3516,7 +3550,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -3559,20 +3592,20 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "wasip2", "wasip3", ] @@ -3593,7 +3626,7 @@ version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "libc", "libgit2-sys", "log", @@ -3688,6 +3721,15 @@ version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -3705,9 +3747,6 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", -] [[package]] name = "hashbrown" @@ -3716,7 +3755,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", - "equivalent", "foldhash 0.1.5", ] @@ -3735,11 +3773,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.9.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.16.1", ] [[package]] @@ -3752,6 +3790,20 @@ dependencies = [ "num-traits", ] +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version 0.4.1", + "serde", + "spin", + "stable_deref_trait", +] + [[package]] name = "heck" version = "0.5.0" @@ -3957,7 +4009,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", ] [[package]] @@ -3991,14 +4043,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ "base64 0.22.1", "bytes", "futures-channel", - "futures-core", "futures-util", "http", "http-body", @@ -4007,7 +4058,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.2", + "socket2", "tokio", "tower-service", "tracing", @@ -4186,7 +4237,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4249,11 +4300,11 @@ dependencies = [ [[package]] name = "inotify" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" +checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "inotify-sys", "libc", ] @@ -4279,22 +4330,22 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" dependencies = [ "darling 0.23.0", "indoc", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "interprocess" -version = "2.2.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d941b405bd2322993887859a8ee6ac9134945a24ec5ec763a8a962fc64dfec2d" +checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69" dependencies = [ "doctest-file", "futures-core", @@ -4316,27 +4367,27 @@ dependencies = [ [[package]] name = "ipconfig" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +checksum = "2d72a21f6a71a6c4c3160e095e8925861f5119dd26ef71acee1b9146f74f76c8" dependencies = [ - "socket2 0.5.10", + "socket2", "widestring", - "windows-sys 0.48.0", + "windows-sys 0.61.2", "winreg", ] [[package]] name = "ipnet" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" +checksum = "d8e7418f59cc01c88316161279a7f665217ae316b388e58a0d10e29f54f1e5eb" dependencies = [ "memchr", "serde", @@ -4377,9 +4428,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jni" @@ -4390,7 +4441,7 @@ dependencies = [ "cesu8", "cfg-if", "combine", - "jni-sys", + "jni-sys 0.3.1", "log", "thiserror 1.0.69", "walkdir", @@ -4399,9 +4450,31 @@ dependencies = [ [[package]] name = "jni-sys" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] [[package]] name = "jobserver" @@ -4415,9 +4488,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", @@ -4527,7 +4600,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4627,9 +4700,9 @@ dependencies = [ [[package]] name = "kasuari" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" +checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" dependencies = [ "hashbrown 0.16.1", "portable-atomic", @@ -4689,9 +4762,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libgit2-sys" @@ -4753,13 +4826,14 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "libc", - "redox_syscall 0.7.0", + "plain", + "redox_syscall 0.7.3", ] [[package]] @@ -4780,9 +4854,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.23" +version = "1.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +checksum = "d52f4c29e2a68ac30c9087e1b772dc9f44a2b66ed44edf2266cf2be9b03dafc1" dependencies = [ "cc", "libc", @@ -4796,7 +4870,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -4817,9 +4891,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" @@ -4849,15 +4923,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "lru" -version = "0.12.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" -dependencies = [ - "hashbrown 0.15.5", -] - [[package]] name = "lru" version = "0.16.3" @@ -4907,6 +4972,12 @@ dependencies = [ "libc", ] +[[package]] +name = "mach2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" + [[package]] name = "macro-string" version = "0.1.4" @@ -4915,7 +4986,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4926,7 +4997,7 @@ checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4940,15 +5011,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memmap2" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" dependencies = [ "libc", ] @@ -4980,7 +5051,7 @@ checksum = "161ab904c2c62e7bda0f7562bf22f96440ca35ff79e66c800cbac298f2f4f5ec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -4999,13 +5070,13 @@ dependencies = [ [[package]] name = "metrics-process" -version = "2.4.2" +version = "2.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f615e08e049bd14a44c4425415782efb9bcd479fc1e19ddeb971509074c060d0" +checksum = "4268d87f64a752f5a651314fc683f04da10be65701ea3e721ba4d74f79163cac" dependencies = [ "libc", "libproc", - "mach2", + "mach2 0.6.0", "metrics", "once_cell", "procfs", @@ -5091,14 +5162,14 @@ checksum = "59b43b4fd69e3437618106f7754f34021b831a514f9e1a98ae863cabcd8d8dad" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "moka" -version = "0.12.13" +version = "0.12.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac832c50ced444ef6be0767a008b02c106a909ba79d1d830501e94b96f6b7e" +checksum = "957228ad12042ee839f93c8f257b62b4c0ab5eaae1d4fa60de53b27c9d7c5046" dependencies = [ "crossbeam-channel", "crossbeam-epoch", @@ -5160,17 +5231,17 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" dependencies = [ "libc", "log", "openssl", - "openssl-probe 0.1.6", + "openssl-probe", "openssl-sys", "schannel", - "security-framework 2.11.1", + "security-framework", "security-framework-sys", "tempfile", ] @@ -5191,7 +5262,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "fsevent-sys", "inotify", "kqueue", @@ -5209,14 +5280,14 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "ntapi" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +checksum = "c3b335231dfd352ffb0f8017f3b6027a4917f7df785ea2143d8af2adc66980ae" dependencies = [ "winapi", ] @@ -5323,9 +5394,9 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", "rustversion", @@ -5333,14 +5404,14 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5369,18 +5440,18 @@ dependencies = [ [[package]] name = "objc2-core-foundation" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "objc2-io-kit" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" dependencies = [ "libc", "objc2-core-foundation", @@ -5388,9 +5459,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" dependencies = [ "critical-section", "portable-atomic", @@ -5526,11 +5597,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.75" +version = "0.10.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "cfg-if", "foreign-types", "libc", @@ -5547,15 +5618,9 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - [[package]] name = "openssl-probe" version = "0.2.1" @@ -5564,9 +5629,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" dependencies = [ "cc", "libc", @@ -5597,7 +5662,7 @@ dependencies = [ "opentelemetry", "tracing", "tracing-core", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -5615,9 +5680,9 @@ dependencies = [ [[package]] name = "opentelemetry-otlp" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2366db2dca4d2ad033cad11e6ee42844fd727007af5ad04a1730f4cb8163bf" +checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" dependencies = [ "http", "opentelemetry", @@ -5721,7 +5786,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5787,9 +5852,9 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.5" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9eb05c21a464ea704b53158d358a31e6425db2f63a1a7312268b05fe2b75f7" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", "ucd-trie", @@ -5836,7 +5901,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5850,29 +5915,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pin-utils" @@ -5896,6 +5961,12 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "plain_hasher" version = "0.2.3" @@ -5923,6 +5994,19 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "heapless", + "serde", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -5964,7 +6048,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -5989,9 +6073,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit", ] @@ -6015,7 +6099,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6033,7 +6117,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "chrono", "flate2", "procfs-core", @@ -6046,20 +6130,20 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "chrono", "hex", ] [[package]] name = "proptest" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee689443a2bd0a16ab0348b52ee43e3b2d1b1f931c8aa5c9f8de4c86fbe8c40" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.10.0", + "bitflags 2.11.0", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -6088,7 +6172,7 @@ checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6111,7 +6195,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6157,7 +6241,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.6.2", + "socket2", "thiserror 2.0.18", "tokio", "tracing", @@ -6166,9 +6250,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ "bytes", "getrandom 0.3.4", @@ -6194,16 +6278,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.2", + "socket2", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -6214,6 +6298,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "radium" version = "0.7.0" @@ -6302,9 +6392,9 @@ dependencies = [ [[package]] name = "rapidhash" -version = "4.2.1" +version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8b5b858a440a0bc02625b62dd95131b9201aa9f69f411195dd4a7cfb1de3d7" +checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" dependencies = [ "rand 0.9.2", "rustversion", @@ -6328,13 +6418,13 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "compact_str", "hashbrown 0.16.1", "indoc", "itertools 0.14.0", "kasuari", - "lru 0.16.3", + "lru", "strum", "thiserror 2.0.18", "unicode-segmentation", @@ -6360,7 +6450,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "hashbrown 0.16.1", "indoc", "instability", @@ -6379,7 +6469,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -6414,16 +6504,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] name = "redox_syscall" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", ] [[package]] @@ -6465,14 +6555,14 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -6482,9 +6572,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -6493,9 +6583,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "regress" @@ -6550,7 +6640,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", ] [[package]] @@ -6824,7 +6914,7 @@ source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.2#793a3d5fb3e3413e9e dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -6888,7 +6978,7 @@ dependencies = [ "reth-node-api", "reth-primitives-traits", "reth-tracing", - "ringbuffer", + "ringbuffer 0.16.0", "serde", "serde_json", "tokio", @@ -7778,7 +7868,7 @@ name = "reth-libmdbx" version = "1.11.2" source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.2#793a3d5fb3e3413e9ecc9b024a5e26672e61e7c3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "byteorder", "dashmap", "derive_more", @@ -7973,7 +8063,7 @@ version = "1.11.2" source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.2#793a3d5fb3e3413e9ecc9b024a5e26672e61e7c3" dependencies = [ "anyhow", - "bincode 1.3.3", + "bincode", "derive_more", "lz4_flex", "memmap2", @@ -8800,7 +8890,7 @@ dependencies = [ "alloy-consensus", "alloy-eips", "alloy-primitives", - "bincode 1.3.3", + "bincode", "eyre", "futures-util", "itertools 0.14.0", @@ -9017,7 +9107,7 @@ dependencies = [ "tracing-journald", "tracing-logfmt", "tracing-samply", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -9034,7 +9124,7 @@ dependencies = [ "opentelemetry_sdk", "tracing", "tracing-opentelemetry", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", "url", ] @@ -9049,7 +9139,7 @@ dependencies = [ "alloy-rlp", "aquamarine", "auto_impl", - "bitflags 2.10.0", + "bitflags 2.11.0", "futures-util", "metrics", "parking_lot", @@ -9338,9 +9428,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.34.2" +version = "0.34.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e435414e9de50a1b930da602067c76365fea2fea11e80ceb50783c94ddd127f" +checksum = "e341d9777b1903a8428bc6f8fe7a8f80671d2249837c9749566e61328ef14125" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -9371,9 +9461,9 @@ dependencies = [ [[package]] name = "revm-precompile" -version = "32.0.0" +version = "32.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c1285c848d240678bf69cb0f6179ff5a4aee6fc8e921d89708087197a0aff3" +checksum = "e2ec11f45deec71e4945e1809736bb20d454285f9167ab53c5159dae1deb603f" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -9395,9 +9485,9 @@ dependencies = [ [[package]] name = "revm-primitives" -version = "22.0.0" +version = "22.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba580c56a8ec824a64f8a1683577876c2e1dbe5247044199e9b881421ad5dcf9" +checksum = "4bcfb5ce6cf18b118932bcdb7da05cd9c250f2cb9f64131396b55f3fe3537c35" dependencies = [ "alloy-primitives", "num_enum", @@ -9412,7 +9502,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "311720d4f0f239b041375e7ddafdbd20032a33b7bae718562ea188e188ed9fd3" dependencies = [ "alloy-eip7928", - "bitflags 2.10.0", + "bitflags 2.11.0", "revm-bytecode", "revm-primitives", "serde", @@ -9442,6 +9532,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ringbuffer" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df6368f71f205ff9c33c076d170dd56ebf68e8161c733c0caa07a7a5509ed53" + [[package]] name = "ringbuffer" version = "0.16.0" @@ -9459,9 +9555,9 @@ dependencies = [ [[package]] name = "rlimit" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7043b63bd0cd1aaa628e476b80e6d4023a3b50eb32789f2728908107bd0c793a" +checksum = "f35ee2729c56bb610f6dba436bf78135f728b7373bdffae2ec815b2d3eb98cc3" dependencies = [ "libc", ] @@ -9609,11 +9705,11 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "errno", "libc", "linux-raw-sys", @@ -9622,9 +9718,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.36" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ "log", "once_cell", @@ -9641,10 +9737,10 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe 0.2.1", + "openssl-probe", "rustls-pki-types", "schannel", - "security-framework 3.5.1", + "security-framework", ] [[package]] @@ -9663,7 +9759,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ - "core-foundation 0.10.1", + "core-foundation", "core-foundation-sys", "jni", "log", @@ -9672,7 +9768,7 @@ dependencies = [ "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", - "security-framework 3.5.1", + "security-framework", "security-framework-sys", "webpki-root-certs 0.26.11", "windows-sys 0.59.0", @@ -9715,9 +9811,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "ryu-js" @@ -9736,9 +9832,9 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" dependencies = [ "windows-sys 0.61.2", ] @@ -9757,9 +9853,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e910108742c57a770f492731f99be216a52fadd361b06c8fb59d74ccc267d2" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" dependencies = [ "dyn-clone", "ref-cast", @@ -9842,25 +9938,12 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.11.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" -dependencies = [ - "bitflags 2.10.0", - "core-foundation 0.10.1", + "bitflags 2.11.0", + "core-foundation", "core-foundation-sys", "libc", "security-framework-sys", @@ -9868,9 +9951,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.15.0" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ "core-foundation-sys", "libc", @@ -9967,7 +10050,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -9986,9 +10069,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" dependencies = [ "serde_core", ] @@ -10007,9 +10090,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.16.1" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" dependencies = [ "base64 0.22.1", "chrono", @@ -10017,7 +10100,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.13.0", "schemars 0.9.0", - "schemars 1.2.0", + "schemars 1.2.1", "serde_core", "serde_json", "serde_with_macros", @@ -10026,14 +10109,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.16.1" +version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" dependencies = [ - "darling 0.21.3", + "darling 0.23.0", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10099,9 +10182,9 @@ dependencies = [ [[package]] name = "shellexpand" -version = "3.1.1" +version = "3.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1fdf65dd6331831494dd616b30351c38e96e45921a27745cf98490458b90bb" +checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8" dependencies = [ "dirs", ] @@ -10161,9 +10244,9 @@ checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simple_asn1" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" dependencies = [ "num-bigint", "num-traits", @@ -10179,9 +10262,9 @@ checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" [[package]] name = "sketches-ddsketch" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e9a774a6c28142ac54bb25d25562e6bcf957493a184f15ad4eebccb23e410a" +checksum = "0c6f73aeb92d671e0cc4dca167e59b2deb6387c375391bc99ee743f326994a2b" [[package]] name = "slab" @@ -10216,22 +10299,12 @@ checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" [[package]] name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - -[[package]] -name = "socket2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -10250,6 +10323,15 @@ dependencies = [ "sha1", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -10296,7 +10378,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10318,9 +10400,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -10336,7 +10418,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10356,14 +10438,14 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "sysinfo" -version = "0.38.3" +version = "0.38.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d03c61d2a49c649a15c407338afe7accafde9dac869995dccb73e5f7ef7d9034" +checksum = "92ab6a2f8bfe508deb3c6406578252e491d299cbbf3bc0529ecc3313aee4a52f" dependencies = [ "libc", "memchr", @@ -10404,12 +10486,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.24.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.4", + "getrandom 0.4.2", "once_cell", "rustix", "windows-sys 0.59.0", @@ -10417,9 +10499,9 @@ dependencies = [ [[package]] name = "test-fuzz" -version = "7.2.5" +version = "7.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11e5c77910b1d5b469a342be541cf44933f0ad2c4b8d5acb32ee46697fd60546" +checksum = "bdcc9e8244ec7140f52b55bb67ff77fe5e10f1e7651a9d7ca1187555344a212d" dependencies = [ "serde", "serde_combinators", @@ -10430,35 +10512,35 @@ dependencies = [ [[package]] name = "test-fuzz-internal" -version = "7.2.5" +version = "7.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25f2f0ee315b130411a98570dd128dfe344bfaa0a28bf33d38f4a1fe85f39b" +checksum = "b36ec8e4151160eb1b1d42d4691c24b5a6f3891c75d41afb343346801ab60ce5" dependencies = [ - "bincode 2.0.1", "cargo_metadata 0.19.2", + "postcard", "serde", ] [[package]] name = "test-fuzz-macro" -version = "7.2.5" +version = "7.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8c03ba0a9e3e4032f94d71c85e149af147843c6f212e4ca4383542d606b04a6" +checksum = "bd45ef045619b976f1efa036aee72b6a80dc359d800e11f9d636332ebf6655f9" dependencies = [ - "darling 0.21.3", + "darling 0.23.0", "heck", "itertools 0.14.0", "prettyplease", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "test-fuzz-runtime" -version = "7.2.5" +version = "7.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a4ac481aa983d386e857a7be0006c2f0ef26e0c5326bbc7262f73c2891b91d" +checksum = "91e243f304ded602493cfd77bee2f627f376bec4ea185cf8e9674b6150ea3837" dependencies = [ "hex", "num-traits", @@ -10499,7 +10581,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10510,7 +10592,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10609,9 +10691,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -10624,9 +10706,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.49.0" +version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ "bytes", "libc", @@ -10634,20 +10716,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.2", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10691,6 +10773,7 @@ dependencies = [ "futures-util", "log", "rustls", + "rustls-native-certs", "rustls-pki-types", "tokio", "tokio-rustls", @@ -10727,17 +10810,17 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.11+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap 2.13.0", "serde_core", "serde_spanned", - "toml_datetime", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -10749,38 +10832,47 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.8+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" dependencies = [ "indexmap 2.13.0", - "toml_datetime", + "toml_datetime 1.1.0+spec-1.1.0", "toml_parser", - "winnow", + "winnow 1.0.0", ] [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" dependencies = [ - "winnow", + "winnow 1.0.0", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" [[package]] name = "tonic" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a286e33f82f8a1ee2df63f4fa35c0becf4a85a0cb03091a15fd7bf0b402dc94a" +checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" dependencies = [ "async-trait", "base64 0.22.1", @@ -10804,9 +10896,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c55a2d6a14174563de34409c9f92ff981d006f56da9c6ecd40d9d4a31500b0" +checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" dependencies = [ "bytes", "prost", @@ -10841,7 +10933,7 @@ checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", "base64 0.22.1", - "bitflags 2.10.0", + "bitflags 2.11.0", "bytes", "futures-core", "futures-util", @@ -10897,7 +10989,7 @@ dependencies = [ "crossbeam-channel", "thiserror 2.0.18", "time", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -10908,7 +11000,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -10939,7 +11031,7 @@ checksum = "2d3a81ed245bfb62592b1e2bc153e77656d94ee6a0497683a65a12ccaf2438d0" dependencies = [ "libc", "tracing-core", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -10962,7 +11054,7 @@ dependencies = [ "time", "tracing", "tracing-core", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -10977,7 +11069,7 @@ dependencies = [ "tracing", "tracing-core", "tracing-log", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", "web-time", ] @@ -10990,11 +11082,11 @@ dependencies = [ "cfg-if", "itoa", "libc", - "mach2", + "mach2 0.5.0", "memmap2", "smallvec", "tracing-core", - "tracing-subscriber 0.3.22", + "tracing-subscriber 0.3.23", ] [[package]] @@ -11018,9 +11110,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -11057,7 +11149,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -11168,9 +11260,9 @@ checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" @@ -11191,9 +11283,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -11223,12 +11315,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.8" @@ -11268,11 +11354,11 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.20.0" +version = "1.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.2", "js-sys", "wasm-bindgen", ] @@ -11336,12 +11422,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" @@ -11350,7 +11430,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -11407,9 +11487,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", @@ -11420,9 +11500,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.58" +version = "0.4.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a6e77fd0ae8029c9ea0063f87c46fde723e7d887703d74ad2616d792e51e6f" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" dependencies = [ "cfg-if", "futures-util", @@ -11434,9 +11514,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -11444,22 +11524,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] @@ -11505,7 +11585,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.0", "hashbrown 0.15.5", "indexmap 2.13.0", "semver 1.0.27", @@ -11527,9 +11607,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.85" +version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" dependencies = [ "js-sys", "wasm-bindgen", @@ -11551,14 +11631,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.5", + "webpki-root-certs 1.0.6", ] [[package]] name = "webpki-root-certs" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a29fc0408b113f68cf32637857ab740edfafdf460c326cd2afaa2d84cc05dc" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ "rustls-pki-types", ] @@ -11569,14 +11649,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.5", + "webpki-roots 1.0.6", ] [[package]] name = "webpki-roots" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" dependencies = [ "rustls-pki-types", ] @@ -11609,7 +11689,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -11671,7 +11751,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -11682,7 +11762,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -11728,15 +11808,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -11788,21 +11859,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -11851,12 +11907,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -11875,12 +11925,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -11899,12 +11943,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -11935,12 +11973,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -11959,12 +11991,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -11983,12 +12009,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -12007,12 +12027,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -12027,21 +12041,30 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" dependencies = [ "memchr", ] [[package]] name = "winreg" -version = "0.50.0" +version = "0.55.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" dependencies = [ "cfg-if", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -12074,7 +12097,7 @@ dependencies = [ "heck", "indexmap 2.13.0", "prettyplease", - "syn 2.0.114", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -12090,7 +12113,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -12102,7 +12125,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.10.0", + "bitflags 2.11.0", "indexmap 2.13.0", "log", "serde", @@ -12213,28 +12236,28 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] [[package]] name = "zerocopy" -version = "0.8.37" +version = "0.8.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7456cf00f0685ad319c5b1693f291a650eaf345e941d082fc4e03df8a03996ac" +checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.37" +version = "0.8.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0" +checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -12254,7 +12277,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", "synstructure", ] @@ -12275,7 +12298,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] @@ -12309,14 +12332,14 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.114", + "syn 2.0.117", ] [[package]] name = "zmij" -version = "1.0.18" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1966f8ac2c1f76987d69a74d0e0f929241c10e78136434e3be70ff7f58f64214" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zstd" diff --git a/Cargo.toml b/Cargo.toml index e5c4fe70..a76a373b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bera-reth" -version = "1.3.2" +version = "1.3.3" edition = "2024" license = "MIT OR Apache-2.0" @@ -13,15 +13,18 @@ alloy-network = "1.6.3" alloy-primitives = "1.5.6" alloy-rlp = "0.3.13" alloy-rpc-types = "1.6.3" +alloy-rpc-types-engine = "1.6.3" alloy-rpc-types-eth = "1.6.3" alloy-serde = "1.6.3" alloy-signer-local = "1.6.3" alloy-sol-macro = "1.5.6" alloy-sol-types = "1.5.6" +blst = "0.3" bytes = "1.10.1" clap = { version = "4.5.40", features = ["derive"] } derive_more = "2.0.1" eyre = "0.6.12" +hex = "0.4" sha2 = "0.10" # rpc @@ -30,12 +33,15 @@ jsonrpsee-core = { version = "0.26.0", features = ["server"] } jsonrpsee-proc-macros = "0.26.0" async-trait = "0.1.88" +brotli = "7" +futures-util = "0.3" +metrics = "0.24" modular-bitfield = "0.13.1" reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } +reth-chain-state = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } -reth-cli-commands = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-cli-util = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-codecs = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-consensus-common = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } @@ -50,6 +56,8 @@ reth-ethereum-payload-builder = { git = "https://github.com/paradigmxyz/reth", t reth-ethereum-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-evm-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } +reth-execution-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } +reth-metrics = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-network-peers = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-node-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } @@ -58,17 +66,24 @@ reth-node-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11 reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-payload-validator = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } +reth-revm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-rpc-convert = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-rpc-engine-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-rpc-eth-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-storage-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } +reth-tasks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.11.2" } +ringbuffer = "0.15" serde = { version = "1.0", features = ["derive"], default-features = false } +serde_json = "1.0" thiserror = "2.0" -tokio = "1.46.0" +tokio = { version = "1.46.0", features = ["sync", "net", "macros"] } +tokio-tungstenite = { version = "0.26", features = ["rustls-tls-native-roots"] } +tokio-util = "0.7" tracing = "0.1.41" +url = "2" [dev-dependencies] alloy-provider = "1.6.3" @@ -111,50 +126,3 @@ lto = "thin" [package.metadata.cargo-machete] ignored = ["modular-bitfield"] - -# [patch."https://github.com/paradigmxyz/reth"] -# reth = { path = "../reth/bin/reth" } -# reth-rpc = { path = "../reth/crates/rpc/rpc" } -# reth-basic-payload-builder = { path = "../reth/crates/payload/basic" } -# reth-chainspec = { path = "../reth/crates/chainspec" } -# reth-cli = { path = "../reth/crates/cli/cli" } -# reth-cli-commands = { path = "../reth/crates/cli/commands" } -# reth-cli-util = { path = "../reth/crates/cli/util" } -# reth-db = { path = "../reth/crates/storage/db" } -# reth-engine-local = { path = "../reth/crates/engine/local" } -# reth-engine-primitives = { path = "../reth/crates/engine/primitives" } -# reth-ethereum-cli = { path = "../reth/crates/ethereum/cli" } -# reth-engine-tree = { path = "../reth/crates/engine/tree" } -# reth-ethereum-engine-primitives = { path = "../reth/crates/ethereum/engine-primitives" } -# reth-ethereum-payload-builder = { path = "../reth/crates/ethereum/payload" } -# reth-ethereum-primitives = { path = "../reth/crates/ethereum/primitives" } -# reth-evm = { path = "../reth/crates/evm/evm" } -# reth-evm-ethereum = { path = "../reth/crates/ethereum/evm" } -# reth-network-peers = { path = "../reth/crates/net/peers" } -# reth-node-api = { path = "../reth/crates/node/api" } -# reth-node-builder = { path = "../reth/crates/node/builder" } -# reth-node-core = { path = "../reth/crates/node/core" } -# reth-payload-validator = { path = "../reth/crates/payload/validator" } -# reth-node-ethereum = { path = "../reth/crates/ethereum/node" } -# reth-payload-primitives = { path = "../reth/crates/payload/primitives" } -# reth-primitives-traits = { path = "../reth/crates/primitives-traits" } -# reth-tracing = { path = "../reth/crates/tracing" } -# reth-codecs = { path = "../reth/crates/storage/codecs" } -# reth-db-api = { path = "../reth/crates/storage/db-api" } -# reth-rpc-engine-api = { path = "../reth/crates/rpc/rpc-engine-api" } -# reth-rpc-eth-api = { path = "../reth/crates/rpc/rpc-eth-api" } -# reth-rpc-eth-types = { path = "../reth/crates/rpc/rpc-eth-types" } -# reth-rpc-convert = { path = "../reth/crates/rpc/rpc-convert" } -# reth-transaction-pool = { path = "../reth/crates/transaction-pool" } -# reth-zstd-compressors = { path = "../reth/crates/storage/zstd-compressors" } -# reth-trie-common = { path = "../reth/crates/trie/common" } -# reth-trie-db = { path = "../reth/crates/trie/db" } -# reth-trie = { path = "../reth/crates/trie/trie" } -# reth-stages-types = { path = "../reth/crates/stages/types" } -# reth-execution-types = { path = "../reth/crates/evm/execution-types" } -# reth-execution-errors = { path = "../reth/crates/evm/execution-errors" } -# reth-storage-api = { path = "../reth/crates/storage/storage-api" } -# reth-provider = { path = "../reth/crates/storage/provider" } -# reth-storage-errors = { path = "../reth/crates/storage/errors" } -# reth-e2e-test-utils = { path = "../reth/crates/e2e-test-utils" } -# reth-rpc-builder = { path = "../reth/crates/rpc/rpc-builder" } diff --git a/deny.toml b/deny.toml index f1d3fcfd..f00d61c7 100644 --- a/deny.toml +++ b/deny.toml @@ -73,6 +73,8 @@ ignore = [ "RUSTSEC-2025-0141", # https://rustsec.org/advisories/RUSTSEC-2026-0002 lru 0.12.5 unsound IterMut, pinned by discv5 "RUSTSEC-2026-0002", + # https://rustsec.org/advisories/RUSTSEC-2023-0089 atomic-polyfill unmaintained, pinned by test-fuzz + "RUSTSEC-2023-0089", ] # If this is true, then cargo deny will use the git executable to fetch advisory database. # If this is false, then it uses a built-in git library. diff --git a/src/engine/payload.rs b/src/engine/payload.rs index 8f4e7a45..4618c1cd 100644 --- a/src/engine/payload.rs +++ b/src/engine/payload.rs @@ -1,5 +1,6 @@ use crate::{ chainspec::BerachainChainSpec, + node::evm::config::BERACHAIN_BLOCK_TIME_SECONDS, primitives::{BerachainBlock, BerachainHeader, BerachainPrimitives, header::BlsPublicKey}, }; use alloy_consensus::BlockHeader; @@ -142,13 +143,7 @@ impl PayloadAttributesBuilder for LocalPayloadAttributesBuilder { fn build(&self, parent: &SealedHeader) -> BerachainPayloadAttributes { - let mut timestamp = - std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs(); - - if self.enforce_increasing_timestamp { - timestamp = std::cmp::max(parent.timestamp().saturating_add(1), timestamp); - } - + let timestamp = parent.timestamp() + BERACHAIN_BLOCK_TIME_SECONDS; BerachainPayloadAttributes { inner: EthPayloadAttributes { timestamp, diff --git a/src/evm/mod.rs b/src/evm/mod.rs index 34e441d9..3c949946 100644 --- a/src/evm/mod.rs +++ b/src/evm/mod.rs @@ -189,9 +189,9 @@ where type Error = EVMError; type HaltReason = HaltReason; type Spec = SpecId; + type BlockEnv = BlockEnv; type Precompiles = PRECOMPILE; type Inspector = I; - type BlockEnv = BlockEnv; fn block(&self) -> &BlockEnv { &self.block @@ -280,8 +280,8 @@ impl EvmFactory for BerachainEvmFactory { type Error = EVMError; type HaltReason = HaltReason; type Spec = SpecId; - type Precompiles = PrecompilesMap; type BlockEnv = BlockEnv; + type Precompiles = PrecompilesMap; fn create_evm(&self, db: DB, input: EvmEnv) -> Self::Evm { BerachainEvmBuilder::new(db, input).build() @@ -406,13 +406,13 @@ mod tests { if let Ok(result) = &result_with_tracer && let ExecutionResult::Success { gas_used, .. } = &result.result { - assert_eq!(*gas_used, 0); + assert_eq!(gas_used, &0); } if let Ok(result) = &result_without_tracer && let ExecutionResult::Success { gas_used, .. } = &result.result { - assert_eq!(*gas_used, 0); + assert_eq!(gas_used, &0); } // Verify tracer captured system call details diff --git a/src/flashblocks/cache.rs b/src/flashblocks/cache.rs new file mode 100644 index 00000000..31ef1d3d --- /dev/null +++ b/src/flashblocks/cache.rs @@ -0,0 +1,215 @@ +use crate::flashblocks::{ + FlashBlockCompleteSequence, + payload::PendingFlashBlock, + sequence::{FlashBlockPendingSequence, SequenceExecutionOutcome}, + traits::{FlashblockDiff, FlashblockPayload, FlashblockPayloadBase}, + worker::BuildArgs, +}; +use alloy_eips::eip2718::WithEncoded; +use alloy_primitives::B256; +use reth_primitives_traits::{NodePrimitives, Recovered}; +use reth_revm::cached::CachedReads; +use ringbuffer::{AllocRingBuffer, RingBuffer}; +use tokio::sync::broadcast; +use tracing::*; + +type CachedSequenceEntry

= ( + FlashBlockCompleteSequence

, + Vec::SignedTx>>>, +); + +type SequenceBuildArgs

= BuildArgs< + Vec::SignedTx>>>, +

::Base, +>; + +const CACHE_SIZE: usize = 3; +pub(crate) const FLASHBLOCK_BLOCK_TIME: u64 = 200; + +#[derive(Debug)] +pub(crate) struct SequenceManager { + pending: FlashBlockPendingSequence

, + pending_transactions: Vec>>, + completed_cache: AllocRingBuffer>, + block_broadcaster: broadcast::Sender>, + compute_state_root: bool, +} + +impl SequenceManager

{ + pub(crate) fn new(compute_state_root: bool) -> Self { + let (block_broadcaster, _) = broadcast::channel(128); + Self { + pending: FlashBlockPendingSequence::new(), + pending_transactions: Vec::new(), + completed_cache: AllocRingBuffer::new(CACHE_SIZE), + block_broadcaster, + compute_state_root, + } + } + + pub(crate) const fn block_sequence_broadcaster( + &self, + ) -> &broadcast::Sender> { + &self.block_broadcaster + } + + pub(crate) fn subscribe_block_sequence( + &self, + ) -> broadcast::Receiver> { + self.block_broadcaster.subscribe() + } + + pub(crate) fn insert_flashblock(&mut self, flashblock: P) -> eyre::Result<()> { + if flashblock.index() == 0 && self.pending.count() > 0 { + let completed = self.pending.finalize()?; + let block_number = completed.block_number(); + let parent_hash = completed.payload_base().parent_hash(); + + trace!( + target: "flashblocks", + block_number, + %parent_hash, + cache_size = self.completed_cache.len(), + "Caching completed flashblock sequence" + ); + + if self.block_broadcaster.receiver_count() > 0 { + let _ = self.block_broadcaster.send(completed.clone()); + } + + let txs = std::mem::take(&mut self.pending_transactions); + self.completed_cache.push((completed, txs)); + + let _ = self.pending.take_cached_reads(); + } + + self.pending_transactions + .extend(flashblock.recover_transactions().collect::, _>>()?); + self.pending.insert(flashblock); + Ok(()) + } + + pub(crate) const fn pending(&self) -> &FlashBlockPendingSequence

{ + &self.pending + } + + pub(crate) fn next_buildable_args( + &mut self, + local_tip_hash: B256, + local_tip_timestamp: u64, + ) -> Option> { + let ( + base, + last_index, + last_hash, + last_state_root_zero, + transactions, + cached_state, + source_name, + ) = if let Some(base) = + self.pending.payload_base().cloned().filter(|b| b.parent_hash() == local_tip_hash) + { + let cached_state = self.pending.take_cached_reads().map(|r| (base.parent_hash(), r)); + let last_fb = self.pending.last_flashblock()?; + let transactions = self.pending_transactions.clone(); + ( + base, + last_fb.index(), + last_fb.diff().block_hash(), + last_fb.diff().state_root().is_zero(), + transactions, + cached_state, + "pending", + ) + } else if let Some((cached, txs)) = self + .completed_cache + .iter() + .find(|(c, _)| c.payload_base().parent_hash() == local_tip_hash) + { + let base = cached.payload_base().clone(); + let last_fb = cached.last(); + let transactions = txs.clone(); + ( + base, + last_fb.index(), + last_fb.diff().block_hash(), + last_fb.diff().state_root().is_zero(), + transactions, + None, + "cached", + ) + } else { + return None; + }; + + let block_time_ms = base.timestamp().saturating_sub(local_tip_timestamp) * 1000; + let expected_final_flashblock = block_time_ms / FLASHBLOCK_BLOCK_TIME; + let compute_state_root = self.compute_state_root && + last_state_root_zero && + last_index >= expected_final_flashblock.saturating_sub(1); + + trace!( + target: "flashblocks", + block_number = base.block_number(), + source = source_name, + flashblock_index = last_index, + expected_final_flashblock, + compute_state_root_enabled = self.compute_state_root, + state_root_is_zero = last_state_root_zero, + will_compute_state_root = compute_state_root, + "Building from flashblock sequence" + ); + + Some(BuildArgs { + base, + transactions, + cached_state, + last_flashblock_index: last_index, + last_flashblock_hash: last_hash, + compute_state_root, + }) + } + + pub(crate) fn on_build_complete( + &mut self, + parent_hash: B256, + result: Option<(PendingFlashBlock, CachedReads)>, + ) { + let Some((computed_block, cached_reads)) = result else { + return; + }; + + let execution_outcome = computed_block.computed_state_root().map(|state_root| { + SequenceExecutionOutcome { block_hash: computed_block.block().hash(), state_root } + }); + + if self.pending.payload_base().is_some_and(|base| base.parent_hash() == parent_hash) { + self.pending.set_execution_outcome(execution_outcome); + self.pending.set_cached_reads(cached_reads); + trace!( + target: "flashblocks", + block_number = self.pending.block_number(), + has_computed_state_root = execution_outcome.is_some(), + "Updated pending sequence with build results" + ); + } else if let Some((cached, _)) = self + .completed_cache + .iter_mut() + .find(|(c, _)| c.payload_base().parent_hash() == parent_hash) + { + let needs_rebroadcast = + execution_outcome.is_some() && cached.execution_outcome().is_none(); + + cached.set_execution_outcome(execution_outcome); + + if needs_rebroadcast && self.block_broadcaster.receiver_count() > 0 { + trace!( + target: "flashblocks", + block_number = cached.block_number(), + "Re-broadcasting sequence with computed state_root" + ); + let _ = self.block_broadcaster.send(cached.clone()); + } + } + } +} diff --git a/src/flashblocks/mod.rs b/src/flashblocks/mod.rs new file mode 100644 index 00000000..2013c6ee --- /dev/null +++ b/src/flashblocks/mod.rs @@ -0,0 +1,275 @@ +#[cfg(test)] +pub mod test_utils; + +pub mod traits; +pub use traits::{FlashblockDiff, FlashblockPayload, FlashblockPayloadBase}; + +pub mod payload; +pub use payload::PendingFlashBlock; + +pub mod sequence; +pub use sequence::{ + FlashBlockCompleteSequence, FlashBlockPendingSequence, SequenceExecutionOutcome, +}; + +pub mod service; +pub use service::{FlashBlockBuildInfo, FlashBlockService}; + +mod cache; +mod worker; + +pub mod ws; +pub use ws::{FlashBlockDecoder, WsConnect, WsFlashBlockStream}; + +pub type PendingBlockRx = tokio::sync::watch::Receiver>>; + +pub type FlashBlockCompleteSequenceRx

= + tokio::sync::broadcast::Receiver>; + +pub type InProgressFlashBlockRx = tokio::sync::watch::Receiver>; + +use reth_primitives_traits::NodePrimitives; +use std::sync::Arc; + +#[derive(Debug)] +pub struct FlashblocksListeners { + pub pending_block_rx: PendingBlockRx, + pub flashblocks_sequence: tokio::sync::broadcast::Sender>, + pub in_progress_rx: InProgressFlashBlockRx, + pub received_flashblocks: tokio::sync::broadcast::Sender>, +} + +impl FlashblocksListeners { + pub const fn new( + pending_block_rx: PendingBlockRx, + flashblocks_sequence: tokio::sync::broadcast::Sender>, + in_progress_rx: InProgressFlashBlockRx, + received_flashblocks: tokio::sync::broadcast::Sender>, + ) -> Self { + Self { pending_block_rx, flashblocks_sequence, in_progress_rx, received_flashblocks } + } +} + +use crate::{ + primitives::header::BlsPublicKey, sequencer::signing::BlsSignature, + transaction::BerachainTxEnvelope, +}; +use alloy_consensus::{crypto::RecoveryError, transaction::Recovered}; +use alloy_eips::{ + eip2718::WithEncoded, + eip4895::{Withdrawal, Withdrawals}, +}; +use alloy_primitives::{Address, B256, Bloom, Bytes, U256}; +use reth::rpc::types::engine::PayloadId; + +#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct BerachainFlashblockPayloadBase { + pub parent_beacon_block_root: B256, + pub parent_hash: B256, + pub fee_recipient: Address, + pub prev_randao: B256, + #[serde(with = "alloy_serde::quantity")] + pub block_number: u64, + #[serde(with = "alloy_serde::quantity")] + pub gas_limit: u64, + #[serde(with = "alloy_serde::quantity")] + pub timestamp: u64, + pub extra_data: Bytes, + pub base_fee_per_gas: U256, + pub prev_proposer_pubkey: Option, +} + +impl FlashblockPayloadBase for BerachainFlashblockPayloadBase { + fn parent_hash(&self) -> B256 { + self.parent_hash + } + + fn block_number(&self) -> u64 { + self.block_number + } + + fn timestamp(&self) -> u64 { + self.timestamp + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct BerachainFlashblockPayloadDiff { + pub transactions: Vec, + pub withdrawals: Vec, +} + +impl FlashblockDiff for BerachainFlashblockPayloadDiff { + fn block_hash(&self) -> B256 { + B256::ZERO + } + + fn state_root(&self) -> B256 { + B256::ZERO + } + + fn gas_used(&self) -> u64 { + 0 + } + + fn logs_bloom(&self) -> &Bloom { + &Bloom::ZERO + } + + fn receipts_root(&self) -> B256 { + B256::ZERO + } + + fn transactions_raw(&self) -> &[Bytes] { + &self.transactions + } + + fn withdrawals(&self) -> Option<&Withdrawals> { + None + } + + fn withdrawals_root(&self) -> Option { + None + } +} + +#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct BerachainFlashblockPayloadMetadata { + pub block_number: u64, +} + +#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub struct BerachainFlashblockPayload { + pub payload_id: PayloadId, + pub index: u64, + pub base: Option, + pub diff: BerachainFlashblockPayloadDiff, + pub metadata: BerachainFlashblockPayloadMetadata, + #[serde(with = "signature_serde")] + pub signature: BlsSignature, + pub is_last: bool, +} + +mod signature_serde { + use super::BlsSignature; + use serde::{Deserialize, Deserializer, Serializer}; + + pub fn serialize(sig: &BlsSignature, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("0x{}", hex::encode(sig))) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let hex_str = String::deserialize(deserializer)?; + let hex_str = hex_str.trim_start_matches("0x"); + let bytes = hex::decode(hex_str).map_err(serde::de::Error::custom)?; + if bytes.len() != 96 { + return Err(serde::de::Error::custom(format!("expected 96 bytes, got {}", bytes.len()))); + } + let mut arr = [0u8; 96]; + arr.copy_from_slice(&bytes); + Ok(arr) + } +} + +impl BerachainFlashblockPayload { + pub const fn block_number(&self) -> u64 { + self.metadata.block_number + } +} + +impl FlashblockPayload for BerachainFlashblockPayload { + type Base = BerachainFlashblockPayloadBase; + type Diff = BerachainFlashblockPayloadDiff; + type SignedTx = BerachainTxEnvelope; + + fn index(&self) -> u64 { + self.index + } + + fn payload_id(&self) -> PayloadId { + self.payload_id + } + + fn base(&self) -> Option<&Self::Base> { + self.base.as_ref() + } + + fn diff(&self) -> &Self::Diff { + &self.diff + } + + fn block_number(&self) -> u64 { + Self::block_number(self) + } + + fn recover_transactions( + &self, + ) -> impl Iterator>, RecoveryError>> { + use alloy_consensus::transaction::SignerRecoverable; + use alloy_eips::Decodable2718; + + self.diff.transactions.clone().into_iter().map(|raw| { + let tx = BerachainTxEnvelope::decode_2718(&mut raw.as_ref()) + .map_err(RecoveryError::from_source)?; + tx.try_into_recovered().map(|tx| tx.into_encoded_with(raw.clone())) + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::flashblocks::test_utils::BerachainTestFlashBlockFactory; + + #[test] + fn test_flashblock_payload_serde_roundtrip() { + let factory = BerachainTestFlashBlockFactory::new(); + let fb = + factory.flashblock_at(0).transactions(vec![Bytes::from_static(&[1, 2, 3])]).build(); + + let serialized = serde_json::to_string(&fb).expect("serialize"); + let deserialized: BerachainFlashblockPayload = + serde_json::from_str(&serialized).expect("deserialize"); + + assert_eq!(fb, deserialized); + } + + #[test] + fn test_transaction_aggregation_across_flashblocks() { + let factory = BerachainTestFlashBlockFactory::new(); + + let fb0 = factory + .flashblock_at(0) + .transactions(vec![Bytes::from_static(&[1, 2, 3]), Bytes::from_static(&[4, 5, 6])]) + .build(); + let fb1 = factory + .flashblock_after(&fb0) + .transactions(vec![Bytes::from_static(&[7, 8, 9])]) + .build(); + + let complete = FlashBlockCompleteSequence::new(vec![fb0, fb1], None).unwrap(); + let all_txs = complete.all_transactions(); + + assert_eq!(all_txs.len(), 3); + assert_eq!(all_txs[0], Bytes::from_static(&[1, 2, 3])); + assert_eq!(all_txs[1], Bytes::from_static(&[4, 5, 6])); + assert_eq!(all_txs[2], Bytes::from_static(&[7, 8, 9])); + } + + #[test] + fn test_berachain_payload_base_preserved() { + let factory = BerachainTestFlashBlockFactory::new(); + let pubkey = BlsPublicKey::random(); + + let fb0 = factory.flashblock_at(0).prev_proposer_pubkey(Some(pubkey)).build(); + + let complete = FlashBlockCompleteSequence::new(vec![fb0], None).unwrap(); + assert_eq!(complete.payload_base().prev_proposer_pubkey, Some(pubkey)); + } +} diff --git a/src/flashblocks/payload.rs b/src/flashblocks/payload.rs new file mode 100644 index 00000000..bb8d846f --- /dev/null +++ b/src/flashblocks/payload.rs @@ -0,0 +1,29 @@ +use alloy_consensus::BlockHeader; +use alloy_primitives::B256; +use derive_more::Deref; +use reth_primitives_traits::NodePrimitives; +use reth_rpc_eth_types::PendingBlock; + +#[derive(Debug, Clone, Deref)] +pub struct PendingFlashBlock { + #[deref] + pub pending: PendingBlock, + pub last_flashblock_index: u64, + pub last_flashblock_hash: B256, + pub has_computed_state_root: bool, +} + +impl PendingFlashBlock { + pub const fn new( + pending: PendingBlock, + last_flashblock_index: u64, + last_flashblock_hash: B256, + has_computed_state_root: bool, + ) -> Self { + Self { pending, last_flashblock_index, last_flashblock_hash, has_computed_state_root } + } + + pub fn computed_state_root(&self) -> Option { + self.has_computed_state_root.then_some(self.pending.block().state_root()) + } +} diff --git a/src/flashblocks/sequence.rs b/src/flashblocks/sequence.rs new file mode 100644 index 00000000..c93b3af7 --- /dev/null +++ b/src/flashblocks/sequence.rs @@ -0,0 +1,202 @@ +use crate::flashblocks::traits::FlashblockPayload; +use alloy_primitives::{B256, Bytes}; +use alloy_rpc_types_engine::PayloadId; +use core::mem; +use eyre::{OptionExt, bail}; +use reth_revm::cached::CachedReads; +use std::{collections::BTreeMap, ops::Deref}; +use tokio::sync::broadcast; +use tracing::*; + +use crate::flashblocks::traits::FlashblockDiff; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SequenceExecutionOutcome { + pub block_hash: B256, + pub state_root: B256, +} + +#[derive(Debug)] +pub struct FlashBlockPendingSequence { + inner: BTreeMap, + block_broadcaster: broadcast::Sender>, + execution_outcome: Option, + cached_reads: Option, +} + +impl FlashBlockPendingSequence

{ + pub fn new() -> Self { + let (tx, _) = broadcast::channel(128); + Self { + inner: BTreeMap::new(), + block_broadcaster: tx, + execution_outcome: None, + cached_reads: None, + } + } + + pub const fn block_sequence_broadcaster( + &self, + ) -> &broadcast::Sender> { + &self.block_broadcaster + } + + pub fn subscribe_block_sequence(&self) -> broadcast::Receiver> { + self.block_broadcaster.subscribe() + } + + pub fn insert(&mut self, flashblock: P) { + if flashblock.index() == 0 { + trace!(target: "flashblocks", number=%flashblock.block_number(), "Tracking new flashblock sequence"); + self.inner.insert(flashblock.index(), flashblock); + return; + } + + let same_block = self.block_number() == Some(flashblock.block_number()); + let same_payload = self.payload_id() == Some(flashblock.payload_id()); + + if same_block && same_payload { + trace!(target: "flashblocks", number=%flashblock.block_number(), index = %flashblock.index(), block_count = self.inner.len(), "Received followup flashblock"); + self.inner.insert(flashblock.index(), flashblock); + } else { + trace!(target: "flashblocks", number=%flashblock.block_number(), index = %flashblock.index(), current=?self.block_number(), "Ignoring untracked flashblock following"); + } + } + + pub const fn set_execution_outcome( + &mut self, + execution_outcome: Option, + ) { + self.execution_outcome = execution_outcome; + } + + pub fn set_cached_reads(&mut self, cached_reads: CachedReads) { + self.cached_reads = Some(cached_reads); + } + + pub const fn take_cached_reads(&mut self) -> Option { + self.cached_reads.take() + } + + pub fn block_number(&self) -> Option { + Some(self.inner.values().next()?.block_number()) + } + + pub fn payload_base(&self) -> Option<&P::Base> { + self.inner.values().next()?.base() + } + + pub fn count(&self) -> usize { + self.inner.len() + } + + pub fn last_flashblock(&self) -> Option<&P> { + self.inner.last_key_value().map(|(_, b)| b) + } + + pub fn index(&self) -> Option { + Some(self.inner.values().last()?.index()) + } + + pub fn payload_id(&self) -> Option { + Some(self.inner.values().next()?.payload_id()) + } + + pub fn finalize(&mut self) -> eyre::Result> { + if self.inner.is_empty() { + bail!("Cannot finalize empty flashblock sequence"); + } + + let flashblocks = mem::take(&mut self.inner); + let execution_outcome = mem::take(&mut self.execution_outcome); + self.cached_reads = None; + + FlashBlockCompleteSequence::new(flashblocks.into_values().collect(), execution_outcome) + } + + pub fn flashblocks(&self) -> impl Iterator { + self.inner.values() + } +} + +impl Default for FlashBlockPendingSequence

{ + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug, Clone)] +pub struct FlashBlockCompleteSequence { + inner: Vec

, + execution_outcome: Option, +} + +impl FlashBlockCompleteSequence

{ + pub fn new( + blocks: Vec

, + execution_outcome: Option, + ) -> eyre::Result { + let first_block = blocks.first().ok_or_eyre("No flashblocks in sequence")?; + first_block.base().ok_or_eyre("Flashblock at index 0 has no base")?; + + if !blocks.iter().enumerate().all(|(idx, block)| { + idx == block.index() as usize && + block.payload_id() == first_block.payload_id() && + block.block_number() == first_block.block_number() + }) { + bail!("Flashblock inconsistencies detected in sequence"); + } + + Ok(Self { inner: blocks, execution_outcome }) + } + + pub fn block_number(&self) -> u64 { + self.inner.first().unwrap().block_number() + } + + pub fn payload_base(&self) -> &P::Base { + self.inner.first().unwrap().base().unwrap() + } + + pub const fn count(&self) -> usize { + self.inner.len() + } + + pub fn last(&self) -> &P { + self.inner.last().unwrap() + } + + pub const fn execution_outcome(&self) -> Option { + self.execution_outcome + } + + pub const fn set_execution_outcome( + &mut self, + execution_outcome: Option, + ) { + self.execution_outcome = execution_outcome; + } + + pub fn all_transactions(&self) -> Vec { + self.inner.iter().flat_map(|fb| fb.diff().transactions_raw().iter().cloned()).collect() + } + + pub fn flashblocks(&self) -> impl Iterator { + self.inner.iter() + } +} + +impl Deref for FlashBlockCompleteSequence

{ + type Target = Vec

; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl TryFrom> for FlashBlockCompleteSequence

{ + type Error = eyre::Error; + fn try_from(sequence: FlashBlockPendingSequence

) -> Result { + Self::new(sequence.inner.into_values().collect(), sequence.execution_outcome) + } +} diff --git a/src/flashblocks/service.rs b/src/flashblocks/service.rs new file mode 100644 index 00000000..39432fa6 --- /dev/null +++ b/src/flashblocks/service.rs @@ -0,0 +1,238 @@ +use crate::flashblocks::{ + FlashBlockCompleteSequence, InProgressFlashBlockRx, + cache::SequenceManager, + payload::PendingFlashBlock, + traits::{FlashblockPayload, FlashblockPayloadBase}, + worker::FlashBlockBuilder, +}; +use alloy_primitives::B256; +use futures_util::{FutureExt, Stream, StreamExt}; +use metrics::{Gauge, Histogram}; +use reth_evm::ConfigureEvm; +use reth_metrics::Metrics; +use reth_primitives_traits::{AlloyBlockHeader, BlockTy, HeaderTy, NodePrimitives, ReceiptTy}; +use reth_revm::cached::CachedReads; +use reth_storage_api::{BlockReaderIdExt, StateProviderFactory}; +use reth_tasks::TaskExecutor; +use std::{ + sync::Arc, + time::{Duration, Instant}, +}; +use tokio::{ + sync::{broadcast, oneshot, watch}, + time::sleep, +}; +use tracing::*; + +const CONNECTION_BACKOUT_PERIOD: Duration = Duration::from_secs(5); + +#[derive(Debug)] +pub struct FlashBlockService +where + N: NodePrimitives, + P: FlashblockPayload, + EvmConfig: ConfigureEvm + Unpin>, +{ + incoming_flashblock_rx: S, + in_progress_tx: watch::Sender>, + received_flashblocks_tx: broadcast::Sender>, + builder: FlashBlockBuilder, + spawner: TaskExecutor, + job: Option>, + sequences: SequenceManager

, + metrics: FlashBlockServiceMetrics, +} + +impl FlashBlockService +where + N: NodePrimitives, + P: FlashblockPayload, + S: Stream> + Unpin + 'static, + EvmConfig: + ConfigureEvm + Unpin> + Clone + 'static, + Provider: StateProviderFactory + + BlockReaderIdExt< + Header = HeaderTy, + Block = BlockTy, + Transaction = N::SignedTx, + Receipt = ReceiptTy, + > + Unpin + + Clone + + 'static, +{ + pub fn new( + incoming_flashblock_rx: S, + evm_config: EvmConfig, + provider: Provider, + spawner: TaskExecutor, + compute_state_root: bool, + ) -> Self { + let (in_progress_tx, _) = watch::channel(None); + let (received_flashblocks_tx, _) = broadcast::channel(128); + Self { + incoming_flashblock_rx, + in_progress_tx, + received_flashblocks_tx, + builder: FlashBlockBuilder::new(evm_config, provider), + spawner, + job: None, + sequences: SequenceManager::new(compute_state_root), + metrics: FlashBlockServiceMetrics::default(), + } + } + + pub const fn flashblocks_broadcaster(&self) -> &broadcast::Sender> { + &self.received_flashblocks_tx + } + + pub const fn block_sequence_broadcaster( + &self, + ) -> &broadcast::Sender> { + self.sequences.block_sequence_broadcaster() + } + + pub fn subscribe_block_sequence(&self) -> broadcast::Receiver> { + self.sequences.subscribe_block_sequence() + } + + pub fn subscribe_in_progress(&self) -> InProgressFlashBlockRx { + self.in_progress_tx.subscribe() + } + + pub async fn run(mut self, tx: watch::Sender>>) { + loop { + tokio::select! { + Some(result) = async { + match self.job.as_mut() { + Some((_, rx)) => rx.await.ok(), + None => std::future::pending().await, + } + } => { + let (start_time, _) = self.job.take().unwrap(); + let _ = self.in_progress_tx.send(None); + + match result { + Ok(Some((pending, cached_reads))) => { + let parent_hash = pending.parent_hash(); + self.sequences + .on_build_complete(parent_hash, Some((pending.clone(), cached_reads))); + + let elapsed = start_time.elapsed(); + self.metrics.execution_duration.record(elapsed.as_secs_f64()); + + let _ = tx.send(Some(pending)); + } + Ok(None) => { + trace!(target: "flashblocks", "Build job returned None"); + } + Err(err) => { + warn!(target: "flashblocks", %err, "Build job failed"); + } + } + } + + result = self.incoming_flashblock_rx.next() => { + match result { + Some(Ok(flashblock)) => { + self.process_flashblock(flashblock); + + while let Some(result) = self.incoming_flashblock_rx.next().now_or_never().flatten() { + match result { + Ok(fb) => self.process_flashblock(fb), + Err(err) => warn!(target: "flashblocks", %err, "Error receiving flashblock"), + } + } + + self.try_start_build_job(); + } + Some(Err(err)) => { + warn!( + target: "flashblocks", + %err, + retry_period = CONNECTION_BACKOUT_PERIOD.as_secs(), + "Error receiving flashblock" + ); + sleep(CONNECTION_BACKOUT_PERIOD).await; + } + None => { + warn!(target: "flashblocks", "Flashblock stream ended"); + break; + } + } + } + } + } + } + + fn process_flashblock(&mut self, flashblock: P) { + self.notify_received_flashblock(&flashblock); + + if flashblock.index() == 0 { + self.metrics.last_flashblock_length.record(self.sequences.pending().count() as f64); + } + + if let Err(err) = self.sequences.insert_flashblock(flashblock) { + warn!(target: "flashblocks", %err, "Failed to insert flashblock"); + } + } + + fn notify_received_flashblock(&self, flashblock: &P) { + if self.received_flashblocks_tx.receiver_count() > 0 { + let _ = self.received_flashblocks_tx.send(Arc::new(flashblock.clone())); + } + } + + fn try_start_build_job(&mut self) { + if self.job.is_some() { + return; + } + + let Some(latest) = self.builder.provider().latest_header().ok().flatten() else { + return; + }; + + let Some(args) = self.sequences.next_buildable_args(latest.hash(), latest.timestamp()) + else { + return; + }; + + let fb_info = FlashBlockBuildInfo { + parent_hash: args.base.parent_hash(), + index: args.last_flashblock_index, + block_number: args.base.block_number(), + }; + self.metrics.current_block_height.set(fb_info.block_number as f64); + self.metrics.current_index.set(fb_info.index as f64); + let _ = self.in_progress_tx.send(Some(fb_info)); + + let (tx, rx) = oneshot::channel(); + let builder = self.builder.clone(); + self.spawner.spawn_blocking(move || { + let _ = tx.send(builder.execute(args)); + }); + self.job = Some((Instant::now(), rx)); + } +} + +#[derive(Debug, Clone, Copy)] +pub struct FlashBlockBuildInfo { + pub parent_hash: B256, + pub index: u64, + pub block_number: u64, +} + +type BuildJob = + (Instant, oneshot::Receiver, CachedReads)>>>); + +#[derive(Metrics)] +#[metrics(scope = "flashblock_service")] +struct FlashBlockServiceMetrics { + /// Number of flashblocks in the last completed sequence. + last_flashblock_length: Histogram, + /// Duration of the last flashblock execution in seconds. + execution_duration: Histogram, + /// Current block height being processed. + current_block_height: Gauge, + /// Current flashblock index within the sequence. + current_index: Gauge, +} diff --git a/src/flashblocks/test_utils.rs b/src/flashblocks/test_utils.rs new file mode 100644 index 00000000..abb5294a --- /dev/null +++ b/src/flashblocks/test_utils.rs @@ -0,0 +1,156 @@ +//! Test utilities for creating flashblock payloads. + +use crate::{ + flashblocks::{ + BerachainFlashblockPayload, BerachainFlashblockPayloadBase, BerachainFlashblockPayloadDiff, + BerachainFlashblockPayloadMetadata, + }, + primitives::header::BlsPublicKey, +}; +use alloy_primitives::{Address, B256, Bytes, U256}; +use reth::rpc::types::engine::PayloadId; + +#[derive(Debug)] +pub struct BerachainTestFlashBlockFactory { + block_time: u64, + base_timestamp: u64, + current_block_number: u64, +} + +impl BerachainTestFlashBlockFactory { + pub fn new() -> Self { + Self { block_time: 2, base_timestamp: 1_000_000, current_block_number: 100 } + } + + pub fn flashblock_at(&self, index: u64) -> BerachainTestFlashBlockBuilder { + self.builder().index(index).block_number(self.current_block_number) + } + + pub fn flashblock_after( + &self, + previous: &BerachainFlashblockPayload, + ) -> BerachainTestFlashBlockBuilder { + let parent_hash = previous.base.as_ref().map(|b| b.parent_hash).unwrap_or_default(); + + self.builder() + .index(previous.index + 1) + .block_number(previous.metadata.block_number) + .payload_id(previous.payload_id) + .parent_hash(parent_hash) + .timestamp(previous.base.as_ref().map(|b| b.timestamp).unwrap_or(self.base_timestamp)) + } + + pub fn flashblock_for_next_block( + &self, + previous: &BerachainFlashblockPayload, + ) -> BerachainTestFlashBlockBuilder { + let prev_timestamp = + previous.base.as_ref().map(|b| b.timestamp).unwrap_or(self.base_timestamp); + let prev_block_number = previous.metadata.block_number; + + self.builder() + .index(0) + .block_number(prev_block_number + 1) + .payload_id(PayloadId::new(B256::random().0[0..8].try_into().unwrap())) + .parent_hash(B256::random()) + .timestamp(prev_timestamp + self.block_time) + } + + fn builder(&self) -> BerachainTestFlashBlockBuilder { + BerachainTestFlashBlockBuilder { + index: 0, + block_number: self.current_block_number, + payload_id: PayloadId::new([1u8; 8]), + parent_hash: B256::random(), + timestamp: self.base_timestamp, + transactions: vec![], + prev_proposer_pubkey: Some(BlsPublicKey::random()), + } + } +} + +impl Default for BerachainTestFlashBlockFactory { + fn default() -> Self { + Self::new() + } +} + +#[derive(Debug)] +pub struct BerachainTestFlashBlockBuilder { + index: u64, + block_number: u64, + payload_id: PayloadId, + parent_hash: B256, + timestamp: u64, + transactions: Vec, + prev_proposer_pubkey: Option, +} + +impl BerachainTestFlashBlockBuilder { + pub fn index(mut self, index: u64) -> Self { + self.index = index; + self + } + + pub fn block_number(mut self, block_number: u64) -> Self { + self.block_number = block_number; + self + } + + pub fn payload_id(mut self, payload_id: PayloadId) -> Self { + self.payload_id = payload_id; + self + } + + pub fn parent_hash(mut self, parent_hash: B256) -> Self { + self.parent_hash = parent_hash; + self + } + + pub fn timestamp(mut self, timestamp: u64) -> Self { + self.timestamp = timestamp; + self + } + + pub fn transactions(mut self, transactions: Vec) -> Self { + self.transactions = transactions; + self + } + + pub fn prev_proposer_pubkey(mut self, pubkey: Option) -> Self { + self.prev_proposer_pubkey = pubkey; + self + } + + pub fn build(self) -> BerachainFlashblockPayload { + let base = if self.index == 0 { + Some(BerachainFlashblockPayloadBase { + parent_hash: self.parent_hash, + parent_beacon_block_root: B256::random(), + fee_recipient: Address::default(), + prev_randao: B256::random(), + block_number: self.block_number, + gas_limit: 30_000_000, + timestamp: self.timestamp, + extra_data: Default::default(), + base_fee_per_gas: U256::from(1_000_000_000u64), + prev_proposer_pubkey: self.prev_proposer_pubkey, + }) + } else { + None + }; + + BerachainFlashblockPayload { + index: self.index, + payload_id: self.payload_id, + base, + diff: BerachainFlashblockPayloadDiff { + transactions: self.transactions, + withdrawals: vec![], + }, + metadata: BerachainFlashblockPayloadMetadata { block_number: self.block_number }, + signature: [0u8; 96], + is_last: false, + } + } +} diff --git a/src/flashblocks/traits.rs b/src/flashblocks/traits.rs new file mode 100644 index 00000000..a0308daa --- /dev/null +++ b/src/flashblocks/traits.rs @@ -0,0 +1,50 @@ +use alloy_consensus::crypto::RecoveryError; +use alloy_eips::eip4895::Withdrawals; +use alloy_primitives::{B256, Bloom, Bytes}; +use alloy_rpc_types_engine::PayloadId; + +pub trait FlashblockPayloadBase: Clone + Send + Sync + 'static { + fn parent_hash(&self) -> B256; + fn block_number(&self) -> u64; + fn timestamp(&self) -> u64; +} + +pub trait FlashblockDiff: Clone + Send + Sync + 'static { + fn block_hash(&self) -> B256; + fn state_root(&self) -> B256; + fn gas_used(&self) -> u64; + fn logs_bloom(&self) -> &Bloom; + fn receipts_root(&self) -> B256; + fn transactions_raw(&self) -> &[Bytes]; + + fn withdrawals(&self) -> Option<&Withdrawals> { + None + } + + fn withdrawals_root(&self) -> Option { + None + } +} + +pub trait FlashblockPayload: + Clone + Send + Sync + 'static + for<'de> serde::Deserialize<'de> +{ + type Base: FlashblockPayloadBase; + type Diff: FlashblockDiff; + type SignedTx: reth_primitives_traits::SignedTransaction; + + fn index(&self) -> u64; + fn payload_id(&self) -> PayloadId; + fn base(&self) -> Option<&Self::Base>; + fn diff(&self) -> &Self::Diff; + fn block_number(&self) -> u64; + + fn recover_transactions( + &self, + ) -> impl Iterator< + Item = Result< + alloy_eips::eip2718::WithEncoded>, + RecoveryError, + >, + >; +} diff --git a/src/flashblocks/worker.rs b/src/flashblocks/worker.rs new file mode 100644 index 00000000..34b56cb2 --- /dev/null +++ b/src/flashblocks/worker.rs @@ -0,0 +1,143 @@ +use crate::flashblocks::{payload::PendingFlashBlock, traits::FlashblockPayloadBase}; +use alloy_eips::{BlockNumberOrTag, eip2718::WithEncoded}; +use alloy_primitives::B256; +use reth_chain_state::{ComputedTrieData, ExecutedBlock}; +use reth_errors::RethError; +use reth_evm::{ + ConfigureEvm, + execute::{BlockBuilder, BlockBuilderOutcome}, +}; +use reth_execution_types::BlockExecutionOutput; +use reth_primitives_traits::{BlockTy, HeaderTy, NodePrimitives, ReceiptTy, Recovered}; +use reth_revm::{cached::CachedReads, database::StateProviderDatabase, db::State}; +use reth_rpc_eth_types::{EthApiError, PendingBlock}; +use reth_storage_api::{BlockReaderIdExt, StateProviderFactory, noop::NoopProvider}; +use std::{ + marker::PhantomData, + sync::Arc, + time::{Duration, Instant}, +}; +use tracing::trace; + +#[derive(Debug)] +pub(crate) struct FlashBlockBuilder { + evm_config: EvmConfig, + provider: Provider, + _base: PhantomData, +} + +impl FlashBlockBuilder { + pub(crate) const fn new(evm_config: EvmConfig, provider: Provider) -> Self { + Self { evm_config, provider, _base: PhantomData } + } + + pub(crate) const fn provider(&self) -> &Provider { + &self.provider + } +} + +pub(crate) struct BuildArgs { + pub(crate) base: Base, + pub(crate) transactions: I, + pub(crate) cached_state: Option<(B256, CachedReads)>, + pub(crate) last_flashblock_index: u64, + pub(crate) last_flashblock_hash: B256, + pub(crate) compute_state_root: bool, +} + +impl FlashBlockBuilder +where + N: NodePrimitives, + Base: FlashblockPayloadBase, + EvmConfig: ConfigureEvm + Unpin>, + Provider: StateProviderFactory + + BlockReaderIdExt< + Header = HeaderTy, + Block = BlockTy, + Transaction = N::SignedTx, + Receipt = ReceiptTy, + > + Unpin, +{ + pub(crate) fn execute>>>( + &self, + mut args: BuildArgs, + ) -> eyre::Result, CachedReads)>> { + trace!(target: "flashblocks", "Attempting new pending block from flashblocks"); + + let latest = self + .provider + .latest_header()? + .ok_or(EthApiError::HeaderNotFound(BlockNumberOrTag::Latest.into()))?; + let latest_hash = latest.hash(); + + if args.base.parent_hash() != latest_hash { + trace!(target: "flashblocks", flashblock_parent = ?args.base.parent_hash(), local_latest=?latest.num_hash(),"Skipping non consecutive flashblock"); + return Ok(None); + } + + let state_provider = self.provider.history_by_block_hash(latest.hash())?; + + let mut request_cache = args + .cached_state + .take() + .filter(|(hash, _)| hash == &latest_hash) + .map(|(_, state)| state) + .unwrap_or_default(); + let cached_db = request_cache.as_db_mut(StateProviderDatabase::new(&state_provider)); + let mut state = State::builder().with_database(cached_db).with_bundle_update().build(); + + let mut builder = self + .evm_config + .builder_for_next_block(&mut state, &latest, args.base.into()) + .map_err(RethError::other)?; + + builder.apply_pre_execution_changes()?; + + for tx in args.transactions { + let _gas_used = builder.execute_transaction(tx)?; + } + + let BlockBuilderOutcome { execution_result, block, hashed_state, .. } = + if args.compute_state_root { + trace!(target: "flashblocks", "Computing block state root"); + builder.finish(&state_provider)? + } else { + builder.finish(NoopProvider::default())? + }; + + let execution_outcome = + BlockExecutionOutput { state: state.take_bundle(), result: execution_result }; + + let pending_block = PendingBlock::with_executed_block( + Instant::now() + Duration::from_secs(1), + ExecutedBlock::new( + block.into(), + Arc::new(execution_outcome), + ComputedTrieData::without_trie_input( + Arc::new(hashed_state.into_sorted()), + Arc::default(), + ), + ), + ); + let pending_flashblock = PendingFlashBlock::new( + pending_block, + args.last_flashblock_index, + args.last_flashblock_hash, + args.compute_state_root, + ); + + Ok(Some((pending_flashblock, request_cache))) + } +} + +impl Clone + for FlashBlockBuilder +{ + fn clone(&self) -> Self { + Self { + evm_config: self.evm_config.clone(), + provider: self.provider.clone(), + _base: PhantomData, + } + } +} diff --git a/src/flashblocks/ws/decoding.rs b/src/flashblocks/ws/decoding.rs new file mode 100644 index 00000000..1a03645c --- /dev/null +++ b/src/flashblocks/ws/decoding.rs @@ -0,0 +1,41 @@ +use alloy_primitives::bytes::Bytes; +use std::io::{self, Read}; + +pub trait FlashBlockDecoder: Send + 'static { + fn decode(&self, bytes: Bytes) -> eyre::Result; +} + +impl FlashBlockDecoder for () +where + F: serde::de::DeserializeOwned, +{ + fn decode(&self, bytes: Bytes) -> eyre::Result { + decode_flashblock(bytes) + } +} + +fn decode_flashblock(bytes: Bytes) -> eyre::Result +where + F: serde::de::DeserializeOwned, +{ + let bytes = try_decompress(bytes)?; + let payload: F = + serde_json::from_slice(&bytes).map_err(|e| eyre::eyre!("failed to parse message: {e}"))?; + Ok(payload) +} + +// Well above worst-case block size to avoid clipping legitimate data, +// while still preventing decompression bombs from consuming gigabytes. +const MAX_DECOMPRESSED_SIZE: u64 = 32 * 1024 * 1024; + +fn try_decompress(bytes: Bytes) -> eyre::Result { + if bytes.trim_ascii_start().starts_with(b"{") { + return Ok(bytes); + } + + let decompressor = brotli::Decompressor::new(bytes.as_ref(), 4096); + let mut decompressed = Vec::new(); + io::copy(&mut decompressor.take(MAX_DECOMPRESSED_SIZE), &mut decompressed)?; + + Ok(decompressed.into()) +} diff --git a/src/flashblocks/ws/mod.rs b/src/flashblocks/ws/mod.rs new file mode 100644 index 00000000..651d83c9 --- /dev/null +++ b/src/flashblocks/ws/mod.rs @@ -0,0 +1,6 @@ +pub use stream::{WsConnect, WsFlashBlockStream}; + +mod decoding; +pub use decoding::FlashBlockDecoder; + +mod stream; diff --git a/src/flashblocks/ws/stream.rs b/src/flashblocks/ws/stream.rs new file mode 100644 index 00000000..70bcfec6 --- /dev/null +++ b/src/flashblocks/ws/stream.rs @@ -0,0 +1,213 @@ +use crate::flashblocks::ws::FlashBlockDecoder; +use futures_util::{ + FutureExt, Sink, Stream, StreamExt, + stream::{SplitSink, SplitStream}, +}; +use std::{ + fmt::{Debug, Formatter}, + future::Future, + pin::Pin, + task::{Context, Poll, ready}, +}; +use tokio::net::TcpStream; +use tokio_tungstenite::{ + MaybeTlsStream, WebSocketStream, connect_async, + tungstenite::{Bytes, Error, Message, protocol::CloseFrame}, +}; +use tracing::debug; +use url::Url; + +pub struct WsFlashBlockStream { + ws_url: Url, + state: State, + connector: Connector, + decoder: Box>, + connect: ConnectFuture, + stream: Option, + sink: Option, +} + +impl WsFlashBlockStream +where + F: serde::de::DeserializeOwned, +{ + pub fn new(ws_url: Url) -> Self { + Self { + ws_url, + state: State::default(), + connector: WsConnector, + decoder: Box::new(()), + connect: Box::pin(async move { Err(Error::ConnectionClosed)? }), + stream: None, + sink: None, + } + } +} + +impl WsFlashBlockStream { + pub fn with_decoder(self, decoder: Box>) -> Self { + Self { decoder, ..self } + } +} + +impl WsFlashBlockStream +where + F: serde::de::DeserializeOwned, +{ + pub fn with_connector(ws_url: Url, connector: C) -> Self { + Self { + ws_url, + state: State::default(), + decoder: Box::new(()), + connector, + connect: Box::pin(async move { Err(Error::ConnectionClosed)? }), + stream: None, + sink: None, + } + } +} + +impl Stream for WsFlashBlockStream +where + Str: Stream> + Unpin, + S: Sink + Send + Unpin, + C: WsConnect + Clone + Send + 'static + Unpin, + F: 'static, +{ + type Item = eyre::Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let this = self.get_mut(); + + 'start: loop { + if this.state == State::Initial { + this.connect(); + } + + if this.state == State::Connect { + match ready!(this.connect.poll_unpin(cx)) { + Ok((sink, stream)) => this.stream(sink, stream), + Err(err) => { + this.state = State::Initial; + return Poll::Ready(Some(Err(err))); + } + } + } + + while let State::Stream(msg) = &mut this.state { + if msg.is_some() { + let mut sink = Pin::new(this.sink.as_mut().unwrap()); + let _ = ready!(sink.as_mut().poll_ready(cx)); + if let Some(pong) = msg.take() { + let _ = sink.as_mut().start_send(pong); + } + let _ = ready!(sink.as_mut().poll_flush(cx)); + } + + let Some(msg) = ready!( + this.stream + .as_mut() + .expect("Stream state should be unreachable without stream") + .poll_next_unpin(cx) + ) else { + this.state = State::Initial; + continue 'start; + }; + + match msg { + Ok(Message::Binary(bytes)) => { + return Poll::Ready(Some(this.decoder.decode(bytes))); + } + Ok(Message::Text(bytes)) => { + return Poll::Ready(Some(this.decoder.decode(bytes.into()))); + } + Ok(Message::Ping(bytes)) => this.ping(bytes), + Ok(Message::Close(frame)) => this.close(frame), + Ok(msg) => { + debug!(target: "flashblocks", "Received unexpected message: {:?}", msg) + } + Err(err) => return Poll::Ready(Some(Err(err.into()))), + } + } + } + } +} + +impl WsFlashBlockStream +where + C: WsConnect + Clone + Send + 'static, +{ + fn connect(&mut self) { + let ws_url = self.ws_url.clone(); + let mut connector = self.connector.clone(); + Pin::new(&mut self.connect).set(Box::pin(async move { connector.connect(ws_url).await })); + self.state = State::Connect; + } + + fn stream(&mut self, sink: S, stream: Stream) { + self.sink.replace(sink); + self.stream.replace(stream); + self.state = State::Stream(None); + } + + fn ping(&mut self, pong: Bytes) { + if let State::Stream(current) = &mut self.state { + current.replace(Message::Pong(pong)); + } + } + + fn close(&mut self, frame: Option) { + if let State::Stream(current) = &mut self.state { + current.replace(Message::Close(frame)); + } + } +} + +impl Debug for WsFlashBlockStream { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FlashBlockStream") + .field("ws_url", &self.ws_url) + .field("state", &self.state) + .field("connector", &self.connector) + .field("connect", &"Pin>>") + .field("stream", &self.stream) + .finish() + } +} + +#[derive(Default, Debug, Eq, PartialEq)] +enum State { + #[default] + Initial, + Connect, + Stream(Option), +} + +type Ws = WebSocketStream>; +type WsStream = SplitStream; +type WsSink = SplitSink; +type ConnectFuture = + Pin> + Send + 'static>>; + +pub trait WsConnect { + type Stream; + type Sink; + + fn connect( + &mut self, + ws_url: Url, + ) -> impl Future> + Send; +} + +#[derive(Debug, Clone)] +pub struct WsConnector; + +impl WsConnect for WsConnector { + type Stream = WsStream; + type Sink = WsSink; + + async fn connect(&mut self, ws_url: Url) -> eyre::Result<(WsSink, WsStream)> { + let (stream, _response) = connect_async(ws_url.as_str()).await?; + Ok(stream.split()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 30b4118d..41f537e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,12 +6,14 @@ pub mod chainspec; pub mod consensus; pub mod engine; pub mod evm; +pub mod flashblocks; pub mod genesis; pub mod hardforks; pub mod node; pub mod pool; pub mod primitives; pub mod rpc; +pub mod sequencer; pub mod transaction; pub mod version; diff --git a/src/main.rs b/src/main.rs index d07ba485..aa0b214f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,14 +8,20 @@ use bera_reth::{ consensus::BerachainBeaconConsensus, evm::BerachainEvmFactory, node::{BerachainNode, evm::config::BerachainEvmConfig}, + rpc::{BerachainAddOns, BerachainEthApiBuilder}, + sequencer::{ + FlashblockPayloadServiceBuilder, FlashblockSigner, SequencerConfig, WebSocketPublisher, + }, version::init_bera_version, }; use clap::Parser; +use jsonrpsee::client_transport::ws::Url; use reth::CliRunner; -use reth_cli_commands::node::NoArgs; +use reth_chainspec::EthChainSpec; use reth_ethereum_cli::Cli; -use reth_node_builder::NodeHandle; -use std::sync::Arc; +use reth_node_builder::{Node, NodeHandle, components::BasicPayloadServiceBuilder}; +use std::{net::SocketAddr, path::PathBuf, sync::Arc}; +use tokio_util::sync::CancellationToken; use tracing::info; /// Persist every canonical block to disk immediately rather than buffering. @@ -23,6 +29,30 @@ use tracing::info; /// eager persistence to keep the in-memory block window minimal. const BERACHAIN_DEFAULT_PERSISTENCE_THRESHOLD: u64 = 0; +/// Sequencer-specific CLI arguments +#[derive(Debug, Clone, clap::Args)] +pub struct SequencerArgs { + /// Enable sequencer mode for flashblock production + #[arg(long, default_value = "false")] + pub sequencer_enabled: bool, + + /// Flashblock emission interval in milliseconds + #[arg(long, default_value = "200")] + pub flashblock_interval_ms: u64, + + /// WebSocket address for flashblock publishing + #[arg(long, default_value = "0.0.0.0:8548")] + pub flashblock_ws_addr: SocketAddr, + + /// Path to BLS secret key file for signing flashblocks (hex-encoded 32-byte key) + #[arg(long)] + pub flashblock_signing_key: Option, + + /// WebSocket URL for subscribing to flashblocks from a sequencer (e.g., ws://sequencer:8548) + #[arg(long)] + pub flashblocks_url: Option, +} + fn main() { // Install signal handler for better crash reporting reth_cli_util::sigsegv_handler::install(); @@ -47,16 +77,87 @@ fn main() { ) }; - if let Err(err) = Cli::::parse() + if let Err(err) = Cli::::parse() .with_runner_and_components::( CliRunner::try_default_runtime().expect("Failed to create default runtime"), cli_components_builder, - async move |builder, _| { - info!(target: "reth::cli", "Launching Berachain node"); - let NodeHandle { node: _node, node_exit_future } = - builder.node(BerachainNode::default()).launch_with_debug_capabilities().await?; + async move |builder, extra_args| { + if extra_args.sequencer_enabled { + // Signing key is required in sequencer mode + let chain_id = builder.config().chain.chain().id(); + let key_path = extra_args.flashblock_signing_key.ok_or_else(|| { + eyre::eyre!("--flashblock-signing-key is required in sequencer mode") + })?; + let signer = FlashblockSigner::from_file(&key_path, chain_id) + .map_err(|e| eyre::eyre!("failed to load signing key from {:?}: {}", key_path, e))?; + + info!( + target: "reth::cli", + interval_ms = extra_args.flashblock_interval_ms, + ws_addr = %extra_args.flashblock_ws_addr, + pubkey = %hex::encode(signer.public_key_bytes()), + "Launching Berachain node in SEQUENCER mode" + ); + + let config = SequencerConfig::new( + extra_args.flashblock_interval_ms, + extra_args.flashblock_ws_addr, + signer, + ); + + let publisher = Arc::new(WebSocketPublisher::new(config.ws_addr)); + let ws_cancel = CancellationToken::new(); + + // Bind WebSocket server before launching node + let ws_listener = publisher.bind().await?; + + // Spawn WebSocket server + let ws_publisher = publisher.clone(); + let ws_cancel_token = ws_cancel.clone(); + tokio::spawn(async move { + if let Err(e) = ws_publisher.run(ws_listener, ws_cancel_token).await { + tracing::error!(target: "sequencer::publisher", error = %e, "WebSocket server error"); + } + }); + + // Build node with flashblock payload builder + let berachain_node = BerachainNode::default(); + let flashblock_builder = FlashblockPayloadServiceBuilder::new(config, publisher); + + let NodeHandle { node: _node, node_exit_future } = builder + .with_types::() + .with_components( + berachain_node + .components_builder() + .payload(BasicPayloadServiceBuilder::new(flashblock_builder)), + ) + .with_add_ons(berachain_node.add_ons()) + .launch_with_debug_capabilities() + .await?; + + let result = node_exit_future.await; + ws_cancel.cancel(); + result + } else { + if let Some(ref url) = extra_args.flashblocks_url { + info!(target: "reth::cli", %url, "Launching Berachain node with flashblocks"); + } else { + info!(target: "reth::cli", "Launching Berachain node"); + } + + let eth_api_builder = BerachainEthApiBuilder::default() + .with_flashblocks_url(extra_args.flashblocks_url); + let berachain_node = BerachainNode::default(); + + let NodeHandle { node: _node, node_exit_future } = builder + .with_types::() + .with_components(berachain_node.components_builder()) + .with_add_ons(BerachainAddOns::new(eth_api_builder)) + .launch_with_debug_capabilities() + .await?; - node_exit_future.await + node_exit_future.await + } }, ) { diff --git a/src/node/evm/config.rs b/src/node/evm/config.rs index 785640e0..93d11741 100644 --- a/src/node/evm/config.rs +++ b/src/node/evm/config.rs @@ -2,6 +2,7 @@ use crate::{ chainspec::BerachainChainSpec, engine::BerachainExecutionData, evm::BerachainEvmFactory, + flashblocks::BerachainFlashblockPayloadBase, node::evm::{ assembler::BerachainBlockAssembler, block_context::BerachainBlockExecutionCtx, builder::BerachainBlockBuilder, receipt::BerachainReceiptBuilder, @@ -37,7 +38,7 @@ use reth_primitives_traits::{ use reth_rpc_eth_api::helpers::pending_block::BuildPendingEnv; use std::{borrow::Cow, convert::Infallible, fmt::Debug, sync::Arc}; -const BERACHAIN_BLOCK_TIME_SECONDS: u64 = 2; +pub const BERACHAIN_BLOCK_TIME_SECONDS: u64 = 2; #[derive(Debug, Clone)] pub struct BerachainEvmConfig { @@ -365,3 +366,18 @@ impl ConfigureEngineEvm for BerachainEvmConfig { })) } } + +impl From for BerachainNextBlockEnvAttributes { + fn from(value: BerachainFlashblockPayloadBase) -> Self { + Self { + timestamp: value.timestamp, + suggested_fee_recipient: value.fee_recipient, + prev_randao: value.prev_randao, + gas_limit: value.gas_limit, + parent_beacon_block_root: Some(value.parent_beacon_block_root), + withdrawals: None, + prev_proposer_pubkey: value.prev_proposer_pubkey, + extra_data: Default::default(), + } + } +} diff --git a/src/node/evm/mod.rs b/src/node/evm/mod.rs index afe8e57d..bd43c9ed 100644 --- a/src/node/evm/mod.rs +++ b/src/node/evm/mod.rs @@ -2,12 +2,14 @@ mod assembler; mod block_context; -mod builder; +pub mod builder; pub mod config; pub mod error; pub mod executor; pub mod receipt; +pub use builder::BerachainBlockBuilder; + use crate::{ evm::BerachainEvmFactory, node::{BerachainNode, evm::config::BerachainEvmConfig}, diff --git a/src/rpc/api.rs b/src/rpc/api.rs index 35817e31..31e77571 100644 --- a/src/rpc/api.rs +++ b/src/rpc/api.rs @@ -1,10 +1,11 @@ use crate::{ + flashblocks::{BerachainFlashblockPayload, FlashblocksListeners}, primitives::BerachainHeader, rpc::receipt::BerachainReceiptEnvelope, transaction::{BerachainTxEnvelope, BerachainTxType, POL_TX_TYPE}, }; -use alloy_consensus::Transaction; -use alloy_eips::eip2930::AccessList; +use alloy_consensus::{BlockHeader, Transaction}; +use alloy_eips::{BlockId, BlockNumberOrTag, eip2930::AccessList}; use alloy_network::{ BuildResult, Network, NetworkWallet, TransactionBuilder, TransactionBuilderError, }; @@ -13,16 +14,17 @@ use alloy_rpc_types_eth::{Transaction as RpcTransaction, TransactionRequest}; use core::fmt; use derive_more::Deref; use reth::{ - providers::ProviderHeader, + providers::{BlockReaderIdExt, ProviderHeader}, rpc::compat::RpcConvert, tasks::{ TaskSpawner, pool::{BlockingTaskGuard, BlockingTaskPool}, }, - transaction_pool::{PoolTransaction, TransactionPool}, }; +use reth_chain_state::BlockState; +use reth_primitives_traits::{BlockBody, RecoveredBlock}; use reth_rpc_eth_api::{ - EthApiTypes, RpcNodeCore, RpcNodeCoreExt, + EthApiTypes, FromEthApiError, RpcNodeCore, RpcNodeCoreExt, RpcReceipt, helpers::{ Call, EthApiSpec, EthBlocks, EthCall, EthFees, EthState, EthTransactions, LoadBlock, LoadFee, LoadPendingBlock, LoadReceipt, LoadState, LoadTransaction, SpawnBlocking, Trace, @@ -31,9 +33,13 @@ use reth_rpc_eth_api::{ }; use reth_rpc_eth_types::{ EthApiError, EthStateCache, FeeHistoryCache, GasPriceOracle, PendingBlock, - builder::config::PendingBlockKind, error::FromEvmError, + block::BlockAndReceipts, builder::config::PendingBlockKind, error::FromEvmError, }; -use reth_transaction_pool::{AddedTransactionOutcome, TransactionOrigin}; +use reth_storage_api::{BlockIdReader, BlockReader, StateProviderBox, StateProviderFactory}; +use reth_transaction_pool::{ + AddedTransactionOutcome, PoolTransaction, TransactionOrigin, TransactionPool, +}; +use std::sync::Arc; impl fmt::Display for BerachainTxType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -327,6 +333,29 @@ pub struct BerachainApi { /// All nested fields bundled together. #[deref] pub(super) inner: reth_rpc::EthApi, + + /// Flashblocks listeners. + /// + /// If set, provides receivers for pending blocks, flashblock sequences, and build status. + pub flashblocks: Option>>, +} + +impl BerachainApi +where + N::Provider: BlockReaderIdExt, +{ + /// Returns the current pending block from the flashblock stream, if any. + /// + /// Only returns a block whose parent hash matches the latest canonical header, + /// ensuring stale flashblocks are not served during reorgs. + pub fn pending_flashblock(&self) -> Option> { + let latest_hash = self.provider().latest_header().ok().flatten()?.hash(); + + self.flashblocks + .as_ref() + .and_then(|f| f.pending_block_rx.borrow().as_ref().map(|b| b.pending.clone())) + .filter(|pending| pending.block().parent_hash() == latest_hash) + } } impl Clone for BerachainApi @@ -335,7 +364,7 @@ where Rpc: RpcConvert, { fn clone(&self) -> Self { - Self { inner: self.inner.clone() } + Self { inner: self.inner.clone(), flashblocks: self.flashblocks.clone() } } } @@ -453,19 +482,36 @@ where ) -> Result { let (raw_tx, recovered) = tx.split(); - // broadcast raw transaction to subscribers if there is any. self.broadcast_raw_transaction(raw_tx); let pool_transaction = ::Transaction::from_pooled(recovered); - // Upstream reth v1.11.1 changed eth_sendRawTransaction to pass External origin, - // but Berachain treats all RPC-submitted transactions as Local to bypass pool - // capacity limits and ensure consistent block gas utilization. let AddedTransactionOutcome { hash, .. } = self.pool().add_transaction(TransactionOrigin::Local, pool_transaction).await?; Ok(hash) } + + fn transaction_receipt( + &self, + hash: B256, + ) -> impl Future>, Self::Error>> + Send + { + let this = self.clone(); + async move { + let tx_receipt = this.load_transaction_and_receipt(hash).await?; + + if tx_receipt.is_none() && + let Some(pending_block) = this.pending_flashblock() && + let Some(Ok(receipt)) = + pending_block.find_and_convert_transaction_receipt(hash, this.converter()) + { + return Ok(Some(receipt)); + } + let Some((tx, meta, receipt)) = tx_receipt else { return Ok(None) }; + this.build_transaction_receipt(tx, meta, receipt).await.map(Some) + } + } } impl LoadTransaction for BerachainApi @@ -500,6 +546,43 @@ where EthApiError: FromEvmError, Rpc: RpcConvert, { + #[allow(clippy::manual_async_fn)] + fn block_transaction_count( + &self, + block_id: BlockId, + ) -> impl Future, Self::Error>> + Send { + async move { + if (block_id.is_latest() || block_id.is_pending()) && + let Some(pending) = self.pending_flashblock() + { + return Ok(Some(pending.block().body().transaction_count())); + } + + if block_id.is_pending() { + return Ok(self + .provider() + .pending_block() + .map_err(Into::::into)? + .map(|block| block.body().transaction_count())); + } + + let block_hash = match self + .provider() + .block_hash_for_id(block_id) + .map_err(Into::::into)? + { + Some(block_hash) => block_hash, + None => return Ok(None), + }; + + Ok(self + .cache() + .get_recovered_block(block_hash) + .await + .map_err(Into::::into)? + .map(|b| b.body().transaction_count())) + } + } } impl LoadBlock for BerachainApi @@ -508,6 +591,52 @@ where N: RpcNodeCore, Rpc: RpcConvert, { + #[allow(clippy::manual_async_fn, clippy::collapsible_if)] + fn recovered_block( + &self, + block_id: BlockId, + ) -> impl Future< + Output = Result< + Option::Block>>>, + Self::Error, + >, + > + Send { + async move { + // Serve flashblock for both "latest" and "pending" when available + if block_id.is_latest() || block_id.is_pending() { + if self.pending_flashblock().is_some() { + if let Some(pending) = self.local_pending_block().await? { + return Ok(Some(pending.block)); + } + } + } + + // Default pending fallback: CL-provided block, then locally built + if block_id.is_pending() { + if let Some(pending_block) = + self.provider().pending_block().map_err(Self::Error::from_eth_err)? + { + return Ok(Some(Arc::new(pending_block))); + } + + return match self.local_pending_block().await? { + Some(pending) => Ok(Some(pending.block)), + None => Ok(None), + }; + } + + let block_hash = match self + .provider() + .block_hash_for_id(block_id) + .map_err(Self::Error::from_eth_err)? + { + Some(block_hash) => block_hash, + None => return Ok(None), + }; + + self.cache().get_recovered_block(block_hash).await.map_err(Self::Error::from_eth_err) + } + } } impl EthCall for BerachainApi @@ -582,6 +711,30 @@ where Rpc: RpcConvert, Self: LoadPendingBlock, { + #[allow(clippy::manual_async_fn, clippy::collapsible_if)] + fn state_at_block_id_or_latest( + &self, + block_id: Option, + ) -> impl Future> + Send + where + Self: SpawnBlocking, + { + async move { + let should_use_flashblock = block_id.is_none_or(|id| id.is_latest() || id.is_pending()); + + if should_use_flashblock { + if let Ok(Some(state)) = self.local_pending_state().await { + return Ok(state); + } + } + + if let Some(block_id) = block_id { + self.state_at_block_id(block_id).await + } else { + Ok(self.latest_state()?) + } + } + } } impl LoadFee for BerachainApi @@ -599,6 +752,25 @@ where fn fee_history_cache(&self) -> &FeeHistoryCache> { self.inner.fee_history_cache() } + + #[allow(clippy::manual_async_fn)] + fn gas_price(&self) -> impl Future> + Send { + async move { + let suggested_tip = LoadFee::suggested_priority_fee(self).await?; + + let base_fee = if let Some(pending) = self.pending_flashblock() { + pending.block().base_fee_per_gas().unwrap_or_default() + } else { + self.provider() + .latest_header() + .map_err(Into::::into)? + .and_then(|h| h.base_fee_per_gas()) + .unwrap_or_default() + }; + + Ok(suggested_tip + U256::from(base_fee)) + } + } } impl LoadPendingBlock for BerachainApi @@ -621,4 +793,54 @@ where fn pending_block_kind(&self) -> PendingBlockKind { self.inner.pending_block_kind() } + + /// Returns the pending state built on top of the latest flashblock. + /// + /// If the flashblock's parent block hasn't been imported into the DB yet, falls back to + /// canonical state via `Ok(None)`. + async fn local_pending_state(&self) -> Result, Self::Error> + where + Self: SpawnBlocking, + { + let Some(pending_block) = self.pending_flashblock() else { + tracing::info!("no pending flashblock available, falling back to canonical state"); + return Ok(None); + }; + + let parent_hash = pending_block.block().parent_hash(); + + let Ok(latest_historical) = + self.provider().history_by_block_hash(parent_hash).map_err(Into::::into) + else { + tracing::info!( + %parent_hash, + "parent block not imported yet, falling back to canonical state" + ); + return Ok(None); + }; + + let state = BlockState::from(pending_block); + Ok(Some(Box::new(state.state_provider(latest_historical)) as StateProviderBox)) + } + + async fn local_pending_block( + &self, + ) -> Result>, Self::Error> { + if let Some(pending) = self.pending_flashblock() { + return Ok(Some(pending.into_block_and_receipts())); + } + + let latest = self + .provider() + .latest_header()? + .ok_or(EthApiError::HeaderNotFound(BlockNumberOrTag::Latest.into()))?; + + let latest = self + .cache() + .get_block_and_receipts(latest.hash()) + .await + .map_err(Into::::into)? + .map(|(block, receipts)| BlockAndReceipts { block, receipts }); + Ok(latest) + } } diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index f9b582ab..24351393 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -1,4 +1,6 @@ +use jsonrpsee::client_transport::ws::Url; use reth_rpc_eth_api::helpers::config::EthConfigApiServer; +use std::sync::Arc; pub mod api; pub mod config; pub mod receipt; @@ -9,6 +11,10 @@ use crate::{ BerachainExecutionData, rpc::BerachainEngineApiBuilder, validator::BerachainEngineValidatorBuilder, }, + flashblocks::{ + BerachainFlashblockPayload, BerachainFlashblockPayloadBase, FlashBlockService, + FlashblocksListeners, WsFlashBlockStream, + }, node::evm::config::{BerachainEvmConfig, BerachainNextBlockEnvAttributes}, primitives::BerachainPrimitives, rpc::{ @@ -37,10 +43,48 @@ use reth_node_builder::rpc::{ }; use reth_rpc_convert::{RpcConvert, RpcConverter}; use reth_rpc_eth_api::helpers::pending_block::BuildPendingEnv; +use tokio::sync::watch; +use tracing::info; /// Builds `BerachainEthApi` for Berachain. -#[derive(Debug, Default)] -pub struct BerachainEthApiBuilder; +#[derive(Clone, Default)] +pub struct BerachainEthApiBuilder { + /// A URL pointing to a secure websocket connection (wss) that streams out flashblocks. + flashblocks_url: Option, + /// Pre-built flashblocks listeners (for testing/dependency injection). + /// Wrapped in Arc for clonability. + flashblocks_listeners: + Option>>, +} + +impl std::fmt::Debug for BerachainEthApiBuilder { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("BerachainEthApiBuilder") + .field("flashblocks_url", &self.flashblocks_url) + .field("has_flashblocks_listeners", &self.flashblocks_listeners.is_some()) + .finish() + } +} + +impl BerachainEthApiBuilder { + /// Configure flashblocks with a WebSocket URL. + pub fn with_flashblocks_url(mut self, url: Option) -> Self { + self.flashblocks_url = url; + self + } + + /// Configure flashblocks with pre-built listeners. + /// + /// This is primarily useful for testing, where you can build `FlashblocksListeners` + /// from a custom stream source instead of connecting to a real WebSocket server. + pub fn with_flashblocks_listeners( + mut self, + listeners: FlashblocksListeners, + ) -> Self { + self.flashblocks_listeners = Some(Arc::new(listeners)); + self + } +} pub type BerachainEthRpcConverterFor = RpcConverter< BerachainNetwork, @@ -55,7 +99,11 @@ where ChainSpec: EthereumHardforks + Hardforks, Primitives = BerachainPrimitives, >, - Evm: ConfigureEvm>>, + Evm: ConfigureEvm< + NextBlockEnvCtx: BuildPendingEnv> + + From + + Unpin, + >, >, BerachainEthRpcConverterFor: RpcConvert< Primitives = PrimitivesTy, @@ -68,13 +116,52 @@ where type EthApi = BerachainApi>; async fn build_eth_api(self, ctx: EthApiCtx<'_, N>) -> eyre::Result { + if self.flashblocks_listeners.is_some() && self.flashblocks_url.is_some() { + return Err(eyre::eyre!( + "Cannot configure both flashblocks_listeners and flashblocks_url. \ + Use flashblocks_listeners for testing or flashblocks_url for production." + )); + } + let tx_resp_builder = BerachainEthRpcConverterFor::::new( BerachainEthReceiptConverter::new(ctx.components.provider().clone().chain_spec()), ); + let flashblocks = if let Some(listeners) = self.flashblocks_listeners { + info!(target: "bera-reth:rpc", "Using pre-built flashblocks listeners"); + Some(listeners) + } else if let Some(ws_url) = self.flashblocks_url { + info!(target: "bera-reth:rpc", %ws_url, "Launching flashblocks service"); + + let (tx, pending_rx) = watch::channel(None); + let stream: WsFlashBlockStream<_, _, _, BerachainFlashblockPayload> = + WsFlashBlockStream::new(ws_url); + let service = FlashBlockService::new( + stream, + ctx.components.evm_config().clone(), + ctx.components.provider().clone(), + ctx.components.task_executor().clone(), + false, + ); + + let flashblocks_sequence = service.block_sequence_broadcaster().clone(); + let received_flashblocks = service.flashblocks_broadcaster().clone(); + let in_progress_rx = service.subscribe_in_progress(); + ctx.components.task_executor().spawn_task(Box::pin(service.run(tx))); + + Some(Arc::new(FlashblocksListeners::new( + pending_rx, + flashblocks_sequence, + in_progress_rx, + received_flashblocks, + ))) + } else { + None + }; + let inner = ctx.eth_api_builder().with_rpc_converter(tx_resp_builder.clone()).build(); - Ok(BerachainApi { inner }) + Ok(BerachainApi { inner, flashblocks }) } } @@ -97,9 +184,27 @@ where BerachainEthApiBuilder: EthApiBuilder, { fn default() -> Self { + Self::new(BerachainEthApiBuilder::default()) + } +} + +impl BerachainAddOns +where + N: FullNodeComponents, + BerachainEthApiBuilder: EthApiBuilder, +{ + /// Creates new Berachain add-ons with a custom EthApiBuilder. + /// + /// This is useful for testing, where you can inject pre-built flashblocks listeners: + /// ```ignore + /// let eth_api_builder = BerachainEthApiBuilder::default() + /// .with_flashblocks_listeners(listeners); + /// let add_ons = BerachainAddOns::new(eth_api_builder); + /// ``` + pub fn new(eth_api_builder: BerachainEthApiBuilder) -> Self { Self { inner: RpcAddOns::new( - BerachainEthApiBuilder, + eth_api_builder, BerachainEngineValidatorBuilder::default(), BerachainEngineApiBuilder::::default(), BasicEngineValidatorBuilder::new(BerachainEngineValidatorBuilder::default()), diff --git a/src/sequencer/builder.rs b/src/sequencer/builder.rs new file mode 100644 index 00000000..c1f91c18 --- /dev/null +++ b/src/sequencer/builder.rs @@ -0,0 +1,620 @@ +//! Flashblock-aware payload builder for sequencer mode. +//! +//! This builder produces flashblocks at regular intervals (~200ms) while building +//! a block, publishing them via WebSocket for preconfirmation subscribers. + +use crate::{ + chainspec::BerachainChainSpec, + engine::payload::{BerachainBuiltPayload, BerachainPayloadBuilderAttributes}, + flashblocks::{ + BerachainFlashblockPayload, BerachainFlashblockPayloadBase, BerachainFlashblockPayloadDiff, + BerachainFlashblockPayloadMetadata, + }, + hardforks::BerachainHardforks, + node::evm::config::{BerachainEvmConfig, BerachainNextBlockEnvAttributes}, + primitives::BerachainHeader, + sequencer::{ + SequencerConfig, WebSocketPublisher, + signing::{FlashblockSigner, compute_transactions_hash}, + }, + transaction::BerachainTxEnvelope, +}; +use alloy_consensus::Transaction; +use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawal}; +use alloy_primitives::{Bytes, U256}; +use reth::{ + api::{FullNodeTypes, NodeTypes, PayloadBuilderError, PayloadTypes, TxTy}, + chainspec::EthereumHardforks, + providers::StateProviderFactory, + revm::{State, context::Block, database::StateProviderDatabase}, + transaction_pool::{PoolTransaction, TransactionPool}, +}; +use reth_basic_payload_builder::{ + BuildArguments, BuildOutcome, MissingPayloadBehaviour, PayloadBuilder, PayloadConfig, +}; +use reth_chainspec::{ChainSpecProvider, EthChainSpec}; +use reth_ethereum_engine_primitives::BlobSidecars; +use reth_ethereum_payload_builder::EthereumBuilderConfig; +use reth_ethereum_primitives::Receipt; +use reth_evm::{ + ConfigureEvm, Evm, + block::{BlockExecutionError, BlockValidationError, CommitChanges}, + execute::{BlockBuilder, BlockBuilderOutcome}, +}; +use reth_node_builder::{BuilderContext, PayloadBuilderConfig, components::PayloadBuilderBuilder}; +use reth_payload_primitives::PayloadBuilderAttributes; +use reth_primitives_traits::transaction::error::InvalidTransactionError; +use reth_transaction_pool::{ + BestTransactions, BestTransactionsAttributes, ValidPoolTransaction, + error::{Eip4844PoolTransactionError, InvalidPoolTransactionError}, +}; +use std::{ + sync::{ + Arc, + atomic::{AtomicBool, Ordering}, + }, + time::{Duration, Instant}, +}; +use tracing::{debug, info, trace, warn}; + +use crate::transaction::BerachainTxType; + +type BestTransactionsIter = Box< + dyn BestTransactions::Transaction>>>, +>; + +/// Service builder for creating flashblock-aware payload builders. +#[derive(Clone, Debug)] +pub struct FlashblockPayloadServiceBuilder { + config: SequencerConfig, + publisher: Arc, +} + +impl FlashblockPayloadServiceBuilder { + /// Create a new flashblock payload service builder. + pub fn new(config: SequencerConfig, publisher: Arc) -> Self { + Self { config, publisher } + } +} + +impl PayloadBuilderBuilder + for FlashblockPayloadServiceBuilder +where + Types: NodeTypes< + ChainSpec = BerachainChainSpec, + Primitives = crate::primitives::BerachainPrimitives, + >, + Node: FullNodeTypes, + Pool: TransactionPool>> + + Unpin + + 'static, + Types::Payload: PayloadTypes< + BuiltPayload = BerachainBuiltPayload, + PayloadAttributes = crate::engine::payload::BerachainPayloadAttributes, + PayloadBuilderAttributes = BerachainPayloadBuilderAttributes, + >, +{ + type PayloadBuilder = FlashblockPayloadBuilder; + + async fn build_payload_builder( + self, + ctx: &BuilderContext, + pool: Pool, + evm_config: BerachainEvmConfig, + ) -> eyre::Result { + let conf = ctx.payload_builder_config(); + let chain = ctx.chain_spec().chain(); + let gas_limit = conf.gas_limit_for(chain); + + Ok(FlashblockPayloadBuilder::new( + ctx.provider().clone(), + pool, + evm_config, + EthereumBuilderConfig::new().with_gas_limit(gas_limit), + self.config, + self.publisher, + conf.deadline(), + )) + } +} + +/// Flashblock-aware payload builder. +/// +/// This builder emits flashblocks at regular intervals while building a payload, +/// allowing preconfirmation subscribers to track transaction inclusion in real-time. +#[derive(Debug)] +pub struct FlashblockPayloadBuilder { + client: Client, + pool: Pool, + evm_config: BerachainEvmConfig, + builder_config: EthereumBuilderConfig, + sequencer_config: SequencerConfig, + publisher: Arc, + deadline: Duration, + payload_requested: Arc, +} + +impl Clone for FlashblockPayloadBuilder { + fn clone(&self) -> Self { + Self { + client: self.client.clone(), + pool: self.pool.clone(), + evm_config: self.evm_config.clone(), + builder_config: self.builder_config.clone(), + sequencer_config: self.sequencer_config.clone(), + publisher: self.publisher.clone(), + deadline: self.deadline, + payload_requested: self.payload_requested.clone(), + } + } +} + +impl FlashblockPayloadBuilder { + /// Create a new flashblock payload builder. + pub fn new( + client: Client, + pool: Pool, + evm_config: BerachainEvmConfig, + builder_config: EthereumBuilderConfig, + sequencer_config: SequencerConfig, + publisher: Arc, + deadline: Duration, + ) -> Self { + Self { + client, + pool, + evm_config, + builder_config, + sequencer_config, + publisher, + deadline, + payload_requested: Arc::new(AtomicBool::new(false)), + } + } +} + +impl PayloadBuilder for FlashblockPayloadBuilder +where + Client: StateProviderFactory + ChainSpecProvider + Clone, + Pool: TransactionPool>, +{ + type Attributes = BerachainPayloadBuilderAttributes; + type BuiltPayload = BerachainBuiltPayload; + + fn try_build( + &self, + args: BuildArguments, + ) -> Result, PayloadBuilderError> { + self.payload_requested.store(false, Ordering::Relaxed); + build_flashblock_payload( + self.evm_config.clone(), + self.client.clone(), + self.pool.clone(), + self.builder_config.clone(), + self.sequencer_config.clone(), + self.publisher.clone(), + self.deadline, + self.payload_requested.clone(), + args, + |attributes| self.pool.best_transactions_with_attributes(attributes), + ) + } + + fn on_missing_payload( + &self, + _args: BuildArguments, + ) -> MissingPayloadBehaviour { + self.payload_requested.store(true, Ordering::Relaxed); + MissingPayloadBehaviour::AwaitInProgress + } + + fn build_empty_payload( + &self, + config: PayloadConfig, + ) -> Result { + warn!(target: "sequencer::builder", "build_empty_payload called, no payload was ready in time"); + let args = BuildArguments::new(Default::default(), config, Default::default(), None); + self.try_build(args)?.into_payload().ok_or_else(|| PayloadBuilderError::MissingPayload) + } +} + +/// Tracks execution data for flashblock emission. +struct FlashblockExecutionTracker { + /// Cumulative receipts for all executed transactions. + receipts: Vec>, + /// Encoded transactions for the current interval. + interval_transactions: Vec, + /// All encoded transactions. + all_transactions: Vec, + /// Cumulative gas used. + cumulative_gas_used: u64, + /// Total fees collected. + total_fees: U256, +} + +impl FlashblockExecutionTracker { + fn new() -> Self { + Self { + receipts: Vec::new(), + interval_transactions: Vec::new(), + all_transactions: Vec::new(), + cumulative_gas_used: 0, + total_fees: U256::ZERO, + } + } + + /// Clear interval transactions for next flashblock. + fn clear_interval(&mut self) { + self.interval_transactions.clear(); + } +} + +/// Build a payload while emitting flashblocks at regular intervals. +#[allow(clippy::too_many_arguments)] +fn build_flashblock_payload( + evm_config: BerachainEvmConfig, + client: Client, + pool: Pool, + builder_config: EthereumBuilderConfig, + sequencer_config: SequencerConfig, + publisher: Arc, + deadline: Duration, + payload_requested: Arc, + args: BuildArguments, + best_txs: F, +) -> Result, PayloadBuilderError> +where + Client: StateProviderFactory + ChainSpecProvider, + Pool: TransactionPool>, + F: FnOnce(BestTransactionsAttributes) -> BestTransactionsIter, +{ + let BuildArguments { mut cached_reads, config, cancel: _, best_payload: _ } = args; + let PayloadConfig { parent_header, attributes } = config; + + let state_provider = client.state_by_block_hash(parent_header.hash())?; + let state = StateProviderDatabase::new(&state_provider); + let mut db = + State::builder().with_database(cached_reads.as_db_mut(state)).with_bundle_update().build(); + + let mut builder = evm_config + .builder_for_next_block( + &mut db, + &parent_header, + BerachainNextBlockEnvAttributes { + timestamp: attributes.timestamp(), + suggested_fee_recipient: attributes.suggested_fee_recipient(), + prev_randao: attributes.prev_randao(), + gas_limit: builder_config.gas_limit(parent_header.gas_limit), + parent_beacon_block_root: attributes.parent_beacon_block_root(), + withdrawals: Some(attributes.withdrawals().clone()), + prev_proposer_pubkey: attributes.prev_proposer_pubkey, + extra_data: Default::default(), + }, + ) + .map_err(PayloadBuilderError::other)?; + + let chain_spec = client.chain_spec(); + let payload_id = attributes.id; + let block_number = parent_header.number + 1; + + info!( + target: "sequencer::builder", + id = %payload_id, + parent_hash = ?parent_header.hash(), + parent_number = parent_header.number, + block_number, + timestamp = attributes.timestamp(), + deadline_ms = deadline.as_millis(), + interval_ms = sequencer_config.interval.as_millis(), + "starting flashblock payload build" + ); + + let block_gas_limit: u64 = builder.evm_mut().block().gas_limit; + let base_fee = builder.evm_mut().block().basefee; + + let mut best_txs = best_txs(BestTransactionsAttributes::new( + base_fee, + builder.evm_mut().block().blob_gasprice().map(|gasprice| gasprice as u64), + )); + + // Apply pre-execution changes (PoL, withdrawals, etc.) + builder.apply_pre_execution_changes().map_err(|err| { + warn!(target: "sequencer::builder", %err, "failed to apply pre-execution changes"); + PayloadBuilderError::Internal(err.into()) + })?; + + // Check if Prague3 is active + if chain_spec.is_prague3_active_at_timestamp(attributes.timestamp()) { + return Err(PayloadBuilderError::Other(Box::from( + "Prague 3 block building is not supported", + ))); + } + + // Build the base payload for flashblock 0 + let base = BerachainFlashblockPayloadBase { + parent_beacon_block_root: attributes.parent_beacon_block_root().unwrap_or_default(), + parent_hash: parent_header.hash(), + fee_recipient: attributes.suggested_fee_recipient(), + prev_randao: attributes.prev_randao(), + block_number, + gas_limit: block_gas_limit, + timestamp: attributes.timestamp(), + extra_data: Bytes::default(), + base_fee_per_gas: U256::from(base_fee), + prev_proposer_pubkey: attributes.prev_proposer_pubkey, + }; + + // Withdrawals go in first flashblock (index 0) + let withdrawals: Vec = attributes.withdrawals().to_vec(); + + let mut tracker = FlashblockExecutionTracker::new(); + let mut blob_sidecars = BlobSidecars::Empty; + let mut flashblock_index = 0u64; + let mut last_flashblock_time = Instant::now(); + let interval = sequencer_config.interval; + let build_start_time = Instant::now(); + + // Helper to emit flashblock. Per BRIP-0007, no state root computation needed. + let emit = |flashblock_index: u64, tracker: &FlashblockExecutionTracker, is_last: bool| { + emit_flashblock( + &publisher, + payload_id, + flashblock_index, + &base, + flashblock_index == 0, + tracker, + block_number, + &sequencer_config.signer, + &withdrawals, + is_last, + ); + }; + + // Main transaction execution loop with flashblock emission. + // Flashblocks are emitted at regular intervals (~200ms) regardless of transaction activity. + // Empty flashblocks serve as heartbeats, allowing subscribers to detect liveness and + // track the current state even when no transactions are being processed. + loop { + if payload_requested.load(Ordering::Relaxed) { + info!( + target: "sequencer::builder", + id = %payload_id, + flashblock_index, + total_txs = tracker.all_transactions.len(), + cumulative_gas = tracker.cumulative_gas_used, + "payload build cancelled (getPayload called), finalizing" + ); + break; + } + + // Check if deadline exceeded (--builder.deadline). + if build_start_time.elapsed() >= deadline { + info!( + target: "sequencer::builder", + id = %payload_id, + flashblock_index, + total_txs = tracker.all_transactions.len(), + cumulative_gas = tracker.cumulative_gas_used, + deadline_ms = deadline.as_millis(), + "payload build deadline reached, finalizing" + ); + break; + } + + // Check if gas limit reached (use a small buffer to account for minimum tx gas) + if tracker.cumulative_gas_used + 21_000 > block_gas_limit { + info!( + target: "sequencer::builder", + id = %payload_id, + flashblock_index, + total_txs = tracker.all_transactions.len(), + cumulative_gas = tracker.cumulative_gas_used, + block_gas_limit, + "block gas limit reached, finalizing" + ); + break; + } + + // Emit flashblock at regular intervals (may be empty, serving as heartbeat) + if last_flashblock_time.elapsed() >= interval { + emit(flashblock_index, &tracker, false); + flashblock_index += 1; + tracker.clear_interval(); + last_flashblock_time = Instant::now(); + } + + // Try to get the next transaction + let Some(pool_tx) = best_txs.next() else { + // No transactions available, sleep briefly to prevent busy-waiting and check again + std::thread::sleep(Duration::from_millis(10)); + continue; + }; + + // Check gas limit + if tracker.cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { + best_txs.mark_invalid( + &pool_tx, + &InvalidPoolTransactionError::ExceedsGasLimit(pool_tx.gas_limit(), block_gas_limit), + ); + continue; + } + + let tx = pool_tx.to_consensus(); + let tx_hash = *tx.hash(); + + // Fetch blob sidecar before execution so we can skip the tx if it's missing + let mut blob_tx_sidecar = None; + if tx.as_eip4844().is_some() { + match pool.get_blob(tx_hash).map_err(PayloadBuilderError::other)? { + Some(sidecar) => { + blob_tx_sidecar = Some(sidecar); + } + None => { + best_txs.mark_invalid( + &pool_tx, + &InvalidPoolTransactionError::Eip4844( + Eip4844PoolTransactionError::MissingEip4844BlobSidecar, + ), + ); + continue; + } + } + } + + // Execute the transaction and capture the result + let mut execution_logs = Vec::new(); + let mut tx_success = false; + + let result = builder.execute_transaction_with_commit_condition(tx.clone(), |exec_result| { + tx_success = exec_result.is_success(); + if tx_success { + execution_logs = exec_result.logs().to_vec(); + } + CommitChanges::Yes + }); + + let gas_used = match result { + Ok(Some(gas)) => gas, + Ok(None) => continue, // Transaction was not committed + Err(BlockExecutionError::Validation(BlockValidationError::InvalidTx { + error, .. + })) => { + if error.is_nonce_too_low() { + trace!(target: "sequencer::builder", %error, ?tx, "skipping nonce too low transaction"); + } else { + trace!(target: "sequencer::builder", %error, ?tx, "skipping invalid transaction"); + best_txs.mark_invalid( + &pool_tx, + &InvalidPoolTransactionError::Consensus( + InvalidTransactionError::TxTypeNotSupported, + ), + ); + } + continue; + } + Err(err) => return Err(PayloadBuilderError::evm(err)), + }; + + // Build receipt from execution result + let receipt = Receipt { + tx_type: tx.tx_type(), + success: tx_success, + cumulative_gas_used: tracker.cumulative_gas_used + gas_used, + logs: execution_logs, + }; + tracker.receipts.push(receipt); + + if let Some(sidecar) = blob_tx_sidecar { + blob_sidecars.push_sidecar_variant(sidecar.as_ref().clone()); + } + + // Encode transaction + let tx_bytes = Bytes::from(tx.inner().encoded_2718()); + + // Update tracking + let miner_fee = + tx.effective_tip_per_gas(base_fee).expect("fee is always valid; execution succeeded"); + tracker.total_fees += U256::from(miner_fee) * U256::from(gas_used); + tracker.cumulative_gas_used += gas_used; + + tracker.all_transactions.push(tx_bytes.clone()); + tracker.interval_transactions.push(tx_bytes); + + trace!( + target: "sequencer::builder", + tx_hash = ?tx_hash, + gas_used, + cumulative_gas_used = tracker.cumulative_gas_used, + "executed transaction" + ); + } + + // Always emit the final flashblock marked as last, even if empty. + // This signals to RPC nodes that no more flashblocks will arrive for this payload. + emit(flashblock_index, &tracker, true); + + // Finalize the block + let BlockBuilderOutcome { execution_result, block, .. } = builder.finish(&state_provider)?; + + let requests = chain_spec + .is_prague_active_at_timestamp(attributes.timestamp()) + .then_some(execution_result.requests); + + let sealed_block = Arc::new(block.sealed_block().clone()); + info!( + target: "sequencer::builder", + id = %payload_id, + block_hash = %sealed_block.hash(), + block_number = sealed_block.number, + total_transactions = tracker.all_transactions.len(), + total_fees = %tracker.total_fees, + "sealed flashblock payload ready for getPayload" + ); + + let payload = + BerachainBuiltPayload::new(payload_id, sealed_block, tracker.total_fees, requests) + .with_sidecars(blob_sidecars); + + Ok(BuildOutcome::Better { payload, cached_reads }) +} + +#[allow(clippy::too_many_arguments)] +fn emit_flashblock( + publisher: &WebSocketPublisher, + payload_id: reth::rpc::types::engine::PayloadId, + index: u64, + base: &BerachainFlashblockPayloadBase, + include_base_in_payload: bool, + tracker: &FlashblockExecutionTracker, + block_number: u64, + signer: &FlashblockSigner, + withdrawals: &[Withdrawal], + is_last: bool, +) { + // Withdrawals are included in first flashblock only (index 0) + let diff_withdrawals = if include_base_in_payload { withdrawals.to_vec() } else { vec![] }; + + // Flashblocks just contain transactions, no computed roots + let diff = BerachainFlashblockPayloadDiff { + transactions: tracker.interval_transactions.clone(), + withdrawals: diff_withdrawals, + }; + + // Sign over transactions hash + let tx_hash = compute_transactions_hash(&tracker.interval_transactions); + let signature = signer.sign_flashblock(block_number, payload_id, index, tx_hash); + + let flashblock = BerachainFlashblockPayload { + payload_id, + index, + base: if include_base_in_payload { Some(base.clone()) } else { None }, + diff, + metadata: BerachainFlashblockPayloadMetadata { block_number }, + signature, + is_last, + }; + + match publisher.publish(&flashblock) { + Ok(count) => { + debug!( + target: "sequencer::builder", + payload_id = %payload_id, + index, + block_number, + transactions = tracker.interval_transactions.len(), + subscribers = count, + is_last, + "emitted flashblock" + ); + } + Err(e) => { + warn!( + target: "sequencer::builder", + payload_id = %payload_id, + index, + error = %e, + "failed to publish flashblock" + ); + } + } +} diff --git a/src/sequencer/mod.rs b/src/sequencer/mod.rs new file mode 100644 index 00000000..af3d8061 --- /dev/null +++ b/src/sequencer/mod.rs @@ -0,0 +1,33 @@ +//! Berachain sequencer module for flashblock production. +//! +//! This module provides the sequencer functionality that produces flashblocks +//! at regular intervals (~200ms) and publishes them via WebSocket. + +mod builder; +mod publisher; +pub mod signing; + +pub use builder::{FlashblockPayloadBuilder, FlashblockPayloadServiceBuilder}; +pub use publisher::WebSocketPublisher; +pub use signing::{BlsPublicKeyBytes, BlsSignature, FlashblockSigner, SigningError}; +pub use tokio_util::sync::CancellationToken; + +use std::{net::SocketAddr, sync::Arc, time::Duration}; + +/// Configuration for the sequencer. +#[derive(Debug, Clone)] +pub struct SequencerConfig { + /// Flashblock emission interval. + pub interval: Duration, + /// WebSocket address for flashblock publishing. + pub ws_addr: SocketAddr, + /// BLS signer for flashblock signing. + pub signer: Arc, +} + +impl SequencerConfig { + /// Create a new sequencer config with the required signer. + pub fn new(interval_ms: u64, ws_addr: SocketAddr, signer: FlashblockSigner) -> Self { + Self { interval: Duration::from_millis(interval_ms), ws_addr, signer: Arc::new(signer) } + } +} diff --git a/src/sequencer/publisher.rs b/src/sequencer/publisher.rs new file mode 100644 index 00000000..3f4a97f1 --- /dev/null +++ b/src/sequencer/publisher.rs @@ -0,0 +1,202 @@ +//! WebSocket publisher for broadcasting flashblocks to subscribers. + +use crate::flashblocks::BerachainFlashblockPayload; +use futures_util::{SinkExt, StreamExt}; +use std::{ + io, + net::SocketAddr, + sync::{ + Arc, + atomic::{AtomicUsize, Ordering}, + }, + time::Duration, +}; +use tokio::{ + net::{TcpListener, TcpStream}, + sync::broadcast, +}; +use tokio_tungstenite::{accept_async, tungstenite::Message}; +use tokio_util::sync::CancellationToken; +use tracing::{debug, error, info, warn}; + +/// Capacity for the flashblock broadcast channel. +const FLASHBLOCK_CHANNEL_CAPACITY: usize = 20; + +/// Maximum concurrent WebSocket connections. +const MAX_CONNECTIONS: usize = 256; + +/// Timeout for WebSocket handshake. +const HANDSHAKE_TIMEOUT: Duration = Duration::from_secs(10); + +/// WebSocket publisher that broadcasts flashblocks to all connected clients. +#[derive(Debug)] +pub struct WebSocketPublisher { + sender: broadcast::Sender, + address: SocketAddr, + subscriber_count: Arc, +} + +impl WebSocketPublisher { + /// Create a new WebSocket publisher. + pub fn new(address: SocketAddr) -> Self { + let (sender, _) = broadcast::channel(FLASHBLOCK_CHANNEL_CAPACITY); + Self { sender, address, subscriber_count: Arc::new(AtomicUsize::new(0)) } + } + + /// Get the number of active subscribers. + pub fn subscriber_count(&self) -> usize { + self.subscriber_count.load(Ordering::Relaxed) + } + + /// Get a receiver for flashblock messages. + pub fn subscribe(&self) -> broadcast::Receiver { + self.sender.subscribe() + } + + /// Publish a flashblock to all subscribers. + pub fn publish(&self, payload: &BerachainFlashblockPayload) -> io::Result { + let json = serde_json::to_string(payload) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + match self.sender.send(json) { + Ok(count) => { + debug!( + target: "sequencer::publisher", + payload_id = %payload.payload_id, + index = payload.index, + subscribers = count, + "published flashblock" + ); + Ok(count) + } + Err(_) => { + // No subscribers - this is fine + Ok(0) + } + } + } + + /// Bind the WebSocket server to its configured address. + pub async fn bind(&self) -> eyre::Result { + let listener = TcpListener::bind(self.address).await?; + info!( + target: "sequencer::publisher", + address = %self.address, + "flashblock WebSocket server started" + ); + Ok(listener) + } + + /// Run the WebSocket server until cancelled. + pub async fn run(&self, listener: TcpListener, cancel: CancellationToken) -> eyre::Result<()> { + loop { + tokio::select! { + _ = cancel.cancelled() => { + info!(target: "sequencer::publisher", "shutting down WebSocket server"); + break; + } + result = listener.accept() => { + match result { + Ok((stream, addr)) => { + if self.subscriber_count.load(Ordering::Relaxed) >= MAX_CONNECTIONS { + warn!(target: "sequencer::publisher", %addr, "connection limit reached"); + continue; + } + let rx = self.sender.subscribe(); + let count = self.subscriber_count.clone(); + let conn_cancel = cancel.clone(); + tokio::spawn(async move { + if let Err(e) = handle_connection(stream, addr, rx, count, conn_cancel).await { + warn!(target: "sequencer::publisher", %addr, error = %e, "connection error"); + } + }); + } + Err(e) => { + error!(target: "sequencer::publisher", error = %e, "failed to accept connection"); + } + } + } + } + } + + Ok(()) + } +} + +async fn handle_connection( + stream: TcpStream, + addr: SocketAddr, + mut rx: broadcast::Receiver, + subscriber_count: Arc, + cancel: CancellationToken, +) -> eyre::Result<()> { + let ws_stream = tokio::time::timeout(HANDSHAKE_TIMEOUT, accept_async(stream)) + .await + .map_err(|_| eyre::eyre!("handshake timeout"))??; + let (mut write, mut read) = ws_stream.split(); + + subscriber_count.fetch_add(1, Ordering::Relaxed); + info!( + target: "sequencer::publisher", + %addr, + count = subscriber_count.load(Ordering::Relaxed), + "client connected" + ); + + // Handle incoming messages and broadcast outgoing flashblocks + loop { + tokio::select! { + _ = cancel.cancelled() => { + // Send close frame before shutting down + let _ = write.send(Message::Close(None)).await; + break; + } + // Forward flashblocks to the client + result = rx.recv() => { + match result { + Ok(json) => { + if let Err(e) = write.send(Message::Text(json.into())).await { + debug!(target: "sequencer::publisher", %addr, error = %e, "failed to send message"); + break; + } + } + Err(broadcast::error::RecvError::Lagged(n)) => { + warn!(target: "sequencer::publisher", %addr, skipped = n, "client lagging"); + } + Err(broadcast::error::RecvError::Closed) => { + break; + } + } + } + // Handle client messages (ping/pong, close) + msg = read.next() => { + match msg { + Some(Ok(Message::Ping(data))) => { + if let Err(e) = write.send(Message::Pong(data)).await { + debug!(target: "sequencer::publisher", %addr, error = %e, "failed to send pong"); + break; + } + } + Some(Ok(Message::Close(_))) | None => { + break; + } + Some(Err(e)) => { + debug!(target: "sequencer::publisher", %addr, error = %e, "websocket error"); + break; + } + _ => {} + } + } + } + } + + subscriber_count.fetch_sub(1, Ordering::Relaxed); + info!( + target: "sequencer::publisher", + %addr, + count = subscriber_count.load(Ordering::Relaxed), + "client disconnected" + ); + + Ok(()) +} diff --git a/src/sequencer/signing.rs b/src/sequencer/signing.rs new file mode 100644 index 00000000..6edaf8c9 --- /dev/null +++ b/src/sequencer/signing.rs @@ -0,0 +1,223 @@ +//! BLS signing for flashblock preconfirmations. +//! +//! Implements the signing scheme for Berachain flashblocks: +//! `message = keccak256(domain || block_number || payload_id || index || diff_hash)` +//! where `domain = keccak256("BerachainPreconf-v1" || chain_id)` + +use alloy_primitives::{B256, keccak256}; +use blst::min_pk::{PublicKey, SecretKey, Signature}; +use reth::rpc::types::engine::PayloadId; +use std::path::Path; + +/// BLS signature bytes (96 bytes for BLS12-381 signatures). +pub type BlsSignature = [u8; 96]; + +/// BLS public key bytes (48 bytes for BLS12-381 public keys). +pub type BlsPublicKeyBytes = [u8; 48]; + +/// Domain separator version string. +const DOMAIN_VERSION: &[u8] = b"BerachainPreconf-v1"; + +/// BLS DST (Domain Separation Tag) matching beacon-kit's Proof of Possession scheme. +const BLS_DST: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_"; + +/// Errors that can occur during signing operations. +#[derive(Debug, thiserror::Error)] +pub enum SigningError { + #[error("invalid secret key")] + InvalidSecretKey, + #[error("invalid public key")] + InvalidPublicKey, + #[error("invalid signature")] + InvalidSignature, + #[error("failed to read key file: {0}")] + KeyFileError(#[from] std::io::Error), + #[error("invalid key format: {0}")] + InvalidKeyFormat(String), +} + +/// BLS signer for flashblock preconfirmations. +#[derive(Clone)] +pub struct FlashblockSigner { + secret_key: SecretKey, + public_key: PublicKey, + domain: B256, +} + +impl std::fmt::Debug for FlashblockSigner { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FlashblockSigner") + .field("public_key", &hex::encode(self.public_key.to_bytes())) + .field("domain", &self.domain) + .finish() + } +} + +impl FlashblockSigner { + /// Create a new signer from a secret key and chain ID. + pub fn new(secret_key: SecretKey, chain_id: u64) -> Self { + let public_key = secret_key.sk_to_pk(); + let domain = compute_domain(chain_id); + Self { secret_key, public_key, domain } + } + + /// Create a signer from a hex-encoded secret key. + pub fn from_hex(hex_key: &str, chain_id: u64) -> Result { + let key_bytes = hex::decode(hex_key.trim_start_matches("0x")) + .map_err(|e| SigningError::InvalidKeyFormat(e.to_string()))?; + + if key_bytes.len() != 32 { + return Err(SigningError::InvalidKeyFormat(format!( + "expected 32 bytes, got {}", + key_bytes.len() + ))); + } + + let secret_key = + SecretKey::from_bytes(&key_bytes).map_err(|_| SigningError::InvalidSecretKey)?; + + Ok(Self::new(secret_key, chain_id)) + } + + /// Load a signer from a key file (hex-encoded secret key). + pub fn from_file(path: impl AsRef, chain_id: u64) -> Result { + let contents = std::fs::read_to_string(path)?; + Self::from_hex(contents.trim(), chain_id) + } + + /// Get the public key bytes. + pub fn public_key_bytes(&self) -> BlsPublicKeyBytes { + self.public_key.to_bytes() + } + + /// Sign a flashblock. + pub fn sign_flashblock( + &self, + block_number: u64, + payload_id: PayloadId, + index: u64, + diff_hash: B256, + ) -> BlsSignature { + let message = + compute_signing_message(self.domain, block_number, payload_id, index, diff_hash); + + let signature = self.secret_key.sign(&message, BLS_DST, &[]); + signature.to_bytes() + } + + /// Verify a flashblock signature. + pub fn verify( + public_key: &BlsPublicKeyBytes, + signature: &BlsSignature, + chain_id: u64, + block_number: u64, + payload_id: PayloadId, + index: u64, + diff_hash: B256, + ) -> Result { + let pk = PublicKey::from_bytes(public_key).map_err(|_| SigningError::InvalidPublicKey)?; + let sig = Signature::from_bytes(signature).map_err(|_| SigningError::InvalidSignature)?; + + let domain = compute_domain(chain_id); + let message = compute_signing_message(domain, block_number, payload_id, index, diff_hash); + + let result = sig.verify(true, &message, BLS_DST, &[], &pk, true); + + Ok(result == blst::BLST_ERROR::BLST_SUCCESS) + } +} + +/// Compute the domain separator for the given chain ID. +fn compute_domain(chain_id: u64) -> B256 { + let mut data = Vec::with_capacity(DOMAIN_VERSION.len() + 8); + data.extend_from_slice(DOMAIN_VERSION); + data.extend_from_slice(&chain_id.to_be_bytes()); + keccak256(&data) +} + +/// Compute the message to sign for a flashblock. +fn compute_signing_message( + domain: B256, + block_number: u64, + payload_id: PayloadId, + index: u64, + diff_hash: B256, +) -> [u8; 32] { + let mut data = Vec::with_capacity(32 + 8 + 8 + 8 + 32); + data.extend_from_slice(domain.as_slice()); + data.extend_from_slice(&block_number.to_be_bytes()); + data.extend_from_slice(payload_id.0.as_slice()); + data.extend_from_slice(&index.to_be_bytes()); + data.extend_from_slice(diff_hash.as_slice()); + keccak256(&data).0 +} + +/// Compute the hash of flashblock transactions for signing. +pub fn compute_transactions_hash(transactions: &[impl AsRef<[u8]>]) -> B256 { + let mut data = Vec::new(); + for tx in transactions { + let tx_bytes = tx.as_ref(); + data.extend_from_slice(&(tx_bytes.len() as u64).to_be_bytes()); + data.extend_from_slice(tx_bytes); + } + keccak256(&data) +} + +#[cfg(test)] +mod tests { + use super::*; + + const CHAIN_ID: u64 = 80094; + const BLOCK_NUMBER: u64 = 100; + const INDEX: u64 = 0; + + fn test_signer() -> FlashblockSigner { + let seed = [1u8; 32]; + FlashblockSigner::new(SecretKey::key_gen(&seed, &[]).unwrap(), CHAIN_ID) + } + + #[test] + fn test_sign_and_verify() { + let signer = test_signer(); + let payload_id = PayloadId::new([1u8; 8]); + let tx_hash = B256::repeat_byte(0x42); + + let signature = signer.sign_flashblock(BLOCK_NUMBER, payload_id, INDEX, tx_hash); + + let valid = FlashblockSigner::verify( + &signer.public_key_bytes(), + &signature, + CHAIN_ID, + BLOCK_NUMBER, + payload_id, + INDEX, + tx_hash, + ) + .unwrap(); + + assert!(valid); + } + + #[test] + fn test_invalid_signature_fails_verification() { + let signer = test_signer(); + let payload_id = PayloadId::new([1u8; 8]); + let tx_hash = B256::repeat_byte(0x42); + + let signature = signer.sign_flashblock(BLOCK_NUMBER, payload_id, INDEX, tx_hash); + + let wrong_hash = B256::repeat_byte(0x43); + let valid = FlashblockSigner::verify( + &signer.public_key_bytes(), + &signature, + CHAIN_ID, + BLOCK_NUMBER, + payload_id, + INDEX, + wrong_hash, + ) + .unwrap(); + + assert!(!valid); + } +} diff --git a/tests/e2e/flashblocks.rs b/tests/e2e/flashblocks.rs new file mode 100644 index 00000000..29723327 --- /dev/null +++ b/tests/e2e/flashblocks.rs @@ -0,0 +1,272 @@ +//! E2E tests for flashblock integration. +//! +//! These tests verify the complete flow: flashblock stream → FlashBlockService → +//! pending block construction → RPC receipt availability. + +use crate::e2e::{setup_test_boilerplate, test_signer}; +use alloy_consensus::BlockHeader; +use alloy_eips::eip2718::Encodable2718; +use alloy_primitives::{Address, B256, Bytes, U256}; +use alloy_provider::Provider; +use bera_reth::{ + engine::validator::BerachainEngineValidatorBuilder, + flashblocks::{ + BerachainFlashblockPayload, BerachainFlashblockPayloadBase, BerachainFlashblockPayloadDiff, + BerachainFlashblockPayloadMetadata, FlashBlockCompleteSequence, FlashBlockService, + FlashblocksListeners, + }, + node::BerachainNode, + primitives::{BerachainPrimitives, header::BlsPublicKey}, + rpc::{BerachainAddOns, BerachainEthApiBuilder}, +}; +use reth::{providers::BlockReaderIdExt, rpc::types::engine::PayloadId}; +use reth_chainspec::EthChainSpec; +use reth_e2e_test_utils::transaction::TransactionTestContext; +use reth_node_builder::{Node, NodeBuilder, NodeHandle}; +use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig}; +use std::{pin::Pin, sync::Arc, task::Poll, time::Duration}; +use tokio::sync::{broadcast, watch}; + +fn create_test_flashblock( + index: u64, + block_number: u64, + payload_id: PayloadId, + parent_hash: B256, + timestamp: u64, +) -> BerachainFlashblockPayload { + let base = if index == 0 { + Some(BerachainFlashblockPayloadBase { + parent_beacon_block_root: B256::random(), + parent_hash, + fee_recipient: Address::random(), + prev_randao: B256::random(), + block_number, + gas_limit: 30_000_000, + timestamp, + extra_data: Bytes::default(), + base_fee_per_gas: U256::from(1_000_000_000u64), + prev_proposer_pubkey: Some(BlsPublicKey::random()), + }) + } else { + None + }; + + BerachainFlashblockPayload { + payload_id, + index, + base, + diff: BerachainFlashblockPayloadDiff { transactions: vec![], withdrawals: vec![] }, + metadata: BerachainFlashblockPayloadMetadata { block_number }, + signature: [0u8; 96], + is_last: false, + } +} + +struct MockFlashblockStream { + rx: tokio::sync::mpsc::Receiver, +} + +impl futures_util::Stream for MockFlashblockStream { + type Item = eyre::Result; + + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> Poll> { + match Pin::new(&mut self.rx).poll_recv(cx) { + Poll::Ready(Some(fb)) => Poll::Ready(Some(Ok(fb))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } + } +} + +/// Tests the complete flashblock → pending block → RPC receipt flow. +/// +/// # Data Flow +/// +/// This test wires together two separate systems that need to communicate: +/// +/// ```text +/// TEST HARNESS NODE (RPC) +/// ──────────── ────────── +/// +/// fb_tx ──────► MockFlashblockStream +/// │ +/// ▼ +/// FlashBlockService +/// │ │ +/// │ ▼ +/// │ service.subscribe_in_progress() +/// │ │ +/// ▼ │ (test manually bridges) +/// pending_tx ─────────┼──────────────────► pending_rx ──► RPC EthApi +/// │ (reads pending block) +/// ▼ +/// in_progress_tx ─────────► in_progress_rx ──► RPC EthApi +/// (knows build is active) +/// ``` +/// +/// The key insight is that `FlashblocksListeners` holds the rx ends of channels, which are +/// passed into the node at build time. The test harness holds the tx ends and must: +/// 1. Pass `pending_tx` to `service.run()` so the service can publish pending blocks +/// 2. Manually forward `in_progress` state from service → node (the service has its own internal +/// channel, so we subscribe and forward to the node's channel) +/// +/// When an RPC call like `eth_getTransactionReceipt` comes in, the EthApi checks +/// `pending_rx` for a flashblock-derived pending block containing that transaction. +#[tokio::test] +async fn test_rpc_returns_flashblock_pending_receipt() -> eyre::Result<()> { + let (executor, chain_spec) = setup_test_boilerplate().await?; + + // Channels that connect FlashBlockService (producer) to RPC EthApi (consumer). + // The service writes to pending_tx, the RPC reads from pending_rx. + let (pending_tx, pending_rx) = watch::channel(None); + + // in_progress channel tells RPC layer that a flashblock build is active. + // We manually bridge from service's internal channel to this one (see below). + let (in_progress_tx, in_progress_rx) = watch::channel(None); + + // These broadcast channels are for external listeners (e.g., websocket subscribers). + // Not used in this test but required by FlashblocksListeners constructor. + let (unused_sequence_tx, _) = + broadcast::channel::>(1); + let (unused_received_tx, _) = broadcast::channel::>(1); + + // FlashblocksListeners bundles the rx ends for the node's RPC layer. + let listeners: FlashblocksListeners = + FlashblocksListeners::new( + pending_rx, + unused_sequence_tx, + in_progress_rx, + unused_received_tx, + ); + + let eth_api_builder = BerachainEthApiBuilder::default().with_flashblocks_listeners(listeners); + let add_ons = BerachainAddOns::<_, _, BerachainEngineValidatorBuilder>::new(eth_api_builder); + + let node_config = NodeConfig::new(chain_spec.clone()) + .with_unused_ports() + .with_rpc(RpcServerArgs::default().with_unused_ports().with_http()); + + let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config) + .testing_node(executor.clone()) + .with_types::() + .with_components(BerachainNode::default().components_builder()) + .with_add_ons(add_ons) + .launch() + .await?; + + // Create the flashblock input channel. Test sends flashblocks via fb_tx. + let (fb_tx, fb_rx) = tokio::sync::mpsc::channel::(128); + let stream = MockFlashblockStream { rx: fb_rx }; + + let service = FlashBlockService::new( + stream, + node.evm_config.clone(), + node.provider().clone(), + executor.clone(), + false, + ); + + // Subscribe to service's internal in_progress channel so we can forward to node's channel. + let mut service_in_progress_rx = service.subscribe_in_progress(); + + // Start the service, passing pending_tx so it can publish pending blocks to the RPC layer. + executor.spawn_critical_task( + "flashblock-service", + Box::pin(async move { + service.run(pending_tx).await; + }), + ); + + let latest = node.provider().latest_header()?.expect("should have genesis"); + let latest_hash = latest.hash(); + let next_block = latest.number() + 1; + let next_timestamp = latest.timestamp() + 2; + + let signer = test_signer()?; + let chain_id = chain_spec.chain_id(); + let tx = TransactionTestContext::transfer_tx(chain_id, signer).await; + let tx_bytes = Bytes::from(tx.encoded_2718()); + let tx_hash = *tx.tx_hash(); + + let payload_id = PayloadId::new([1u8; 8]); + let mut fb0 = create_test_flashblock(0, next_block, payload_id, latest_hash, next_timestamp); + fb0.diff.transactions = vec![tx_bytes]; + + // Inject the flashblock into the service via our mock stream. + fb_tx.send(fb0).await?; + + // Wait for service to signal it's building, then forward that state to the node's channel. + tokio::time::timeout(Duration::from_millis(500), service_in_progress_rx.changed()).await??; + in_progress_tx.send(*service_in_progress_rx.borrow())?; + + // Give the service time to build and publish the pending block. + tokio::time::sleep(Duration::from_millis(100)).await; + + // Query the RPC - should find the transaction in the flashblock-derived pending block. + let rpc_url = + format!("http://127.0.0.1:{}", node.rpc_server_handle().http_local_addr().unwrap().port()); + let rpc_client = alloy_provider::ProviderBuilder::new().connect(&rpc_url).await?; + + let receipt = rpc_client.get_transaction_receipt(tx_hash).await?; + + assert!( + receipt.is_some(), + "Transaction receipt should be available from flashblock pending state" + ); + assert_eq!(receipt.unwrap().transaction_hash, tx_hash); + + Ok(()) +} + +/// Tests that flashblocks with invalid parent hashes are rejected. +#[tokio::test] +async fn test_flashblock_rejects_invalid_parent_hash() -> eyre::Result<()> { + let (executor, chain_spec) = setup_test_boilerplate().await?; + + let node_config = NodeConfig::new(chain_spec.clone()) + .with_unused_ports() + .with_rpc(RpcServerArgs::default().with_unused_ports().with_http()); + + let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config) + .testing_node(executor.clone()) + .node(BerachainNode::default()) + .launch() + .await?; + + let (fb_tx, fb_rx) = tokio::sync::mpsc::channel::(128); + let stream = MockFlashblockStream { rx: fb_rx }; + + let service = FlashBlockService::new( + stream, + node.evm_config.clone(), + node.provider().clone(), + executor.clone(), + false, + ); + + let (pending_tx, mut pending_rx) = watch::channel(None); + executor.spawn_critical_task( + "flashblock-service", + Box::pin(async move { + service.run(pending_tx).await; + }), + ); + + let latest = node.provider().latest_header()?.expect("should have genesis"); + let next_block = latest.number() + 1; + let next_timestamp = latest.timestamp() + 2; + + let wrong_parent_hash = B256::random(); + let payload_id = PayloadId::new([1u8; 8]); + let fb = create_test_flashblock(0, next_block, payload_id, wrong_parent_hash, next_timestamp); + + fb_tx.send(fb).await?; + + let result = tokio::time::timeout(Duration::from_millis(200), pending_rx.changed()).await; + assert!(result.is_err(), "Should not build pending block with wrong parent hash"); + + Ok(()) +} diff --git a/tests/e2e/mod.rs b/tests/e2e/mod.rs index 5d6c06bd..88aa4326 100644 --- a/tests/e2e/mod.rs +++ b/tests/e2e/mod.rs @@ -12,6 +12,7 @@ use reth_payload_primitives::PayloadBuilderAttributes; use std::{str::FromStr, sync::Arc}; pub mod coinbase_system_state_change_test; +pub mod flashblocks; pub mod gas_limit_regression_test; pub mod pol_revert_test; pub mod transaction_tests;