From 93e72896e4226d57a0af8931b90cfcc60560f2a5 Mon Sep 17 00:00:00 2001 From: Noah Citron Date: Wed, 2 Oct 2024 18:35:20 -0400 Subject: [PATCH] feat: opstack support (#382) * add lc server * add client * add builder * add config * add server cli * fix rpc socket defaults * change rpc defaults * better block safety --- Cargo.lock | 2023 +++++++++++++++++++++-- Cargo.toml | 3 + cli/Cargo.toml | 3 + cli/src/main.rs | 156 +- core/src/execution/mod.rs | 2 +- core/src/execution/state.rs | 2 +- ethereum/src/config/mod.rs | 5 +- ethereum/src/config/networks.rs | 2 +- opstack/Cargo.toml | 46 + opstack/bin/server.rs | 49 + opstack/src/builder.rs | 96 ++ opstack/src/config.rs | 126 ++ opstack/src/consensus.rs | 306 ++++ opstack/src/lib.rs | 76 + opstack/src/server/mod.rs | 88 + opstack/src/server/net/block_handler.rs | 43 + opstack/src/server/net/bootnodes.rs | 24 + opstack/src/server/net/discovery.rs | 137 ++ opstack/src/server/net/gossip.rs | 233 +++ opstack/src/server/net/mod.rs | 4 + opstack/src/spec.rs | 307 ++++ opstack/src/types.rs | 36 + 22 files changed, 3629 insertions(+), 138 deletions(-) create mode 100644 opstack/Cargo.toml create mode 100644 opstack/bin/server.rs create mode 100644 opstack/src/builder.rs create mode 100644 opstack/src/config.rs create mode 100644 opstack/src/consensus.rs create mode 100644 opstack/src/lib.rs create mode 100644 opstack/src/server/mod.rs create mode 100644 opstack/src/server/net/block_handler.rs create mode 100644 opstack/src/server/net/bootnodes.rs create mode 100644 opstack/src/server/net/discovery.rs create mode 100644 opstack/src/server/net/gossip.rs create mode 100644 opstack/src/server/net/mod.rs create mode 100644 opstack/src/spec.rs create mode 100644 opstack/src/types.rs diff --git a/Cargo.lock b/Cargo.lock index 13ea34b2..c1c79417 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,41 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aead" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.8.11" @@ -148,7 +183,7 @@ dependencies = [ "itoa", "serde", "serde_json", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -313,7 +348,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tracing", ] @@ -359,7 +394,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.4.13", "tracing", "url", ] @@ -530,7 +565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbcba3ca07cf7975f15d871b721fb18031eec8bce51103907f6dcce00b255d98" dependencies = [ "serde", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -560,7 +595,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower", + "tower 0.4.13", "tracing", "url", ] @@ -575,7 +610,7 @@ dependencies = [ "alloy-transport", "reqwest", "serde_json", - "tower", + "tower 0.4.13", "tracing", "url", ] @@ -817,6 +852,12 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.5.2" @@ -829,13 +870,88 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "asn1-rs" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fd5ddaf0351dff5b8da21b2fb4ff8e08ddd02857f0bf69c47639106c0fff0" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "asn1_der" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" + +[[package]] +name = "async-io" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444b0228950ee6501b3568d3c93bf1176a1fdbc3b758dcd9475046d30f4dc7e8" +dependencies = [ + "async-lock 3.4.0", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "async-lock" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "event-listener", + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.3.1", + "event-listener-strategy", + "pin-project-lite", ] [[package]] @@ -882,6 +998,19 @@ dependencies = [ "rustc_version 0.4.1", ] +[[package]] +name = "asynchronous-codec" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + [[package]] name = "atomic" version = "0.6.0" @@ -924,6 +1053,61 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "axum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.4.1", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower 0.5.1", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.1", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.74" @@ -939,6 +1123,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + [[package]] name = "base16ct" version = "0.2.0" @@ -1037,6 +1227,15 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest 0.10.7", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -1081,6 +1280,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bs58" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" + [[package]] name = "bstr" version = "1.10.0" @@ -1172,6 +1377,30 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chacha20poly1305" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", +] + [[package]] name = "chrono" version = "0.4.38" @@ -1214,6 +1443,17 @@ dependencies = [ "half", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", + "zeroize", +] + [[package]] name = "clap" version = "4.5.18" @@ -1266,6 +1506,15 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-hex" version = "1.12.0" @@ -1343,6 +1592,15 @@ dependencies = [ "libc", ] +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "cpufeatures" version = "0.2.14" @@ -1449,19 +1707,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.4", "typenum", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "ctrlc" version = "3.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90eeab0aa92f3f9b4e87f258c72b139c207d251f9cbc1080a0086b86a8870dd3" dependencies = [ - "nix", + "nix 0.29.0", "windows-sys 0.59.0", ] +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version 0.4.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "darling" version = "0.13.4" @@ -1542,7 +1860,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core", + "parking_lot_core 0.9.10", ] [[package]] @@ -1551,6 +1869,36 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "data-encoding-macro" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1559b6cba622276d6d63706db152618eeb15b89b3e4041446b05876e352e639" +dependencies = [ + "data-encoding", + "data-encoding-macro-internal", +] + +[[package]] +name = "data-encoding-macro-internal" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332d754c0af53bc87c108fed664d121ecf59207ec4196041f04d6ab9002ad33f" +dependencies = [ + "data-encoding", + "syn 1.0.109", +] + +[[package]] +name = "delay_map" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4355c25cbf99edcb6b4a0e906f6bdc6956eda149e84455bea49696429b2f8e8" +dependencies = [ + "futures", + "tokio-util", +] + [[package]] name = "der" version = "0.7.9" @@ -1561,6 +1909,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "der-parser" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd676fbbab537128ef0278adb5576cf363cff6aa22a7b24effe97347cfab61e" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1644,7 +2006,49 @@ dependencies = [ ] [[package]] -name = "dlib" +name = "discv5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f569b8c367554666c8652305621e8bae3634a2ff5c6378081d5bd8c399c99f23" +dependencies = [ + "aes", + "aes-gcm", + "alloy-rlp", + "arrayvec 0.7.6", + "ctr", + "delay_map", + "enr", + "fnv", + "futures", + "hashlink", + "hex", + "hkdf", + "lazy_static", + "lru", + "more-asserts", + "parking_lot 0.11.2", + "rand 0.8.5", + "smallvec", + "socket2 0.4.10", + "tokio", + "tracing", + "uint 0.9.5", + "zeroize", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "dlib" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" @@ -1664,6 +2068,12 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + [[package]] name = "dunce" version = "1.0.5" @@ -1702,6 +2112,31 @@ dependencies = [ "spki", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519", + "rand_core 0.6.4", + "serde", + "sha2 0.10.8", + "subtle", + "zeroize", +] + [[package]] name = "either" version = "1.13.0" @@ -1736,6 +2171,37 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "enr" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "972070166c68827e64bd1ebc8159dd8e32d9bc2da7ebe8f20b61308f7974ad30" +dependencies = [ + "alloy-rlp", + "base64 0.21.7", + "bytes", + "ed25519-dalek", + "hex", + "k256", + "log", + "rand 0.8.5", + "serde", + "sha3", + "zeroize", +] + +[[package]] +name = "enum-as-inner" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9720bba047d567ffc8a3cba48bf19126600e249ab7f128e9233e6376976a116" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "enumn" version = "0.1.14" @@ -1891,6 +2357,27 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" +dependencies = [ + "event-listener 5.3.1", + "pin-project-lite", +] + [[package]] name = "eyre" version = "0.6.12" @@ -1938,6 +2425,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "figment" version = "0.10.19" @@ -1947,7 +2440,7 @@ dependencies = [ "atomic", "pear", "serde", - "toml", + "toml 0.8.19", "uncased", "version_check", ] @@ -2131,6 +2624,7 @@ dependencies = [ "futures-core", "futures-task", "futures-util", + "num_cpus", ] [[package]] @@ -2139,6 +2633,16 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +[[package]] +name = "futures-lite" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.30" @@ -2150,6 +2654,17 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "futures-rustls" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2411eed028cdf8c8034eaf21f9915f956b6c3abec4d4c7949ee67f0721127bd" +dependencies = [ + "futures-io", + "rustls 0.20.9", + "webpki", +] + [[package]] name = "futures-sink" version = "0.3.30" @@ -2231,6 +2746,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ghash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "gif" version = "0.12.0" @@ -2403,6 +2928,15 @@ dependencies = [ "serde", ] +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.5", +] + [[package]] name = "heck" version = "0.4.1" @@ -2447,8 +2981,11 @@ dependencies = [ "ctrlc", "dirs", "eyre", + "figment", "futures", + "helios-core", "helios-ethereum", + "helios-opstack", "tokio", "tracing", "tracing-subscriber", @@ -2488,7 +3025,7 @@ dependencies = [ "hex", "jsonrpsee", "openssl", - "parking_lot", + "parking_lot 0.12.3", "reqwest", "revm", "serde", @@ -2517,7 +3054,7 @@ dependencies = [ "helios-core", "hex", "openssl", - "parking_lot", + "parking_lot 0.12.3", "reqwest", "retri", "revm", @@ -2534,6 +3071,39 @@ dependencies = [ "zduny-wasm-timer", ] +[[package]] +name = "helios-opstack" +version = "0.7.0" +dependencies = [ + "alloy", + "axum", + "clap", + "discv5", + "ethereum_ssz 0.6.0", + "ethereum_ssz_derive 0.6.0", + "eyre", + "figment", + "helios-core", + "hex", + "libp2p", + "libp2p-identity", + "op-alloy-consensus", + "op-alloy-network", + "op-alloy-rpc-types", + "reqwest", + "revm", + "serde", + "sha2 0.9.9", + "snap", + "ssz_types", + "tokio", + "tracing", + "tracing-subscriber", + "typenum", + "unsigned-varint", + "url", +] + [[package]] name = "hermit-abi" version = "0.3.9" @@ -2561,6 +3131,31 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hex_fmt" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b07f60793ff0a4d9cef0f18e63b5357e06209987153a64648c972c1e5aff336f" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac 0.12.1", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + [[package]] name = "hmac" version = "0.12.1" @@ -2570,6 +3165,28 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "hmac-drbg" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" +dependencies = [ + "digest 0.9.0", + "generic-array", + "hmac 0.8.1", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "http" version = "0.2.12" @@ -2655,7 +3272,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.5.7", "tokio", "tower-service", "tracing", @@ -2675,6 +3292,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.1", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -2744,9 +3362,9 @@ dependencies = [ "http-body 1.0.1", "hyper 1.4.1", "pin-project-lite", - "socket2", + "socket2 0.5.7", "tokio", - "tower", + "tower 0.4.13", "tower-service", "tracing", ] @@ -2762,7 +3380,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -2780,6 +3398,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.5.0" @@ -2790,6 +3419,35 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "if-addrs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cabb0019d51a643781ff15c9c8a3e5dedc365c47211270f4e8f82812fedd8f0a" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "if-watch" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b0422c86d7ce0e97169cc42e04ae643caf278874a7a3c87b8150a220dc7e1e" +dependencies = [ + "async-io", + "core-foundation", + "fnv", + "futures", + "if-addrs", + "ipnet", + "log", + "rtnetlink", + "system-configuration 0.5.1", + "tokio", + "windows", +] + [[package]] name = "image" version = "0.24.9" @@ -2912,6 +3570,24 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "interprocess" version = "2.2.1" @@ -2927,6 +3603,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2 0.5.7", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + [[package]] name = "ipnet" version = "2.10.0" @@ -3035,7 +3723,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b83cca7a5a7899eed8b2935d5f755c8c4052ad66ab5b328bd34ac2b3ffd3515f" dependencies = [ "anyhow", - "async-lock", + "async-lock 2.8.0", "async-trait", "beef", "futures-timer", @@ -3043,7 +3731,7 @@ dependencies = [ "globset", "hyper 0.14.30", "jsonrpsee-types", - "parking_lot", + "parking_lot 0.12.3", "rand 0.8.5", "rustc-hash", "serde", @@ -3071,7 +3759,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", - "tower", + "tower 0.4.13", "tracing", ] @@ -3082,7 +3770,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d814a21d9a819f8de1a41b819a263ffd68e4bb5f043d936db1c49b54684bde0a" dependencies = [ "heck 0.4.1", - "proc-macro-crate 1.3.1", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", "syn 1.0.109", @@ -3104,7 +3792,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", - "tower", + "tower 0.4.13", "tracing", ] @@ -3153,7 +3841,7 @@ checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" dependencies = [ "base64 0.21.7", "js-sys", - "pem", + "pem 3.0.4", "ring 0.17.8", "serde", "serde_json", @@ -3171,6 +3859,16 @@ dependencies = [ "elliptic-curve", "once_cell", "sha2 0.10.8", + "signature", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", ] [[package]] @@ -3241,20 +3939,402 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] -name = "libredox" -version = "0.1.3" +name = "libp2p" +version = "0.51.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "f35eae38201a993ece6bdc823292d6abd1bffed1c4d0f4a3517d2bd8e1d917fe" dependencies = [ - "bitflags 2.6.0", - "libc", + "bytes", + "futures", + "futures-timer", + "getrandom 0.2.15", + "instant", + "libp2p-allow-block-list", + "libp2p-connection-limits", + "libp2p-core", + "libp2p-dns", + "libp2p-gossipsub", + "libp2p-identity", + "libp2p-mdns", + "libp2p-metrics", + "libp2p-mplex", + "libp2p-noise", + "libp2p-ping", + "libp2p-quic", + "libp2p-swarm", + "libp2p-tcp", + "multiaddr", + "pin-project", ] [[package]] -name = "linux-raw-sys" -version = "0.4.14" +name = "libp2p-allow-block-list" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +checksum = "510daa05efbc25184458db837f6f9a5143888f1caa742426d92e1833ddd38a50" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-connection-limits" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4caa33f1d26ed664c4fe2cca81a08c8e07d4c1c04f2f4ac7655c2dd85467fda0" +dependencies = [ + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "void", +] + +[[package]] +name = "libp2p-core" +version = "0.39.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1df63c0b582aa434fb09b2d86897fa2b419ffeccf934b36f87fcedc8e835c2" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-identity", + "log", + "multiaddr", + "multihash", + "multistream-select", + "once_cell", + "parking_lot 0.12.3", + "pin-project", + "quick-protobuf", + "rand 0.8.5", + "rw-stream-sink", + "smallvec", + "thiserror", + "unsigned-varint", + "void", +] + +[[package]] +name = "libp2p-dns" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146ff7034daae62077c415c2376b8057368042df6ab95f5432ad5e88568b1554" +dependencies = [ + "futures", + "libp2p-core", + "log", + "parking_lot 0.12.3", + "smallvec", + "trust-dns-resolver", +] + +[[package]] +name = "libp2p-gossipsub" +version = "0.44.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70b34b6da8165c0bde35c82db8efda39b824776537e73973549e76cadb3a77c5" +dependencies = [ + "asynchronous-codec", + "base64 0.21.7", + "byteorder", + "bytes", + "either", + "fnv", + "futures", + "hex_fmt", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "prometheus-client", + "quick-protobuf", + "quick-protobuf-codec", + "rand 0.8.5", + "regex", + "sha2 0.10.8", + "smallvec", + "thiserror", + "unsigned-varint", + "void", + "wasm-timer", +] + +[[package]] +name = "libp2p-identity" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "276bb57e7af15d8f100d3c11cbdd32c6752b7eef4ba7a18ecf464972c07abcce" +dependencies = [ + "asn1_der", + "bs58", + "ed25519-dalek", + "libsecp256k1", + "log", + "multiaddr", + "multihash", + "quick-protobuf", + "rand 0.8.5", + "sha2 0.10.8", + "thiserror", + "zeroize", +] + +[[package]] +name = "libp2p-mdns" +version = "0.43.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19983e1f949f979a928f2c603de1cf180cc0dc23e4ac93a62651ccb18341460b" +dependencies = [ + "data-encoding", + "futures", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm", + "log", + "rand 0.8.5", + "smallvec", + "socket2 0.4.10", + "tokio", + "trust-dns-proto", + "void", +] + +[[package]] +name = "libp2p-metrics" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a42ec91e227d7d0dafa4ce88b333cdf5f277253873ab087555c92798db2ddd46" +dependencies = [ + "libp2p-core", + "libp2p-gossipsub", + "libp2p-ping", + "libp2p-swarm", + "prometheus-client", +] + +[[package]] +name = "libp2p-mplex" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d34780b514b159e6f3fd70ba3e72664ec89da28dca2d1e7856ee55e2c7031ba" +dependencies = [ + "asynchronous-codec", + "bytes", + "futures", + "libp2p-core", + "log", + "nohash-hasher", + "parking_lot 0.12.3", + "rand 0.8.5", + "smallvec", + "unsigned-varint", +] + +[[package]] +name = "libp2p-noise" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3673da89d29936bc6435bafc638e2f184180d554ce844db65915113f86ec5e" +dependencies = [ + "bytes", + "curve25519-dalek 3.2.0", + "futures", + "libp2p-core", + "libp2p-identity", + "log", + "once_cell", + "quick-protobuf", + "rand 0.8.5", + "sha2 0.10.8", + "snow", + "static_assertions", + "thiserror", + "x25519-dalek", + "zeroize", +] + +[[package]] +name = "libp2p-ping" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e57759c19c28a73ef1eb3585ca410cefb72c1a709fcf6de1612a378e4219202" +dependencies = [ + "either", + "futures", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-swarm", + "log", + "rand 0.8.5", + "void", +] + +[[package]] +name = "libp2p-quic" +version = "0.7.0-alpha.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6b26abd81cd2398382a1edfe739b539775be8a90fa6914f39b2ab49571ec735" +dependencies = [ + "bytes", + "futures", + "futures-timer", + "if-watch", + "libp2p-core", + "libp2p-identity", + "libp2p-tls", + "log", + "parking_lot 0.12.3", + "quinn-proto", + "rand 0.8.5", + "rustls 0.20.9", + "thiserror", + "tokio", +] + +[[package]] +name = "libp2p-swarm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "903b3d592d7694e56204d211f29d31bc004be99386644ba8731fc3e3ef27b296" +dependencies = [ + "either", + "fnv", + "futures", + "futures-timer", + "instant", + "libp2p-core", + "libp2p-identity", + "libp2p-swarm-derive", + "log", + "rand 0.8.5", + "smallvec", + "tokio", + "void", +] + +[[package]] +name = "libp2p-swarm-derive" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fba456131824ab6acd4c7bf61e9c0f0a3014b5fc9868ccb8e10d344594cdc4f" +dependencies = [ + "heck 0.4.1", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "libp2p-tcp" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d33698596d7722d85d3ab0c86c2c322254fce1241e91208e3679b4eb3026cf" +dependencies = [ + "futures", + "futures-timer", + "if-watch", + "libc", + "libp2p-core", + "log", + "socket2 0.4.10", + "tokio", +] + +[[package]] +name = "libp2p-tls" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff08d13d0dc66e5e9ba6279c1de417b84fa0d0adc3b03e5732928c180ec02781" +dependencies = [ + "futures", + "futures-rustls", + "libp2p-core", + "libp2p-identity", + "rcgen", + "ring 0.16.20", + "rustls 0.20.9", + "thiserror", + "webpki", + "x509-parser", + "yasna", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.6.0", + "libc", +] + +[[package]] +name = "libsecp256k1" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" +dependencies = [ + "arrayref", + "base64 0.13.1", + "digest 0.9.0", + "hmac-drbg", + "libsecp256k1-core", + "libsecp256k1-gen-ecmult", + "libsecp256k1-gen-genmult", + "rand 0.8.5", + "serde", + "sha2 0.9.9", + "typenum", +] + +[[package]] +name = "libsecp256k1-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" +dependencies = [ + "crunchy", + "digest 0.9.0", + "subtle", +] + +[[package]] +name = "libsecp256k1-gen-ecmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "libsecp256k1-gen-genmult" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" +dependencies = [ + "libsecp256k1-core", +] + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -3278,76 +4358,261 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.14.5", +] + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "more-asserts" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fafa6961cabd9c63bcd77a45d7e3b7f3b552b70417831fb0f56db717e72407e" + +[[package]] +name = "multiaddr" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b36f567c7099511fa8612bbbb52dda2419ce0bdbacf31714e3a5ffdb766d3bd" +dependencies = [ + "arrayref", + "byteorder", + "data-encoding", + "log", + "multibase", + "multihash", + "percent-encoding", + "serde", + "static_assertions", + "unsigned-varint", + "url", +] + +[[package]] +name = "multibase" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +dependencies = [ + "base-x", + "data-encoding", + "data-encoding-macro", +] + +[[package]] +name = "multihash" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835d6ff01d610179fbce3de1694d007e500bf33a7f29689838941d6bf783ae40" +dependencies = [ + "core2", + "multihash-derive", + "unsigned-varint", +] + +[[package]] +name = "multihash-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6d4752e6230d8ef7adf7bd5d8c4b1f6561c1014c5ba9a37445ccefe18aa1db" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", + "synstructure", +] + +[[package]] +name = "multistream-select" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8552ab875c1313b97b8d20cb857b9fd63e2d1d6a0a1b53ce9821e575405f27a" +dependencies = [ + "bytes", + "futures", + "log", + "pin-project", + "smallvec", + "unsigned-varint", ] [[package]] -name = "matchers" -version = "0.1.0" +name = "native-tls" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "regex-automata 0.1.10", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] -name = "memchr" -version = "2.7.4" +name = "netlink-packet-core" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "345b8ab5bd4e71a2986663e88c56856699d060e78e152e6e9d7966fcd5491297" +dependencies = [ + "anyhow", + "byteorder", + "libc", + "netlink-packet-utils", +] [[package]] -name = "mime" -version = "0.3.17" +name = "netlink-packet-route" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", +] [[package]] -name = "miniz_oxide" -version = "0.7.4" +name = "netlink-packet-utils" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" dependencies = [ - "adler", - "simd-adler32", + "anyhow", + "byteorder", + "paste", + "thiserror", ] [[package]] -name = "miniz_oxide" -version = "0.8.0" +name = "netlink-proto" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "65b4b14489ab424703c092062176d52ba55485a89c076b4f9db05092b7223aa6" dependencies = [ - "adler2", + "bytes", + "futures", + "log", + "netlink-packet-core", + "netlink-sys", + "thiserror", + "tokio", ] [[package]] -name = "mio" -version = "1.0.2" +name = "netlink-sys" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "416060d346fbaf1f23f9512963e3e878f1a78e707cb699ba9215761754244307" dependencies = [ - "hermit-abi 0.3.9", + "bytes", + "futures", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", + "log", + "tokio", ] [[package]] -name = "native-tls" -version = "0.2.12" +name = "nix" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ + "bitflags 1.3.2", + "cfg-if", "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", ] [[package]] @@ -3362,6 +4627,22 @@ dependencies = [ "libc", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -3491,6 +4772,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "oid-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" +dependencies = [ + "asn1-rs", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -3503,6 +4793,47 @@ version = "11.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" +[[package]] +name = "op-alloy-consensus" +version = "0.1.5" +source = "git+https://github.com/alloy-rs/op-alloy?tag=v0.1.5#ce7f25c524f1eb8346ded160abd37eccfbdee2ac" +dependencies = [ + "alloy-consensus", + "alloy-eips", + "alloy-primitives", + "alloy-rlp", + "alloy-serde", + "derive_more", + "serde", +] + +[[package]] +name = "op-alloy-network" +version = "0.1.5" +source = "git+https://github.com/alloy-rs/op-alloy?tag=v0.1.5#ce7f25c524f1eb8346ded160abd37eccfbdee2ac" +dependencies = [ + "alloy-consensus", + "alloy-network", + "alloy-primitives", + "alloy-rpc-types-eth", + "op-alloy-consensus", + "op-alloy-rpc-types", +] + +[[package]] +name = "op-alloy-rpc-types" +version = "0.1.5" +source = "git+https://github.com/alloy-rs/op-alloy?tag=v0.1.5#ce7f25c524f1eb8346ded160abd37eccfbdee2ac" +dependencies = [ + "alloy-network", + "alloy-primitives", + "alloy-rpc-types-eth", + "alloy-serde", + "op-alloy-consensus", + "serde", + "serde_json", +] + [[package]] name = "opaque-debug" version = "0.3.1" @@ -3622,6 +4953,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + [[package]] name = "parking_lot" version = "0.12.3" @@ -3629,7 +4977,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core", + "parking_lot_core 0.9.10", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi", ] [[package]] @@ -3640,7 +5002,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.4", "smallvec", "windows-targets 0.52.6", ] @@ -3693,6 +5055,15 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "pem" version = "3.0.4" @@ -3846,6 +5217,44 @@ dependencies = [ "miniz_oxide 0.7.4", ] +[[package]] +name = "polling" +version = "3.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2790cd301dec6cd3b7a025e4815cf825724a51c98dccfe6a3e55f05ffb6511" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.4.0", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "poly1305" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" +dependencies = [ + "cpufeatures", + "opaque-debug", + "universal-hash", +] + +[[package]] +name = "polyval" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -3899,12 +5308,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" dependencies = [ - "once_cell", - "toml_edit 0.19.15", + "thiserror", + "toml 0.5.11", ] [[package]] @@ -3913,7 +5322,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ - "toml_edit 0.22.21", + "toml_edit", ] [[package]] @@ -3962,6 +5371,29 @@ dependencies = [ "yansi", ] +[[package]] +name = "prometheus-client" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6fa99d535dd930d1249e6c79cb3c2915f9172a540fe2b02a4c8f9ca954721e" +dependencies = [ + "dtoa", + "itoa", + "parking_lot 0.12.3", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "proptest" version = "1.5.0" @@ -3988,6 +5420,46 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-protobuf" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f" +dependencies = [ + "byteorder", +] + +[[package]] +name = "quick-protobuf-codec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1693116345026436eb2f10b677806169c1a1260c1c60eaaffe3fb5a29ae23d8b" +dependencies = [ + "asynchronous-codec", + "bytes", + "quick-protobuf", + "thiserror", + "unsigned-varint", +] + +[[package]] +name = "quinn-proto" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b0b33c13a79f669c85defaf4c275dc86a0c0372807d0ca3d78e0bb87274863" +dependencies = [ + "bytes", + "rand 0.8.5", + "ring 0.16.20", + "rustc-hash", + "rustls 0.20.9", + "slab", + "thiserror", + "tinyvec", + "tracing", + "webpki", +] + [[package]] name = "quote" version = "1.0.37" @@ -4109,12 +5581,33 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rcgen" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" +dependencies = [ + "pem 1.1.1", + "ring 0.16.20", + "time", + "yasna", +] + [[package]] name = "recvmsg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_syscall" version = "0.5.4" @@ -4210,8 +5703,8 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", - "system-configuration", + "sync_wrapper 1.0.1", + "system-configuration 0.6.1", "tokio", "tokio-native-tls", "tower-service", @@ -4222,6 +5715,16 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + [[package]] name = "retri" version = "0.1.0" @@ -4301,7 +5804,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" dependencies = [ - "hmac", + "hmac 0.12.1", "subtle", ] @@ -4363,6 +5866,21 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "rtnetlink" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322c53fd76a18698f1c27381d58091de3a043d356aa5bd0d510608b565f469a0" +dependencies = [ + "futures", + "log", + "netlink-packet-route", + "netlink-proto", + "nix 0.24.3", + "thiserror", + "tokio", +] + [[package]] name = "ruint" version = "1.12.3" @@ -4430,6 +5948,15 @@ dependencies = [ "semver 1.0.23", ] +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + [[package]] name = "rustix" version = "0.38.37" @@ -4443,6 +5970,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b80e3dec595989ea8510028f30c408a4630db12c9cbb8de34203b89d6577e99" +dependencies = [ + "log", + "ring 0.16.20", + "sct", + "webpki", +] + [[package]] name = "rustls" version = "0.21.12" @@ -4555,6 +6094,17 @@ dependencies = [ "wait-timeout", ] +[[package]] +name = "rw-stream-sink" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26338f5e09bb721b85b135ea05af7767c90b52f6de4f087d4f4a3a9d64e7dc04" +dependencies = [ + "futures", + "pin-project", + "static_assertions", +] + [[package]] name = "ryu" version = "1.0.18" @@ -4720,6 +6270,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.7" @@ -4832,6 +6392,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "sha3-asm" version = "0.1.4" @@ -4909,6 +6479,39 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "snap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" + +[[package]] +name = "snow" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "850948bee068e713b8ab860fe1adc4d109676ab4c3b621fd8147f06b261f2f85" +dependencies = [ + "aes-gcm", + "blake2", + "chacha20poly1305", + "curve25519-dalek 4.1.3", + "rand_core 0.6.4", + "ring 0.17.8", + "rustc_version 0.4.1", + "sha2 0.10.8", + "subtle", +] + +[[package]] +name = "socket2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "socket2" version = "0.5.7" @@ -5081,6 +6684,12 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "sync_wrapper" version = "1.0.1" @@ -5090,6 +6699,29 @@ dependencies = [ "futures-core", ] +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unicode-xid", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys 0.5.0", +] + [[package]] name = "system-configuration" version = "0.6.1" @@ -5098,7 +6730,17 @@ checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.6.0", "core-foundation", - "system-configuration-sys", + "system-configuration-sys 0.6.0", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", ] [[package]] @@ -5253,10 +6895,10 @@ dependencies = [ "bytes", "libc", "mio", - "parking_lot", + "parking_lot 0.12.3", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.7", "tokio-macros", "windows-sys 0.52.0", ] @@ -5342,9 +6984,19 @@ dependencies = [ "futures-io", "futures-sink", "pin-project-lite", + "slab", "tokio", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml" version = "0.8.19" @@ -5354,7 +7006,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.21", + "toml_edit", ] [[package]] @@ -5366,17 +7018,6 @@ dependencies = [ "serde", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.5.0", - "toml_datetime", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.21" @@ -5387,7 +7028,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.18", + "winnow", ] [[package]] @@ -5406,6 +7047,22 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -5544,6 +7201,52 @@ dependencies = [ "triehash", ] +[[package]] +name = "trust-dns-proto" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7f83d1e4a0e4358ac54c5c3681e5d7da5efc5a7a632c90bb6d6669ddd9bc26" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.2.3", + "ipnet", + "lazy_static", + "rand 0.8.5", + "smallvec", + "socket2 0.4.10", + "thiserror", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "trust-dns-resolver" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aff21aa4dcefb0a1afbfac26deb0adc93888c7d295fb63ab273ef276ba2b7cfe" +dependencies = [ + "cfg-if", + "futures-util", + "ipconfig", + "lazy_static", + "lru-cache", + "parking_lot 0.12.3", + "resolv-conf", + "smallvec", + "thiserror", + "tokio", + "tracing", + "trust-dns-proto", +] + [[package]] name = "try-lock" version = "0.2.5" @@ -5648,12 +7351,38 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "universal-hash" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "unsafe-libyaml" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" +[[package]] +name = "unsigned-varint" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6889a77d49f1f013504cec6bf97a2c730394adedaeb1deb5ea08949a50541105" +dependencies = [ + "asynchronous-codec", + "bytes", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -5673,8 +7402,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", - "idna", + "idna 0.5.0", "percent-encoding", + "serde", ] [[package]] @@ -5707,6 +7437,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -5814,6 +7550,21 @@ version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" +[[package]] +name = "wasm-timer" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" +dependencies = [ + "futures", + "js-sys", + "parking_lot 0.11.2", + "pin-utils", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "web-sys" version = "0.3.70" @@ -5824,6 +7575,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "webpki" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed63aea5ce73d0ff405984102c42de94fc55a6b75765d621c65262469b3c9b53" +dependencies = [ + "ring 0.17.8", + "untrusted 0.9.0", +] + [[package]] name = "webpki-roots" version = "0.23.1" @@ -5885,6 +7646,25 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +dependencies = [ + "windows-core 0.51.1", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-core" +version = "0.51.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -6074,20 +7854,21 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.5.40" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] [[package]] -name = "winnow" -version = "0.6.18" +name = "winreg" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "memchr", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] @@ -6127,12 +7908,50 @@ dependencies = [ "tap", ] +[[package]] +name = "x25519-dalek" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "x509-parser" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" +dependencies = [ + "asn1-rs", + "base64 0.13.1", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + [[package]] name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + [[package]] name = "yeslogic-fontconfig-sys" version = "6.0.0" @@ -6152,7 +7971,7 @@ checksum = "0f22d6a02cbc84ea1993b0b341833a55a0866a3378c3a76e0ca664bc2574e370" dependencies = [ "futures", "js-sys", - "parking_lot", + "parking_lot 0.12.3", "pin-utils", "wasm-bindgen", "wasm-bindgen-futures", diff --git a/Cargo.toml b/Cargo.toml index 8ea70d19..142a4694 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ members = [ "core", "ethereum", "ethereum/consensus-core", + "opstack", #"helios-ts", ] @@ -41,6 +42,7 @@ alloy = { version = "0.2.1", features = [ "network", "ssz", "json-rpc", + "signers", ]} revm = { version = "12.1.0", default-features = false, features = [ "std", @@ -58,6 +60,7 @@ tokio = { version = "1", features = ["rt", "sync", "macros"] } # io reqwest = { version = "0.12.4", features = ["json"] } +url = { version = "2.5.2", features = ["serde"] } serde = { version = "1.0.143", features = ["derive"] } serde_json = "1.0.85" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index de0d86b3..a2b794f1 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -13,10 +13,13 @@ eyre.workspace = true tracing.workspace = true futures.workspace = true alloy.workspace = true +figment = { version = "0.10.7", features = ["toml", "env"] } clap = { version = "4.5.4", features = ["derive", "env"] } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } dirs = "5.0.1" ctrlc = "3.2.3" +helios-core = { path = "../core" } helios-ethereum = { path = "../ethereum" } +helios-opstack = { path = "../opstack" } diff --git a/cli/src/main.rs b/cli/src/main.rs index 305f0f2f..6263e1af 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,4 +1,5 @@ -use std::net::IpAddr; +use std::collections::HashMap; +use std::net::{IpAddr, SocketAddr}; use std::{ path::PathBuf, process::exit, @@ -7,20 +8,46 @@ use std::{ }; use alloy::primitives::B256; -use clap::Parser; +use clap::{Args, Parser, Subcommand}; use dirs::home_dir; use eyre::Result; +use figment::providers::Serialized; +use figment::value::Value; use futures::executor::block_on; use tracing::{error, info}; use tracing_subscriber::filter::{EnvFilter, LevelFilter}; use tracing_subscriber::FmtSubscriber; -use helios_ethereum::config::{cli::CliConfig, Config}; +use helios_core::client::Client; +use helios_core::consensus::Consensus; +use helios_core::network_spec::NetworkSpec; +use helios_ethereum::config::{cli::CliConfig, Config as EthereumConfig}; use helios_ethereum::database::FileDB; use helios_ethereum::{EthereumClient, EthereumClientBuilder}; +use helios_opstack::{config::Config as OpStackConfig, OpStackClient, OpStackClientBuilder}; #[tokio::main] async fn main() -> Result<()> { + enable_tracer(); + + let cli = Cli::parse(); + match cli.command { + Command::Ethereum(ethereum) => { + let mut client = ethereum.make_client(); + start_client(&mut client).await; + register_shutdown_handler(client); + } + Command::OpStack(opstack) => { + let mut client = opstack.make_client(); + start_client(&mut client).await; + register_shutdown_handler(client); + } + } + + std::future::pending().await +} + +fn enable_tracer() { let env_filter = EnvFilter::builder() .with_default_directive(LevelFilter::INFO.into()) .from_env() @@ -31,29 +58,20 @@ async fn main() -> Result<()> { .finish(); tracing::subscriber::set_global_default(subscriber).expect("subscriber set failed"); +} - let config = get_config(); - let mut client = match EthereumClientBuilder::new() - .config(config) - .build::() - { - Ok(client) => client, - Err(err) => { - error!(target: "helios::runner", error = %err); - exit(1); - } - }; - +async fn start_client>( + client: &mut Client, +) { if let Err(err) = client.start().await { error!(target: "helios::runner", error = %err); exit(1); } - - register_shutdown_handler(client); - std::future::pending().await } -fn register_shutdown_handler(client: EthereumClient) { +fn register_shutdown_handler>( + client: Client, +) { let client = Arc::new(client); let shutdown_counter = Arc::new(Mutex::new(0)); @@ -85,18 +103,24 @@ fn register_shutdown_handler(client: EthereumClient) { .expect("could not register shutdown handler"); } -fn get_config() -> Config { - let cli = Cli::parse(); - let config_path = home_dir().unwrap().join(".helios/helios.toml"); - let cli_config = cli.as_cli_config(); - - Config::from_file(&config_path, &cli.network, &cli_config) -} - #[derive(Parser)] #[clap(version, about)] -/// Helios is a fast, secure, and portable light client for Ethereum +/// Helios is a fast, secure, and portable multichain light client struct Cli { + #[command(subcommand)] + command: Command, +} + +#[derive(Subcommand)] +enum Command { + #[clap(name = "ethereum")] + Ethereum(EthereumArgs), + #[clap(name = "opstack")] + OpStack(OpStackArgs), +} + +#[derive(Args)] +struct EthereumArgs { #[clap(short, long, default_value = "mainnet")] network: String, #[clap(short = 'b', long, env)] @@ -119,7 +143,24 @@ struct Cli { strict_checkpoint_age: bool, } -impl Cli { +impl EthereumArgs { + fn make_client(&self) -> EthereumClient { + let config_path = home_dir().unwrap().join(".helios/helios.toml"); + let cli_config = self.as_cli_config(); + let config = EthereumConfig::from_file(&config_path, &self.network, &cli_config); + + match EthereumClientBuilder::new() + .config(config) + .build::() + { + Ok(client) => client, + Err(err) => { + error!(target: "helios::runner", error = %err); + exit(1); + } + } + } + fn as_cli_config(&self) -> CliConfig { CliConfig { checkpoint: self.checkpoint, @@ -138,6 +179,63 @@ impl Cli { } } +#[derive(Args, Debug)] +struct OpStackArgs { + #[clap(short, long)] + network: String, + #[clap(short = 'b', long, env, default_value = "127.0.0.1")] + rpc_bind_ip: Option, + #[clap(short = 'p', long, env, default_value = "8545")] + rpc_port: Option, + #[clap(short, long, env)] + execution_rpc: Option, + #[clap(short, long, env)] + consensus_rpc: Option, +} + +impl OpStackArgs { + fn make_client(&self) -> OpStackClient { + let config_path = home_dir().unwrap().join(".helios/helios.toml"); + let cli_provider = self.as_provider(); + let config = OpStackConfig::from_file(&config_path, &self.network, cli_provider); + + match OpStackClientBuilder::new().config(config).build() { + Ok(client) => client, + Err(err) => { + error!(target: "helios::runner", error = %err); + exit(1); + } + } + } + + fn as_provider(&self) -> Serialized> { + let mut user_dict = HashMap::new(); + + if let Some(rpc) = &self.execution_rpc { + user_dict.insert("execution_rpc", Value::from(rpc.clone())); + } + + if let Some(rpc) = &self.consensus_rpc { + user_dict.insert("consensus_rpc", Value::from(rpc.clone())); + } + + if self.rpc_bind_ip.is_some() && self.rpc_port.is_some() { + let rpc_socket = SocketAddr::new(self.rpc_bind_ip.unwrap(), self.rpc_port.unwrap()); + user_dict.insert("rpc_socket", Value::from(rpc_socket.to_string())); + } + + if let Some(ip) = self.rpc_bind_ip { + user_dict.insert("rpc_bind_ip", Value::from(ip.to_string())); + } + + if let Some(port) = self.rpc_port { + user_dict.insert("rpc_port", Value::from(port)); + } + + Serialized::from(user_dict, &self.network) + } +} + fn true_or_none(b: bool) -> Option { if b { Some(b) diff --git a/core/src/execution/mod.rs b/core/src/execution/mod.rs index ff488b65..f0f66c45 100644 --- a/core/src/execution/mod.rs +++ b/core/src/execution/mod.rs @@ -199,7 +199,7 @@ impl> ExecutionClient { let receipts_fut = tx_hashes.iter().map(|hash| async move { let receipt = self.rpc.get_transaction_receipt(*hash).await; - receipt?.ok_or(eyre::eyre!("not reachable")) + receipt?.ok_or(eyre::eyre!("missing block receipt")) }); let receipts = join_all(receipts_fut).await; diff --git a/core/src/execution/state.rs b/core/src/execution/state.rs index 8158b5fc..3efdffd5 100644 --- a/core/src/execution/state.rs +++ b/core/src/execution/state.rs @@ -45,7 +45,7 @@ impl State { inner_ref.write().await.push_finalized_block(block); } - } + }, } } }); diff --git a/ethereum/src/config/mod.rs b/ethereum/src/config/mod.rs index f95350dd..55af3e37 100644 --- a/ethereum/src/config/mod.rs +++ b/ethereum/src/config/mod.rs @@ -64,12 +64,9 @@ impl Config { match err.kind { figment::error::Kind::MissingField(field) => { let field = field.replace('_', "-"); - println!("\x1b[91merror\x1b[0m: missing configuration field: {field}"); - println!("\n\ttry supplying the proper command line argument: --{field}"); - - println!("\talternatively, you can add the field to your helios.toml file or as an environment variable"); + println!("\talternatively, you can add the field to your helios.toml file"); println!("\nfor more information, check the github README"); } _ => println!("cannot parse configuration: {err}"), diff --git a/ethereum/src/config/networks.rs b/ethereum/src/config/networks.rs index 05f7c21c..60528f15 100644 --- a/ethereum/src/config/networks.rs +++ b/ethereum/src/config/networks.rs @@ -79,7 +79,7 @@ pub fn mainnet() -> BaseConfig { "c7fc7b2f4b548bfc9305fa80bc1865ddc6eea4557f0a80507af5dc34db7bd9ce" ), rpc_port: 8545, - consensus_rpc: Some("https://www.lightclientdata.org".to_string()), + consensus_rpc: Some("https://ethereum.operationsolarstorm.org".to_string()), chain: ChainConfig { chain_id: 1, genesis_time: 1606824023, diff --git a/opstack/Cargo.toml b/opstack/Cargo.toml new file mode 100644 index 00000000..740a70b6 --- /dev/null +++ b/opstack/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "helios-opstack" +version = "0.7.0" +edition = "2021" + +[[bin]] +name = "server" +path = "./bin/server.rs" + +[dependencies] +tokio.workspace = true +eyre.workspace = true +tracing.workspace = true +hex.workspace = true +serde.workspace = true +typenum.workspace = true +tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } +reqwest.workspace = true +url.workspace = true + +# consensus +alloy.workspace = true +revm.workspace = true +sha2.workspace = true +ethereum_ssz_derive.workspace = true +ethereum_ssz.workspace = true +ssz_types.workspace = true +op-alloy-network = { git = "https://github.com/alloy-rs/op-alloy", tag = "v0.1.5" } +op-alloy-consensus = { git = "https://github.com/alloy-rs/op-alloy", tag = "v0.1.5" } +op-alloy-rpc-types = { git = "https://github.com/alloy-rs/op-alloy", tag = "v0.1.5" } + +# server +axum = "0.7.6" +clap = { version = "4.5.4", features = ["derive", "env"] } + +# config +figment = { version = "0.10.7", features = ["toml", "env"] } + +# networking +libp2p = { version = "0.51.3", features = ["macros", "tokio", "tcp", "mplex", "noise", "gossipsub", "ping"] } +discv5 = "0.7.0" +libp2p-identity = { version = "0.1.2", features = ["secp256k1"] } +unsigned-varint = "0.7.1" +snap = "1" + +helios-core = { path = "../core" } diff --git a/opstack/bin/server.rs b/opstack/bin/server.rs new file mode 100644 index 00000000..5493ef15 --- /dev/null +++ b/opstack/bin/server.rs @@ -0,0 +1,49 @@ +use std::net::SocketAddr; + +use clap::Parser; +use eyre::Result; +use tracing_subscriber::{EnvFilter, FmtSubscriber}; + +use helios_opstack::{ + config::{Network, NetworkConfig}, + server::start_server, +}; + +#[tokio::main] +async fn main() -> Result<()> { + enable_tracing(); + let cli = Cli::parse(); + let config = NetworkConfig::from(cli.network); + + let chain_id = config.chain.chain_id; + let unsafe_signer = config.chain.unsafe_signer; + let server_addr = cli.server_address; + let gossip_addr = cli.gossip_address; + + start_server(server_addr, gossip_addr, chain_id, unsafe_signer).await?; + + Ok(()) +} + +fn enable_tracing() { + let env_filter = EnvFilter::builder() + .with_default_directive("helios_opstack=info".parse().unwrap()) + .from_env() + .expect("invalid env filter"); + + let subscriber = FmtSubscriber::builder() + .with_env_filter(env_filter) + .finish(); + + tracing::subscriber::set_global_default(subscriber).expect("subscriber set failed"); +} + +#[derive(Parser)] +struct Cli { + #[clap(short, long)] + network: Network, + #[clap(short, long, default_value = "127.0.0.1:3000")] + server_address: SocketAddr, + #[clap(short, long, default_value = "0.0.0.0:9876")] + gossip_address: SocketAddr, +} diff --git a/opstack/src/builder.rs b/opstack/src/builder.rs new file mode 100644 index 00000000..50a5872f --- /dev/null +++ b/opstack/src/builder.rs @@ -0,0 +1,96 @@ +use std::net::SocketAddr; + +use alloy::primitives::Address; +use eyre::Result; +use reqwest::{IntoUrl, Url}; + +use crate::{ + config::{ChainConfig, Config}, + consensus::ConsensusClient, + OpStackClient, +}; + +#[derive(Default)] +pub struct OpStackClientBuilder { + config: Option, + chain_id: Option, + unsafe_signer: Option
, + consensus_rpc: Option, + execution_rpc: Option, + rpc_socket: Option, +} + +impl OpStackClientBuilder { + pub fn new() -> Self { + OpStackClientBuilder::default() + } + + pub fn config(mut self, config: Config) -> Self { + self.config = Some(config); + self + } + + pub fn chain_id(mut self, chain_id: u64) -> Self { + self.chain_id = Some(chain_id); + self + } + + pub fn unsafe_signer(mut self, signer: Address) -> Self { + self.unsafe_signer = Some(signer); + self + } + + pub fn consensus_rpc(mut self, consensus_rpc: T) -> Self { + self.consensus_rpc = Some(consensus_rpc.into_url().unwrap()); + self + } + + pub fn execution_rpc(mut self, execution_rpc: T) -> Self { + self.execution_rpc = Some(execution_rpc.into_url().unwrap()); + self + } + + pub fn rpc_socket(mut self, socket: SocketAddr) -> Self { + self.rpc_socket = Some(socket); + self + } + + pub fn build(self) -> Result { + let config = if let Some(config) = self.config { + config + } else { + let Some(chain_id) = self.chain_id else { + eyre::bail!("chain id required"); + }; + + let Some(unsafe_signer) = self.unsafe_signer else { + eyre::bail!("unsafe signer required"); + }; + + let Some(consensus_rpc) = self.consensus_rpc else { + eyre::bail!("consensus rpc required"); + }; + + let Some(execution_rpc) = self.execution_rpc else { + eyre::bail!("execution rpc required"); + }; + + Config { + consensus_rpc, + execution_rpc, + rpc_socket: self.rpc_socket, + chain: ChainConfig { + chain_id, + unsafe_signer, + }, + } + }; + + let consensus = ConsensusClient::new(&config); + OpStackClient::new( + &config.execution_rpc.to_string(), + consensus, + config.rpc_socket, + ) + } +} diff --git a/opstack/src/config.rs b/opstack/src/config.rs new file mode 100644 index 00000000..25521101 --- /dev/null +++ b/opstack/src/config.rs @@ -0,0 +1,126 @@ +use std::{ + collections::HashMap, fmt::Display, net::SocketAddr, path::PathBuf, process::exit, str::FromStr, +}; + +use alloy::primitives::{address, Address}; +use eyre::Result; +use figment::{ + providers::{Format, Serialized, Toml}, + value::Value, + Figment, +}; +use serde::{Deserialize, Serialize}; +use url::Url; + +#[derive(Serialize, Deserialize)] +pub struct Config { + pub consensus_rpc: Url, + pub execution_rpc: Url, + pub rpc_socket: Option, + pub chain: ChainConfig, +} + +#[derive(Serialize, Deserialize)] +pub struct ChainConfig { + pub chain_id: u64, + pub unsafe_signer: Address, +} + +#[derive(Serialize, Deserialize)] +pub struct NetworkConfig { + pub consensus_rpc: Option, + pub chain: ChainConfig, +} + +#[derive(Copy, Clone, Debug)] +pub enum Network { + Optimism, + Base, +} + +impl Display for Network { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Optimism => f.write_str("optimism"), + Self::Base => f.write_str("base"), + } + } +} + +impl FromStr for Network { + type Err = eyre::Report; + + fn from_str(s: &str) -> Result { + match s { + "optimism" => Ok(Self::Optimism), + "base" => Ok(Self::Base), + _ => Err(eyre::eyre!("network not recognized")), + } + } +} + +impl From for NetworkConfig { + fn from(value: Network) -> Self { + match value { + Network::Optimism => NetworkConfig { + consensus_rpc: Some("https://optimism.operationsolarstorm.org".parse().unwrap()), + chain: ChainConfig { + chain_id: 10, + unsafe_signer: address!("AAAA45d9549EDA09E70937013520214382Ffc4A2"), + }, + }, + Network::Base => NetworkConfig { + consensus_rpc: Some("https://base.operationsolarstorm.org".parse().unwrap()), + chain: ChainConfig { + chain_id: 8453, + unsafe_signer: address!("Af6E19BE0F9cE7f8afd49a1824851023A8249e8a"), + }, + }, + } + } +} + +impl Config { + pub fn from_file( + config_path: &PathBuf, + network: &str, + cli_provider: Serialized>, + ) -> Self { + let network = Network::from_str(network).unwrap(); + let network_config = NetworkConfig::from(network); + + let base_provider = Serialized::from(network_config, network.to_string()); + let toml_provider = Toml::file(config_path).nested(); + + let config_res = Figment::new() + .merge(base_provider) + .merge(toml_provider) + .merge(cli_provider) + .select(network.to_string()) + .extract(); + + match config_res { + Ok(config) => config, + Err(err) => { + match err.kind { + figment::error::Kind::MissingField(field) => { + let field = field.replace('_', "-"); + println!("\x1b[91merror\x1b[0m: missing configuration field: {field}"); + println!("\n\ttry supplying the proper command line argument: --{field}"); + println!("\talternatively, you can add the field to your helios.toml file"); + println!("\nfor more information, check the github README"); + } + figment::error::Kind::InvalidType(_, _) => { + let field = err.path.join(".").replace("_", "-"); + println!("\x1b[91merror\x1b[0m: invalid configuration field: {field}"); + println!("\n\ttry supplying the proper command line argument: --{field}"); + println!("\talternatively, you can add the field to your helios.toml file"); + println!("\nfor more information, check the github README"); + } + _ => println!("cannot parse configuration: {err}"), + } + exit(1); + } + } + } +} diff --git a/opstack/src/consensus.rs b/opstack/src/consensus.rs new file mode 100644 index 00000000..d9c1d24a --- /dev/null +++ b/opstack/src/consensus.rs @@ -0,0 +1,306 @@ +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +use alloy::consensus::Transaction as TxTrait; +use alloy::primitives::{b256, keccak256, Address, B256, U256, U64}; +use alloy::rlp::Decodable; +use alloy::rpc::types::{Parity, Signature, Transaction}; +use eyre::Result; +use op_alloy_consensus::OpTxEnvelope; +use tokio::sync::mpsc::Sender; +use tokio::{ + sync::{ + mpsc::{channel, Receiver}, + watch, + }, + time::sleep, +}; + +use helios_core::consensus::Consensus; +use helios_core::types::{Block, Transactions}; + +use crate::{config::Config, types::ExecutionPayload, SequencerCommitment}; + +pub struct ConsensusClient { + block_recv: Option>>, + finalized_block_recv: Option>>>, + chain_id: u64, +} + +impl ConsensusClient { + pub fn new(config: &Config) -> Self { + let (block_send, block_recv) = channel(256); + let (finalized_block_send, finalied_block_recv) = watch::channel(None); + + let mut inner = Inner { + server_url: config.consensus_rpc.to_string(), + unsafe_signer: config.chain.unsafe_signer, + chain_id: config.chain.chain_id, + latest_block: None, + block_send, + finalized_block_send, + }; + + tokio::spawn(async move { + loop { + _ = inner.advance().await; + sleep(Duration::from_secs(1)).await; + } + }); + + Self { + block_recv: Some(block_recv), + finalized_block_recv: Some(finalied_block_recv), + chain_id: config.chain.chain_id, + } + } +} + +impl Consensus for ConsensusClient { + fn chain_id(&self) -> u64 { + self.chain_id + } + + fn shutdown(&self) -> eyre::Result<()> { + Ok(()) + } + + fn block_recv(&mut self) -> Option>> { + self.block_recv.take() + } + + fn finalized_block_recv(&mut self) -> Option>>> { + self.finalized_block_recv.take() + } + + fn expected_highest_block(&self) -> u64 { + u64::MAX + } +} + +#[allow(dead_code)] +struct Inner { + server_url: String, + unsafe_signer: Address, + chain_id: u64, + latest_block: Option, + block_send: Sender>, + finalized_block_send: watch::Sender>>, +} + +impl Inner { + pub async fn advance(&mut self) -> Result<()> { + let req = format!("{}latest", self.server_url); + let commitment = reqwest::get(req) + .await? + .json::() + .await?; + + if commitment.verify(self.unsafe_signer, self.chain_id).is_ok() { + let payload = ExecutionPayload::try_from(&commitment)?; + if self + .latest_block + .map(|latest| payload.block_number > latest) + .unwrap_or(true) + { + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let timestamp = Duration::from_secs(payload.timestamp); + let age = now.saturating_sub(timestamp); + let number = payload.block_number; + + if let Ok(block) = payload_to_block(payload) { + self.latest_block = Some(block.number.to()); + _ = self.block_send.send(block).await; + + tracing::info!( + "unsafe head updated: block={} age={}s", + number, + age.as_secs() + ); + } else { + tracing::warn!("invalid block received"); + } + } + } + + Ok(()) + } +} + +fn payload_to_block(value: ExecutionPayload) -> Result> { + let empty_nonce = "0x0000000000000000".to_string(); + let empty_uncle_hash = + b256!("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"); + + let txs = value + .transactions + .iter() + .enumerate() + .map(|(i, tx_bytes)| { + let tx_bytes = tx_bytes.to_vec(); + let mut tx_bytes_slice = tx_bytes.as_slice(); + let tx_envelope = OpTxEnvelope::decode(&mut tx_bytes_slice)?; + let transaction_type = Some(tx_envelope.tx_type().into()); + + Ok(match tx_envelope { + OpTxEnvelope::Legacy(inner) => { + let inner_tx = inner.tx(); + Transaction { + hash: *inner.hash(), + nonce: inner_tx.nonce, + block_hash: Some(value.block_hash), + block_number: Some(value.block_number), + transaction_index: Some(i as u64), + to: inner_tx.to.to().cloned(), + value: inner_tx.value, + gas_price: Some(inner_tx.gas_price), + gas: inner_tx.gas_limit, + input: inner_tx.input.to_vec().into(), + chain_id: inner_tx.chain_id, + transaction_type, + from: inner.recover_signer()?, + signature: Some(Signature { + r: inner.signature().r(), + s: inner.signature().s(), + v: U256::from(inner.signature().v().to_u64()), + y_parity: None, + }), + ..Default::default() + } + } + OpTxEnvelope::Eip2930(inner) => { + let inner_tx = inner.tx(); + Transaction { + hash: *inner.hash(), + nonce: inner_tx.nonce, + block_hash: Some(value.block_hash), + block_number: Some(value.block_number), + transaction_index: Some(i as u64), + to: inner_tx.to.to().cloned(), + value: inner_tx.value, + gas_price: Some(inner_tx.gas_price), + gas: inner_tx.gas_limit, + input: inner_tx.input.to_vec().into(), + chain_id: Some(inner_tx.chain_id), + transaction_type, + from: inner.recover_signer()?, + signature: Some(Signature { + r: inner.signature().r(), + s: inner.signature().s(), + v: U256::from(inner.signature().v().to_u64()), + y_parity: Some(Parity(inner.signature().v().to_u64() == 1)), + }), + access_list: Some(inner.tx().access_list.clone()), + ..Default::default() + } + } + OpTxEnvelope::Eip1559(inner) => { + let inner_tx = inner.tx(); + Transaction { + hash: *inner.hash(), + nonce: inner_tx.nonce, + block_hash: Some(value.block_hash), + block_number: Some(value.block_number), + transaction_index: Some(i as u64), + to: inner_tx.to.to().cloned(), + value: inner_tx.value, + gas_price: inner_tx.gas_price(), + gas: inner_tx.gas_limit, + input: inner_tx.input.to_vec().into(), + chain_id: Some(inner_tx.chain_id), + transaction_type, + from: inner.recover_signer()?, + signature: Some(Signature { + r: inner.signature().r(), + s: inner.signature().s(), + v: U256::from(inner.signature().v().to_u64()), + y_parity: Some(Parity(inner.signature().v().to_u64() == 1)), + }), + access_list: Some(inner_tx.access_list.clone()), + max_fee_per_gas: Some(inner_tx.max_fee_per_gas), + max_priority_fee_per_gas: Some(inner_tx.max_priority_fee_per_gas), + ..Default::default() + } + } + OpTxEnvelope::Eip4844(inner) => { + let inner_tx = inner.tx(); + Transaction { + hash: *inner.hash(), + nonce: inner_tx.nonce(), + block_hash: Some(value.block_hash), + block_number: Some(value.block_number), + transaction_index: Some(i as u64), + to: inner_tx.to().to().cloned(), + value: inner_tx.value(), + gas_price: inner_tx.gas_price(), + gas: inner_tx.gas_limit(), + input: inner_tx.input().to_vec().into(), + chain_id: inner_tx.chain_id(), + transaction_type, + from: inner.recover_signer()?, + signature: Some(Signature { + r: inner.signature().r(), + s: inner.signature().s(), + v: U256::from(inner.signature().v().to_u64()), + y_parity: Some(Parity(inner.signature().v().to_u64() == 1)), + }), + access_list: Some(inner_tx.tx().access_list.clone()), + max_fee_per_gas: Some(inner_tx.tx().max_fee_per_gas), + max_priority_fee_per_gas: Some(inner_tx.tx().max_priority_fee_per_gas), + max_fee_per_blob_gas: Some(inner_tx.tx().max_fee_per_blob_gas), + blob_versioned_hashes: Some(inner_tx.tx().blob_versioned_hashes.clone()), + ..Default::default() + } + } + OpTxEnvelope::Deposit(inner) => { + let hash = + keccak256([&[0x7Eu8], alloy::rlp::encode(&inner).as_slice()].concat()); + + let tx = Transaction { + hash, + nonce: inner.nonce(), + block_hash: Some(value.block_hash), + block_number: Some(value.block_number), + transaction_index: Some(i as u64), + to: inner.to().to().cloned(), + value: inner.value(), + gas_price: inner.gas_price(), + gas: inner.gas_limit(), + input: inner.input().to_vec().into(), + chain_id: inner.chain_id(), + transaction_type, + ..Default::default() + }; + + tx + } + _ => unreachable!("new tx type"), + }) + }) + .collect::>>()?; + + Ok(Block { + number: U64::from(value.block_number), + base_fee_per_gas: value.base_fee_per_gas, + difficulty: U256::ZERO, + extra_data: value.extra_data.to_vec().into(), + gas_limit: U64::from(value.gas_limit), + gas_used: U64::from(value.gas_used), + hash: value.block_hash, + logs_bloom: value.logs_bloom.to_vec().into(), + miner: value.fee_recipient, + parent_hash: value.parent_hash, + receipts_root: value.receipts_root, + state_root: value.state_root, + timestamp: U64::from(value.timestamp), + total_difficulty: U64::ZERO, + transactions: Transactions::Full(txs), + mix_hash: value.prev_randao, + nonce: empty_nonce, + sha3_uncles: empty_uncle_hash, + size: U64::ZERO, + transactions_root: B256::default(), + uncles: vec![], + blob_gas_used: Some(U64::from(value.blob_gas_used)), + excess_blob_gas: Some(U64::from(value.excess_blob_gas)), + }) +} diff --git a/opstack/src/lib.rs b/opstack/src/lib.rs new file mode 100644 index 00000000..216c14f1 --- /dev/null +++ b/opstack/src/lib.rs @@ -0,0 +1,76 @@ +use alloy::{ + primitives::{keccak256, Address, Bytes, B256}, + signers::Signature, +}; +use eyre::Result; +use serde::{Deserialize, Serialize}; +use spec::OpStack; +use ssz::Decode; + +use helios_core::client::Client; + +use consensus::ConsensusClient; +use types::ExecutionPayload; + +mod builder; +pub mod config; +pub mod consensus; +pub mod server; +pub mod spec; +pub mod types; + +pub use builder::OpStackClientBuilder; +pub type OpStackClient = Client; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SequencerCommitment { + data: Bytes, + signature: Signature, +} + +impl SequencerCommitment { + pub fn new(data: &[u8]) -> Result { + let mut decoder = snap::raw::Decoder::new(); + let decompressed = decoder.decompress_vec(&data)?; + + let signature = Signature::try_from(&decompressed[..65])?; + let data = Bytes::from(decompressed[65..].to_vec()); + + Ok(SequencerCommitment { data, signature }) + } + + pub fn verify(&self, signer: Address, chain_id: u64) -> Result<()> { + let msg = signature_msg(&self.data, chain_id); + let pk = self.signature.recover_from_prehash(&msg)?; + let recovered_signer = Address::from_public_key(&pk); + + if signer != recovered_signer { + eyre::bail!("invalid signer"); + } + + Ok(()) + } +} + +impl TryFrom<&SequencerCommitment> for ExecutionPayload { + type Error = eyre::Report; + + fn try_from(value: &SequencerCommitment) -> Result { + let payload_bytes = &value.data[32..]; + ExecutionPayload::from_ssz_bytes(payload_bytes).map_err(|_| eyre::eyre!("decode failed")) + } +} + +fn signature_msg(data: &[u8], chain_id: u64) -> B256 { + let domain = B256::ZERO; + let chain_id = B256::left_padding_from(&chain_id.to_be_bytes()); + let payload_hash = keccak256(data); + + let signing_data = [ + domain.as_slice(), + chain_id.as_slice(), + payload_hash.as_slice(), + ]; + + keccak256(signing_data.concat()).into() +} diff --git a/opstack/src/server/mod.rs b/opstack/src/server/mod.rs new file mode 100644 index 00000000..1937bdc1 --- /dev/null +++ b/opstack/src/server/mod.rs @@ -0,0 +1,88 @@ +use std::{net::SocketAddr, sync::Arc, time::Duration}; + +use alloy::primitives::Address; +use axum::{extract::State, routing::get, Json, Router}; +use eyre::Result; +use tokio::{ + sync::{mpsc::Receiver, RwLock}, + time::sleep, +}; + +use crate::{types::ExecutionPayload, SequencerCommitment}; + +use self::net::{block_handler::BlockHandler, gossip::GossipService}; + +pub mod net; + +pub async fn start_server( + server_addr: SocketAddr, + gossip_addr: SocketAddr, + chain_id: u64, + signer: Address, +) -> Result<()> { + let state = Arc::new(RwLock::new(ServerState::new( + gossip_addr, + chain_id, + signer, + )?)); + + let state_copy = state.clone(); + let _handle = tokio::spawn(async move { + loop { + state_copy.write().await.update(); + sleep(Duration::from_secs(1)).await; + } + }); + + let router = Router::new() + .route("/latest", get(latest_handler)) + .with_state(state); + + let listener = tokio::net::TcpListener::bind(server_addr).await?; + axum::serve(listener, router).await?; + + Ok(()) +} + +async fn latest_handler( + State(state): State>>, +) -> Json> { + Json(state.read().await.latest_commitment.clone().map(|v| v.0)) +} + +struct ServerState { + commitment_recv: Receiver, + latest_commitment: Option<(SequencerCommitment, u64)>, +} + +impl ServerState { + pub fn new(addr: SocketAddr, chain_id: u64, signer: Address) -> Result { + let (handler, commitment_recv) = BlockHandler::new(signer, chain_id); + let gossip = GossipService::new(addr, chain_id, handler); + gossip.start()?; + + Ok(Self { + commitment_recv, + latest_commitment: None, + }) + } + + pub fn update(&mut self) { + if let Ok(commitment) = self.commitment_recv.try_recv() { + if let Ok(payload) = ExecutionPayload::try_from(&commitment) { + if self.is_latest_commitment(payload.block_number) { + tracing::info!("new commitment for block: {}", payload.block_number); + self.latest_commitment = Some((commitment, payload.block_number)); + } + } + } + } + + fn is_latest_commitment(&self, block_number: u64) -> bool { + if let Some((_, latest_block_number)) = self.latest_commitment { + block_number > latest_block_number + } else { + true + } + } +} diff --git a/opstack/src/server/net/block_handler.rs b/opstack/src/server/net/block_handler.rs new file mode 100644 index 00000000..c2fcdb6f --- /dev/null +++ b/opstack/src/server/net/block_handler.rs @@ -0,0 +1,43 @@ +use alloy::primitives::Address; +use libp2p::gossipsub::{IdentTopic, Message, MessageAcceptance, TopicHash}; +use tokio::sync::mpsc::{channel, Receiver, Sender}; + +use crate::SequencerCommitment; + +pub struct BlockHandler { + chain_id: u64, + signer: Address, + commitment_sender: Sender, + blocks_v3_topic: IdentTopic, +} + +impl BlockHandler { + pub fn new(signer: Address, chain_id: u64) -> (Self, Receiver) { + let (send, recv) = channel(256); + let handler = Self { + chain_id, + signer, + commitment_sender: send, + blocks_v3_topic: IdentTopic::new(format!("/optimism/{}/2/blocks", chain_id)), + }; + + (handler, recv) + } + + pub fn topics(&self) -> Vec { + vec![self.blocks_v3_topic.hash()] + } + + pub fn handle(&self, msg: Message) -> MessageAcceptance { + let Ok(commitment) = SequencerCommitment::new(&msg.data) else { + return MessageAcceptance::Reject; + }; + + if commitment.verify(self.signer, self.chain_id).is_ok() { + _ = self.commitment_sender.try_send(commitment); + MessageAcceptance::Accept + } else { + MessageAcceptance::Reject + } + } +} diff --git a/opstack/src/server/net/bootnodes.rs b/opstack/src/server/net/bootnodes.rs new file mode 100644 index 00000000..59e13284 --- /dev/null +++ b/opstack/src/server/net/bootnodes.rs @@ -0,0 +1,24 @@ +use std::str::FromStr; + +use discv5::enr::{CombinedKey, Enr}; + +/// Default bootnodes to use. Currently consists of 2 Base bootnodes & 1 Optimism bootnode. +pub fn bootnodes() -> Vec> { + let bootnodes = [ + "enr:-J64QBbwPjPLZ6IOOToOLsSjtFUjjzN66qmBZdUexpO32Klrc458Q24kbty2PdRaLacHM5z-cZQr8mjeQu3pik6jPSOGAYYFIqBfgmlkgnY0gmlwhDaRWFWHb3BzdGFja4SzlAUAiXNlY3AyNTZrMaECmeSnJh7zjKrDSPoNMGXoopeDF4hhpj5I0OsQUUt4u8uDdGNwgiQGg3VkcIIkBg", + "enr:-J64QAlTCDa188Hl1OGv5_2Kj2nWCsvxMVc_rEnLtw7RPFbOfqUOV6khXT_PH6cC603I2ynY31rSQ8sI9gLeJbfFGaWGAYYFIrpdgmlkgnY0gmlwhANWgzCHb3BzdGFja4SzlAUAiXNlY3AyNTZrMaECkySjcg-2v0uWAsFsZZu43qNHppGr2D5F913Qqs5jDCGDdGNwgiQGg3VkcIIkBg", + "enr:-J24QGEzN4mJgLWNTUNwj7riVJ2ZjRLenOFccl2dbRFxHHOCCZx8SXWzgf-sLzrGs6QgqSFCvGXVgGPBkRkfOWlT1-iGAYe6Cu93gmlkgnY0gmlwhCJBEUSHb3BzdGFja4OkAwCJc2VjcDI1NmsxoQLuYIwaYOHg3CUQhCkS-RsSHmUd1b_x93-9yQ5ItS6udIN0Y3CCIyuDdWRwgiMr", + + // Base bootnodes + "enr:-J24QNz9lbrKbN4iSmmjtnr7SjUMk4zB7f1krHZcTZx-JRKZd0kA2gjufUROD6T3sOWDVDnFJRvqBBo62zuF-hYCohOGAYiOoEyEgmlkgnY0gmlwhAPniryHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQKNVFlCxh_B-716tTs-h1vMzZkSs1FTu_OYTNjgufplG4N0Y3CCJAaDdWRwgiQG", + "enr:-J24QH-f1wt99sfpHy4c0QJM-NfmsIfmlLAMMcgZCUEgKG_BBYFc6FwYgaMJMQN5dsRBJApIok0jFn-9CS842lGpLmqGAYiOoDRAgmlkgnY0gmlwhLhIgb2Hb3BzdGFja4OFQgCJc2VjcDI1NmsxoQJ9FTIv8B9myn1MWaC_2lJ-sMoeCDkusCsk4BYHjjCq04N0Y3CCJAaDdWRwgiQG", + "enr:-J24QDXyyxvQYsd0yfsN0cRr1lZ1N11zGTplMNlW4xNEc7LkPXh0NAJ9iSOVdRO95GPYAIc6xmyoCCG6_0JxdL3a0zaGAYiOoAjFgmlkgnY0gmlwhAPckbGHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQJwoS7tzwxqXSyFL7g0JM-KWVbgvjfB8JA__T7yY_cYboN0Y3CCJAaDdWRwgiQG", + "enr:-J24QHmGyBwUZXIcsGYMaUqGGSl4CFdx9Tozu-vQCn5bHIQbR7On7dZbU61vYvfrJr30t0iahSqhc64J46MnUO2JvQaGAYiOoCKKgmlkgnY0gmlwhAPnCzSHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQINc4fSijfbNIiGhcgvwjsjxVFJHUstK9L1T8OTKUjgloN0Y3CCJAaDdWRwgiQG", + "enr:-J24QG3ypT4xSu0gjb5PABCmVxZqBjVw9ca7pvsI8jl4KATYAnxBmfkaIuEqy9sKvDHKuNCsy57WwK9wTt2aQgcaDDyGAYiOoGAXgmlkgnY0gmlwhDbGmZaHb3BzdGFja4OFQgCJc2VjcDI1NmsxoQIeAK_--tcLEiu7HvoUlbV52MspE0uCocsx1f_rYvRenIN0Y3CCJAaDdWRwgiQG", + ]; + + bootnodes + .iter() + .filter_map(|enr| Enr::from_str(enr).ok()) + .collect() +} diff --git a/opstack/src/server/net/discovery.rs b/opstack/src/server/net/discovery.rs new file mode 100644 index 00000000..857d8f0f --- /dev/null +++ b/opstack/src/server/net/discovery.rs @@ -0,0 +1,137 @@ +use std::{ + net::{IpAddr, SocketAddr}, + time::Duration, +}; + +use alloy::{ + primitives::Bytes, + rlp::{self, Decodable}, +}; +use discv5::{ + enr::{Builder, CombinedKey, Enr, NodeId}, + ConfigBuilder, Discv5, ListenConfig, +}; +use eyre::Result; +use tokio::{ + sync::mpsc::{self, Receiver}, + time::sleep, +}; +use unsigned_varint::{decode, encode}; + +use super::bootnodes::bootnodes; + +/// Starts the [Discv5] discovery service and continually tries to find new peers. +/// Returns a [Receiver] to receive [Peer] structs +pub fn start(addr: SocketAddr, chain_id: u64) -> Result> { + let bootnodes = bootnodes(); + let mut disc = create_disc(addr, chain_id)?; + + let (sender, recv) = mpsc::channel::(256); + + tokio::spawn(async move { + bootnodes.into_iter().for_each(|enr| _ = disc.add_enr(enr)); + disc.start().await.unwrap(); + + tracing::info!("started peer discovery"); + + loop { + let target = NodeId::random(); + match disc.find_node(target).await { + Ok(nodes) => { + let peers = nodes + .iter() + .filter(|node| is_valid_node(node, chain_id)) + .flat_map(|peer| { + if let Some(ip) = peer.ip4() { + return Some(SocketAddr::new(IpAddr::V4(ip), peer.tcp4().unwrap())); + } + + if let Some(ip) = peer.ip6() { + return Some(SocketAddr::new(IpAddr::V6(ip), peer.tcp6().unwrap())); + } + + None + }); + + for peer in peers { + _ = sender.send(peer).await; + } + } + Err(err) => { + tracing::warn!("discovery error: {:?}", err); + } + } + + sleep(Duration::from_secs(1)).await; + } + }); + + Ok(recv) +} + +/// Returns `true` if a node [Enr] contains an `opstack` key and is on the same network. +fn is_valid_node(node: &Enr, chain_id: u64) -> bool { + node.get_raw_rlp("opstack") + .map(|opstack| { + OpStackEnrData::try_from(opstack) + .map(|opstack| opstack.chain_id == chain_id && opstack.version == 0) + .unwrap_or_default() + }) + .unwrap_or_default() +} + +/// Generates an [Enr] and creates a [Discv5] service struct +fn create_disc(addr: SocketAddr, chain_id: u64) -> Result { + let opstack = OpStackEnrData { + chain_id, + version: 0, + }; + let opstack_data: Vec = opstack.into(); + + let listen_config = ListenConfig::from(addr); + let config = ConfigBuilder::new(listen_config).build(); + let key = CombinedKey::generate_secp256k1(); + let enr = Builder::default() + .add_value_rlp("opstack", opstack_data.into()) + .build(&key)?; + + Discv5::new(enr, key, config).map_err(|_| eyre::eyre!("could not create disc service")) +} + +/// The unique L2 network identifier +#[derive(Debug)] +struct OpStackEnrData { + /// Chain ID + chain_id: u64, + /// The version. Always set to 0. + version: u64, +} + +impl TryFrom<&[u8]> for OpStackEnrData { + type Error = eyre::Report; + + /// Converts a slice of RLP encoded bytes to Op Stack Enr Data. + fn try_from(value: &[u8]) -> Result { + let mut buffer = value; + let bytes = Bytes::decode(&mut buffer)?; + let (chain_id, rest) = decode::u64(&bytes)?; + let (version, _) = decode::u64(rest)?; + + Ok(Self { chain_id, version }) + } +} + +impl From for Vec { + /// Converts Op Stack Enr data to a vector of bytes. + fn from(value: OpStackEnrData) -> Vec { + let mut chain_id_buf = encode::u128_buffer(); + let chain_id_slice = encode::u128(value.chain_id as u128, &mut chain_id_buf); + + let mut version_buf = encode::u128_buffer(); + let version_slice = encode::u128(value.version as u128, &mut version_buf); + + let opstack = [chain_id_slice, version_slice].concat(); + + rlp::encode(&opstack).to_vec() + } +} diff --git a/opstack/src/server/net/gossip.rs b/opstack/src/server/net/gossip.rs new file mode 100644 index 00000000..0a65b633 --- /dev/null +++ b/opstack/src/server/net/gossip.rs @@ -0,0 +1,233 @@ +use std::{ + net::{IpAddr, SocketAddr}, + time::Duration, +}; + +use eyre::Result; +use libp2p::{ + futures::StreamExt, + gossipsub::{self, IdentTopic, Message, MessageId}, + mplex::MplexConfig, + multiaddr::Protocol, + noise, ping, + swarm::{NetworkBehaviour, SwarmBuilder, SwarmEvent}, + tcp, Multiaddr, PeerId, Swarm, Transport, +}; +use libp2p_identity::Keypair; +use sha2::{Digest, Sha256}; +use tokio::select; + +use super::{block_handler::BlockHandler, discovery}; + +/// OP Stack gossip service +pub struct GossipService { + /// The socket address that the service is listening on. + addr: SocketAddr, + /// The chain ID of the network + chain_id: u64, + /// A unique keypair to validate the node's identity + keypair: Option, + /// Handler for the block + block_handler: BlockHandler, +} + +impl GossipService { + /// Creates a new [Service] + pub fn new(addr: SocketAddr, chain_id: u64, handler: BlockHandler) -> Self { + Self { + addr, + chain_id, + keypair: None, + block_handler: handler, + } + } + + /// Sets the keypair for [Service] + pub fn set_keypair(mut self, keypair: Keypair) -> Self { + self.keypair = Some(keypair); + self + } + + /// Starts the Discv5 peer discovery & libp2p services + /// and continually listens for new peers and messages to handle + pub fn start(self) -> Result<()> { + let keypair = self.keypair.unwrap_or_else(Keypair::generate_secp256k1); + + let mut swarm = create_swarm(keypair, &self.block_handler)?; + let mut peer_recv = discovery::start(self.addr, self.chain_id)?; + let multiaddr = socket_to_multiaddr(self.addr); + + swarm + .listen_on(multiaddr) + .map_err(|_| eyre::eyre!("swarm listen failed"))?; + + tokio::spawn(async move { + loop { + select! { + peer = peer_recv.recv() => { + if let Some(peer) = peer { + tracing::info!("adding peer"); + let peer = socket_to_multiaddr(peer); + _ = swarm.dial(peer); + } + }, + event = swarm.select_next_some() => { + if let SwarmEvent::Behaviour(event) = event { + event.handle(&mut swarm, &self.block_handler); + } + }, + } + } + }); + + Ok(()) + } +} + +fn socket_to_multiaddr(socket: SocketAddr) -> Multiaddr { + let mut multiaddr = Multiaddr::empty(); + match socket.ip() { + IpAddr::V4(ip) => multiaddr.push(Protocol::Ip4(ip)), + IpAddr::V6(ip) => multiaddr.push(Protocol::Ip6(ip)), + } + multiaddr.push(Protocol::Tcp(socket.port())); + multiaddr +} + +/// Computes the message ID of a `gossipsub` message +fn compute_message_id(msg: &Message) -> MessageId { + let mut decoder = snap::raw::Decoder::new(); + let id = match decoder.decompress_vec(&msg.data) { + Ok(data) => { + let domain_valid_snappy: Vec = vec![0x1, 0x0, 0x0, 0x0]; + let mut hasher = Sha256::new(); + hasher.update( + [domain_valid_snappy.as_slice(), data.as_slice()] + .concat() + .as_slice(), + ); + hasher.finalize()[..20].to_vec() + } + Err(_) => { + let domain_invalid_snappy: Vec = vec![0x0, 0x0, 0x0, 0x0]; + let mut hasher = Sha256::new(); + hasher.update( + [domain_invalid_snappy.as_slice(), msg.data.as_slice()] + .concat() + .as_slice(), + ); + hasher.finalize()[..20].to_vec() + } + }; + + MessageId(id) +} + +/// Creates the libp2p [Swarm] +fn create_swarm(keypair: Keypair, handler: &BlockHandler) -> Result> { + let transport = tcp::tokio::Transport::new(tcp::Config::default()) + .upgrade(libp2p::core::upgrade::Version::V1Lazy) + .authenticate(noise::Config::new(&keypair)?) + .multiplex(MplexConfig::default()) + .boxed(); + + let behaviour = Behaviour::new(handler)?; + + Ok( + SwarmBuilder::with_tokio_executor(transport, behaviour, PeerId::from(keypair.public())) + .build(), + ) +} + +/// Specifies the [NetworkBehaviour] of the node +#[derive(NetworkBehaviour)] +#[behaviour(out_event = "Event")] +struct Behaviour { + /// Adds [libp2p::ping] to respond to inbound pings, and send periodic outbound pings + ping: ping::Behaviour, + /// Adds [libp2p::gossipsub] to enable gossipsub as the routing layer + gossipsub: gossipsub::Behaviour, +} + +impl Behaviour { + /// Configures the swarm behaviors, subscribes to the gossip topics, and returns a new [Behaviour] + fn new(handler: &BlockHandler) -> Result { + let ping = ping::Behaviour::default(); + + let gossipsub_config = gossipsub::ConfigBuilder::default() + .mesh_n(8) + .mesh_n_low(6) + .mesh_n_high(12) + .gossip_lazy(6) + .heartbeat_interval(Duration::from_millis(500)) + .fanout_ttl(Duration::from_secs(24)) + .history_length(12) + .history_gossip(3) + .duplicate_cache_time(Duration::from_secs(65)) + .validation_mode(gossipsub::ValidationMode::None) + .validate_messages() + .message_id_fn(compute_message_id) + .build() + .map_err(|_| eyre::eyre!("gossipsub config creation failed"))?; + + let mut gossipsub = + gossipsub::Behaviour::new(gossipsub::MessageAuthenticity::Anonymous, gossipsub_config) + .map_err(|_| eyre::eyre!("gossipsub behaviour creation failed"))?; + + handler + .topics() + .iter() + .map(|topic| { + let topic = IdentTopic::new(topic.to_string()); + gossipsub + .subscribe(&topic) + .map_err(|_| eyre::eyre!("subscription failed")) + }) + .collect::>>()?; + + Ok(Self { ping, gossipsub }) + } +} + +/// The type of message received +enum Event { + /// Represents a [ping::Event] + #[allow(dead_code)] + Ping(ping::Event), + /// Represents a [gossipsub::Event] + Gossipsub(gossipsub::Event), +} + +impl Event { + /// Handles received gossipsub messages. Ping messages are ignored. + /// Reports back to [libp2p::gossipsub] to apply peer scoring and forward the message to other peers if accepted. + fn handle(self, swarm: &mut Swarm, handler: &BlockHandler) { + if let Self::Gossipsub(gossipsub::Event::Message { + propagation_source, + message_id, + message, + }) = self + { + let status = handler.handle(message); + + _ = swarm + .behaviour_mut() + .gossipsub + .report_message_validation_result(&message_id, &propagation_source, status); + } + } +} + +impl From for Event { + /// Converts [ping::Event] to [Event] + fn from(value: ping::Event) -> Self { + Event::Ping(value) + } +} + +impl From for Event { + /// Converts [gossipsub::Event] to [Event] + fn from(value: gossipsub::Event) -> Self { + Event::Gossipsub(value) + } +} diff --git a/opstack/src/server/net/mod.rs b/opstack/src/server/net/mod.rs new file mode 100644 index 00000000..d23dbaf4 --- /dev/null +++ b/opstack/src/server/net/mod.rs @@ -0,0 +1,4 @@ +pub mod block_handler; +mod bootnodes; +pub mod discovery; +pub mod gossip; diff --git a/opstack/src/spec.rs b/opstack/src/spec.rs new file mode 100644 index 00000000..338f0745 --- /dev/null +++ b/opstack/src/spec.rs @@ -0,0 +1,307 @@ +use alloy::{ + consensus::{ + BlobTransactionSidecar, Receipt, ReceiptWithBloom, TxReceipt, TxType, TypedTransaction, + }, + network::Network, + primitives::{Address, Bytes, ChainId, TxKind, U256}, + rpc::types::{AccessList, Log, TransactionRequest}, +}; +use helios_core::{network_spec::NetworkSpec, types::Block}; +use op_alloy_consensus::{ + OpDepositReceipt, OpDepositReceiptWithBloom, OpReceiptEnvelope, OpTxType, +}; +use op_alloy_network::{ + BuildResult, Ethereum, NetworkWallet, TransactionBuilder, TransactionBuilderError, +}; +use revm::primitives::{BlobExcessGasAndPrice, BlockEnv, TxEnv}; + +#[derive(Clone, Copy, Debug)] +pub struct OpStack; + +impl NetworkSpec for OpStack { + fn encode_receipt(receipt: &Self::ReceiptResponse) -> Vec { + let receipt = &receipt.inner.inner; + let bloom = receipt.bloom(); + let tx_type = receipt.tx_type(); + let logs = receipt + .logs() + .iter() + .map(|l| l.inner.clone()) + .collect::>(); + + let raw_encoded = match receipt { + OpReceiptEnvelope::Legacy(inner) + | OpReceiptEnvelope::Eip2930(inner) + | OpReceiptEnvelope::Eip1559(inner) + | OpReceiptEnvelope::Eip4844(inner) => { + let r = Receipt { + status: *inner.status_or_post_state(), + cumulative_gas_used: inner.cumulative_gas_used(), + logs, + }; + let rwb = ReceiptWithBloom::new(r, bloom); + alloy::rlp::encode(rwb) + } + OpReceiptEnvelope::Deposit(inner) => { + let r = Receipt { + status: inner.receipt.inner.status, + cumulative_gas_used: inner.receipt.inner.cumulative_gas_used, + logs, + }; + + let r = OpDepositReceipt { + inner: r, + deposit_nonce: inner.receipt.deposit_nonce, + deposit_receipt_version: inner.receipt.deposit_receipt_version, + }; + + let rwb = OpDepositReceiptWithBloom::new(r, bloom); + alloy::rlp::encode(rwb) + } + _ => panic!("unreachable"), + }; + + match tx_type { + OpTxType::Legacy => raw_encoded, + _ => [vec![tx_type as u8], raw_encoded].concat(), + } + } + + fn receipt_contains(list: &[Self::ReceiptResponse], elem: &Self::ReceiptResponse) -> bool { + for receipt in list { + if receipt == elem { + return true; + } + } + + false + } + + fn receipt_logs(receipt: &Self::ReceiptResponse) -> Vec { + receipt.inner.inner.logs().to_vec() + } + + fn tx_env(tx: &Self::TransactionRequest) -> TxEnv { + let mut tx_env = TxEnv::default(); + tx_env.caller = tx.from.unwrap_or_default(); + tx_env.gas_limit = >::gas_limit(tx) + .map(|v| v as u64) + .unwrap_or(u64::MAX); + tx_env.gas_price = >::gas_price(tx) + .map(U256::from) + .unwrap_or_default(); + tx_env.transact_to = tx.to.unwrap_or_default(); + tx_env.value = tx.value.unwrap_or_default(); + tx_env.data = >::input(tx) + .unwrap_or_default() + .clone(); + tx_env.nonce = >::nonce(tx); + tx_env.chain_id = >::chain_id(tx); + tx_env.access_list = >::access_list(tx) + .map(|v| v.to_vec()) + .unwrap_or_default(); + tx_env.gas_priority_fee = + >::max_priority_fee_per_gas(tx) + .map(U256::from); + tx_env.max_fee_per_blob_gas = + >::max_fee_per_gas(tx).map(U256::from); + tx_env.blob_hashes = tx + .blob_versioned_hashes + .as_ref() + .map(|v| v.to_vec()) + .unwrap_or_default(); + + tx_env + } + + fn block_env(block: &Block) -> BlockEnv { + let mut block_env = BlockEnv::default(); + block_env.number = block.number.to(); + block_env.coinbase = block.miner; + block_env.timestamp = block.timestamp.to(); + block_env.gas_limit = block.gas_limit.to(); + block_env.basefee = block.base_fee_per_gas; + block_env.difficulty = block.difficulty; + block_env.prevrandao = Some(block.mix_hash); + block_env.blob_excess_gas_and_price = block + .excess_blob_gas + .map(|v| BlobExcessGasAndPrice::new(v.to())); + + block_env + } +} + +impl Network for OpStack { + type TxType = op_alloy_consensus::OpTxType; + type TxEnvelope = alloy::consensus::TxEnvelope; + type UnsignedTx = alloy::consensus::TypedTransaction; + type ReceiptEnvelope = op_alloy_consensus::OpReceiptEnvelope; + type Header = alloy::consensus::Header; + type TransactionRequest = TransactionRequest; + type TransactionResponse = alloy::rpc::types::Transaction; + type ReceiptResponse = op_alloy_rpc_types::OpTransactionReceipt; + type HeaderResponse = alloy::rpc::types::Header; +} + +impl TransactionBuilder for TransactionRequest { + fn chain_id(&self) -> Option { + self.chain_id + } + + fn set_chain_id(&mut self, chain_id: ChainId) { + self.chain_id = Some(chain_id); + } + + fn nonce(&self) -> Option { + self.nonce + } + + fn set_nonce(&mut self, nonce: u64) { + self.nonce = Some(nonce); + } + + fn input(&self) -> Option<&Bytes> { + self.input.input() + } + + fn set_input>(&mut self, input: T) { + self.input.input = Some(input.into()); + } + + fn from(&self) -> Option
{ + self.from + } + + fn set_from(&mut self, from: Address) { + self.from = Some(from); + } + + fn kind(&self) -> Option { + self.to + } + + fn clear_kind(&mut self) { + self.to = None; + } + + fn set_kind(&mut self, kind: TxKind) { + self.to = Some(kind); + } + + fn value(&self) -> Option { + self.value + } + + fn set_value(&mut self, value: U256) { + self.value = Some(value) + } + + fn gas_price(&self) -> Option { + self.gas_price + } + + fn set_gas_price(&mut self, gas_price: u128) { + self.gas_price = Some(gas_price); + } + + fn max_fee_per_gas(&self) -> Option { + self.max_fee_per_gas + } + + fn set_max_fee_per_gas(&mut self, max_fee_per_gas: u128) { + self.max_fee_per_gas = Some(max_fee_per_gas); + } + + fn max_priority_fee_per_gas(&self) -> Option { + self.max_priority_fee_per_gas + } + + fn set_max_priority_fee_per_gas(&mut self, max_priority_fee_per_gas: u128) { + self.max_priority_fee_per_gas = Some(max_priority_fee_per_gas); + } + + fn max_fee_per_blob_gas(&self) -> Option { + self.max_fee_per_blob_gas + } + + fn set_max_fee_per_blob_gas(&mut self, max_fee_per_blob_gas: u128) { + self.max_fee_per_blob_gas = Some(max_fee_per_blob_gas) + } + + fn gas_limit(&self) -> Option { + self.gas + } + + fn set_gas_limit(&mut self, gas_limit: u128) { + self.gas = Some(gas_limit); + } + + fn access_list(&self) -> Option<&AccessList> { + self.access_list.as_ref() + } + + fn set_access_list(&mut self, access_list: AccessList) { + self.access_list = Some(access_list); + } + + fn blob_sidecar(&self) -> Option<&BlobTransactionSidecar> { + self.sidecar.as_ref() + } + + fn set_blob_sidecar(&mut self, sidecar: BlobTransactionSidecar) { + TransactionBuilder::::set_blob_sidecar(self, sidecar) + } + + fn complete_type(&self, ty: OpTxType) -> Result<(), Vec<&'static str>> { + match ty { + OpTxType::Deposit => Err(vec!["not implemented for deposit tx"]), + _ => { + let ty = TxType::try_from(ty as u8).unwrap(); + TransactionBuilder::::complete_type(self, ty) + } + } + } + + fn can_submit(&self) -> bool { + TransactionBuilder::::can_submit(self) + } + + fn can_build(&self) -> bool { + TransactionBuilder::::can_build(self) + } + + #[doc(alias = "output_transaction_type")] + fn output_tx_type(&self) -> OpTxType { + OpTxType::try_from(self.preferred_type() as u8).unwrap() + } + + #[doc(alias = "output_transaction_type_checked")] + fn output_tx_type_checked(&self) -> Option { + self.buildable_type() + .map(|tx_ty| OpTxType::try_from(tx_ty as u8).unwrap()) + } + + fn prep_for_submission(&mut self) { + self.transaction_type = Some(self.preferred_type() as u8); + self.trim_conflicting_keys(); + self.populate_blob_hashes(); + } + + fn build_unsigned(self) -> BuildResult { + if let Err((tx_type, missing)) = self.missing_keys() { + let tx_type = OpTxType::try_from(tx_type as u8).unwrap(); + return Err( + TransactionBuilderError::InvalidTransactionRequest(tx_type, missing) + .into_unbuilt(self), + ); + } + Ok(self.build_typed_tx().expect("checked by missing_keys")) + } + + async fn build>( + self, + wallet: &W, + ) -> Result<::TxEnvelope, TransactionBuilderError> { + Ok(wallet.sign_request(self).await?) + } +} diff --git a/opstack/src/types.rs b/opstack/src/types.rs new file mode 100644 index 00000000..be8c0d79 --- /dev/null +++ b/opstack/src/types.rs @@ -0,0 +1,36 @@ +use alloy::primitives::{Address, B256, U256}; +use ssz_derive::{Decode, Encode}; +use ssz_types::{FixedVector, VariableList}; + +#[derive(Debug, Clone, Encode, Decode)] +pub struct ExecutionPayload { + pub parent_hash: B256, + pub fee_recipient: Address, + pub state_root: B256, + pub receipts_root: B256, + pub logs_bloom: LogsBloom, + pub prev_randao: B256, + pub block_number: u64, + pub gas_limit: u64, + pub gas_used: u64, + pub timestamp: u64, + pub extra_data: ExtraData, + pub base_fee_per_gas: U256, + pub block_hash: B256, + pub transactions: VariableList, + pub withdrawals: VariableList, + pub blob_gas_used: u64, + pub excess_blob_gas: u64, +} + +pub type Transaction = VariableList; +pub type LogsBloom = FixedVector; +pub type ExtraData = VariableList; + +#[derive(Clone, Debug, Encode, Decode)] +pub struct Withdrawal { + index: u64, + validator_index: u64, + address: Address, + amount: u64, +}