diff --git a/Cargo.lock b/Cargo.lock index b40ba33..fd53deb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -439,7 +439,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tracing", "wasmtimer", ] @@ -487,7 +487,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tracing", "tracing-futures", "url", @@ -783,7 +783,7 @@ dependencies = [ "serde_json", "thiserror 2.0.12", "tokio", - "tower", + "tower 0.5.2", "tracing", "url", "wasmtimer", @@ -799,7 +799,7 @@ dependencies = [ "alloy-transport", "reqwest 0.12.22", "serde_json", - "tower", + "tower 0.5.2", "tracing", "url", ] @@ -1280,6 +1280,27 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "155a5a185e42c6b77ac7b88a15143d930a9e9727a5b7b77eed417404ab15c247" +[[package]] +name = "assert-json-diff" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + [[package]] name = "async-compression" version = "0.4.25" @@ -1385,7 +1406,7 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302eaff5357a264a2c42f127ecb8bac761cf99749fc3dc95677e2743991f99e7" dependencies = [ - "fastrand", + "fastrand 2.3.0", "tokio", ] @@ -1416,6 +1437,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.21.7" @@ -1908,6 +1935,15 @@ dependencies = [ "digest 0.10.7", ] +[[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 = "console" version = "0.15.11" @@ -2260,6 +2296,25 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "deadpool" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" +dependencies = [ + "async-trait", + "deadpool-runtime", + "num_cpus", + "retain_mut", + "tokio", +] + +[[package]] +name = "deadpool-runtime" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" + [[package]] name = "debug-helper" version = "0.3.13" @@ -2277,6 +2332,17 @@ dependencies = [ "tokio-util", ] +[[package]] +name = "delegate" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b6483c2bbed26f97861cf57651d4f2b731964a28cd2257f934a4b452480d21" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.104", +] + [[package]] name = "der" version = "0.7.10" @@ -2738,6 +2804,12 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + [[package]] name = "eyre" version = "0.6.12" @@ -2748,6 +2820,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -2935,6 +3016,21 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.31" @@ -3018,6 +3114,17 @@ dependencies = [ "zeroize", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.16" @@ -3136,6 +3243,26 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "governor" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" +dependencies = [ + "cfg-if", + "dashmap 5.5.3", + "futures", + "futures-timer", + "no-std-compat", + "nonzero_ext", + "parking_lot", + "portable-atomic", + "quanta", + "rand 0.8.5", + "smallvec", + "spinning_top", +] + [[package]] name = "group" version = "0.13.0" @@ -3401,6 +3528,27 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" +[[package]] +name = "http-types" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" +dependencies = [ + "anyhow", + "async-channel", + "base64 0.13.1", + "futures-lite", + "http 0.2.12", + "infer", + "pin-project-lite", + "rand 0.7.3", + "serde", + "serde_json", + "serde_qs", + "serde_urlencoded", + "url", +] + [[package]] name = "httparse" version = "1.10.1" @@ -3757,6 +3905,12 @@ version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" +[[package]] +name = "infer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" + [[package]] name = "inotify" version = "0.11.0" @@ -3800,6 +3954,15 @@ dependencies = [ "syn 2.0.104", ] +[[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.3" @@ -4001,7 +4164,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tracing", "wasm-bindgen-futures", ] @@ -4025,7 +4188,7 @@ dependencies = [ "serde_json", "thiserror 2.0.12", "tokio", - "tower", + "tower 0.5.2", "url", ] @@ -4065,7 +4228,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", - "tower", + "tower 0.5.2", "tracing", ] @@ -4090,7 +4253,7 @@ dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", - "tower", + "tower 0.5.2", ] [[package]] @@ -4103,7 +4266,7 @@ dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", - "tower", + "tower 0.5.2", "url", ] @@ -4423,11 +4586,14 @@ dependencies = [ "reth-ethereum", "reth-ethereum-cli", "reth-ethereum-payload-builder", + "reth-network", + "reth-node-api", "reth-node-builder", "reth-payload-builder", "reth-primitives-traits", "reth-provider", "reth-revm", + "reth-transaction-pool", "reth-trie-db", "serde", "thiserror 2.0.12", @@ -4448,18 +4614,30 @@ name = "lumen-node" version = "0.1.0" dependencies = [ "alloy-consensus", + "alloy-dyn-abi", "alloy-eips", "alloy-genesis", + "alloy-json-rpc", "alloy-primitives", "alloy-rlp", "alloy-rpc-types", "alloy-rpc-types-engine", + "alloy-serde", "async-trait", + "bytes", + "delegate", "eyre", "futures", + "governor", "hex", + "jsonrpsee", "lumen-common", "lumen-rollkit", + "metrics 0.22.4", + "once_cell", + "parking_lot", + "prometheus", + "reqwest 0.11.27", "reth-basic-payload-builder", "reth-chainspec", "reth-consensus", @@ -4467,11 +4645,14 @@ dependencies = [ "reth-engine-local", "reth-engine-primitives", "reth-errors", + "reth-ethereum", "reth-ethereum-payload-builder", "reth-ethereum-primitives", "reth-evm", "reth-evm-ethereum", "reth-execution-types", + "reth-metrics", + "reth-network", "reth-node-api", "reth-node-builder", "reth-node-core", @@ -4483,9 +4664,12 @@ dependencies = [ "reth-primitives-traits", "reth-provider", "reth-revm", + "reth-rpc", "reth-rpc-api", "reth-rpc-builder", "reth-rpc-engine-api", + "reth-rpc-eth-api", + "reth-rpc-eth-types", "reth-tasks", "reth-testing-utils", "reth-tracing", @@ -4496,7 +4680,9 @@ dependencies = [ "tempfile", "thiserror 2.0.12", "tokio", + "tower 0.4.13", "tracing", + "wiremock", ] [[package]] @@ -4638,6 +4824,16 @@ dependencies = [ "libc", ] +[[package]] +name = "metrics" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d05972e8cbac2671e85aa9d04d9160d193f8bebd1a5c1a2f4542c62e65d1d0" +dependencies = [ + "ahash", + "portable-atomic", +] + [[package]] name = "metrics" version = "0.24.2" @@ -4668,7 +4864,7 @@ checksum = "dd7399781913e5393588a8d8c6a2867bf85fb38eaf2502fdce465aad2dc6f034" dependencies = [ "base64 0.22.1", "indexmap 2.10.0", - "metrics", + "metrics 0.24.2", "metrics-util", "quanta", "thiserror 1.0.69", @@ -4683,7 +4879,7 @@ dependencies = [ "libc", "libproc", "mach2", - "metrics", + "metrics 0.24.2", "once_cell", "procfs", "rlimit", @@ -4699,7 +4895,7 @@ dependencies = [ "crossbeam-epoch", "crossbeam-utils", "hashbrown 0.15.4", - "metrics", + "metrics 0.24.2", "quanta", "rand 0.9.1", "rand_xoshiro", @@ -4867,6 +5063,12 @@ dependencies = [ "tempfile", ] +[[package]] +name = "no-std-compat" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" + [[package]] name = "nom" version = "7.1.3" @@ -4877,6 +5079,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nonzero_ext" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" + [[package]] name = "notify" version = "8.0.0" @@ -5244,6 +5452,12 @@ dependencies = [ "syn 2.0.104", ] +[[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.12.4" @@ -5547,6 +5761,21 @@ dependencies = [ "hex", ] +[[package]] +name = "prometheus" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d33c28a30771f7f96db69893f78b857f7450d7e0237e9c8fc6427a81bae7ed1" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "memchr", + "parking_lot", + "protobuf", + "thiserror 1.0.69", +] + [[package]] name = "proptest" version = "1.7.0" @@ -5588,6 +5817,12 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "protobuf" +version = "2.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" + [[package]] name = "pulldown-cmark" version = "0.9.6" @@ -5705,6 +5940,19 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + [[package]] name = "rand" version = "0.8.5" @@ -5728,6 +5976,16 @@ dependencies = [ "serde", ] +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -5748,6 +6006,15 @@ dependencies = [ "rand_core 0.9.3", ] +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -5767,6 +6034,15 @@ dependencies = [ "serde", ] +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + [[package]] name = "rand_xorshift" version = "0.4.0" @@ -6008,7 +6284,7 @@ dependencies = [ "tokio", "tokio-rustls", "tokio-util", - "tower", + "tower 0.5.2", "tower-http", "tower-service", "url", @@ -6025,6 +6301,12 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95325155c684b1c89f7765e30bc1c42e4a6da51ca513615660cb8a62ef9a88e3" +[[package]] +name = "retain_mut" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" + [[package]] name = "reth-basic-payload-builder" version = "1.5.0" @@ -6035,7 +6317,7 @@ dependencies = [ "alloy-primitives", "futures-core", "futures-util", - "metrics", + "metrics 0.24.2", "reth-chain-state", "reth-metrics", "reth-payload-builder", @@ -6060,7 +6342,7 @@ dependencies = [ "alloy-signer", "alloy-signer-local", "derive_more", - "metrics", + "metrics 0.24.2", "parking_lot", "pin-project", "rand 0.9.1", @@ -6324,7 +6606,7 @@ dependencies = [ "alloy-primitives", "derive_more", "eyre", - "metrics", + "metrics 0.24.2", "page_size", "parking_lot", "reth-db-api", @@ -6353,7 +6635,7 @@ dependencies = [ "arbitrary", "bytes", "derive_more", - "metrics", + "metrics 0.24.2", "modular-bitfield", "parity-scale-codec", "proptest", @@ -6452,7 +6734,7 @@ dependencies = [ "enr", "futures", "itertools 0.14.0", - "metrics", + "metrics 0.24.2", "rand 0.9.1", "reth-chainspec", "reth-ethereum-forks", @@ -6500,7 +6782,7 @@ dependencies = [ "futures", "futures-util", "itertools 0.14.0", - "metrics", + "metrics 0.24.2", "pin-project", "rayon", "reth-config", @@ -6633,7 +6915,7 @@ dependencies = [ "derive_more", "futures", "itertools 0.14.0", - "metrics", + "metrics 0.24.2", "mini-moka", "parking_lot", "rayon", @@ -7023,7 +7305,7 @@ dependencies = [ "auto_impl", "derive_more", "futures-util", - "metrics", + "metrics 0.24.2", "reth-execution-errors", "reth-execution-types", "reth-metrics", @@ -7095,7 +7377,7 @@ dependencies = [ "eyre", "futures", "itertools 0.14.0", - "metrics", + "metrics 0.24.2", "parking_lot", "reth-chain-state", "reth-chainspec", @@ -7190,7 +7472,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", - "tower", + "tower 0.5.2", "tracing", ] @@ -7226,7 +7508,7 @@ version = "1.5.0" source = "git+https://github.com/paradigmxyz/reth.git#60940dd243918a1ba55f5cce5046a443bc75611d" dependencies = [ "futures", - "metrics", + "metrics 0.24.2", "metrics-derive", "tokio", "tokio-util", @@ -7270,7 +7552,7 @@ dependencies = [ "enr", "futures", "itertools 0.14.0", - "metrics", + "metrics 0.24.2", "parking_lot", "pin-project", "rand 0.8.5", @@ -7607,7 +7889,7 @@ dependencies = [ "eyre", "http 1.3.1", "jsonrpsee-server", - "metrics", + "metrics 0.24.2", "metrics-exporter-prometheus", "metrics-process", "metrics-util", @@ -7616,7 +7898,7 @@ dependencies = [ "reth-tasks", "tikv-jemalloc-ctl", "tokio", - "tower", + "tower 0.5.2", "tracing", ] @@ -7660,7 +7942,7 @@ dependencies = [ "alloy-consensus", "alloy-rpc-types", "futures-util", - "metrics", + "metrics 0.24.2", "reth-chain-state", "reth-ethereum-engine-primitives", "reth-metrics", @@ -7772,7 +8054,7 @@ dependencies = [ "dashmap 6.1.0", "eyre", "itertools 0.14.0", - "metrics", + "metrics 0.24.2", "notify", "parking_lot", "rayon", @@ -7814,7 +8096,7 @@ dependencies = [ "alloy-eips", "alloy-primitives", "itertools 0.14.0", - "metrics", + "metrics 0.24.2", "rayon", "reth-chainspec", "reth-config", @@ -7931,7 +8213,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tokio-stream", - "tower", + "tower 0.5.2", "tracing", "tracing-futures", ] @@ -7973,7 +8255,7 @@ dependencies = [ "alloy-provider", "http 1.3.1", "jsonrpsee", - "metrics", + "metrics 0.24.2", "pin-project", "reth-chain-state", "reth-chainspec", @@ -7997,7 +8279,7 @@ dependencies = [ "thiserror 2.0.12", "tokio", "tokio-util", - "tower", + "tower 0.5.2", "tower-http", "tracing", ] @@ -8030,7 +8312,7 @@ dependencies = [ "async-trait", "jsonrpsee-core", "jsonrpsee-types", - "metrics", + "metrics 0.24.2", "parking_lot", "reth-chainspec", "reth-engine-primitives", @@ -8110,7 +8392,7 @@ dependencies = [ "itertools 0.14.0", "jsonrpsee-core", "jsonrpsee-types", - "metrics", + "metrics 0.24.2", "rand 0.9.1", "reth-chain-state", "reth-chainspec", @@ -8146,7 +8428,7 @@ dependencies = [ "http 1.3.1", "jsonrpsee-http-client", "pin-project", - "tower", + "tower 0.5.2", "tower-http", "tracing", ] @@ -8223,7 +8505,7 @@ dependencies = [ "aquamarine", "auto_impl", "futures-util", - "metrics", + "metrics 0.24.2", "reth-consensus", "reth-errors", "reth-metrics", @@ -8334,7 +8616,7 @@ dependencies = [ "auto_impl", "dyn-clone", "futures-util", - "metrics", + "metrics 0.24.2", "pin-project", "rayon", "reth-metrics", @@ -8398,7 +8680,7 @@ dependencies = [ "auto_impl", "bitflags 2.9.1", "futures-util", - "metrics", + "metrics 0.24.2", "parking_lot", "proptest", "proptest-arbitrary-interop", @@ -8437,7 +8719,7 @@ dependencies = [ "alloy-trie", "auto_impl", "itertools 0.14.0", - "metrics", + "metrics 0.24.2", "reth-execution-errors", "reth-metrics", "reth-primitives-traits", @@ -8498,7 +8780,7 @@ dependencies = [ "alloy-rlp", "derive_more", "itertools 0.14.0", - "metrics", + "metrics 0.24.2", "rayon", "reth-db-api", "reth-execution-errors", @@ -8523,7 +8805,7 @@ dependencies = [ "alloy-rlp", "alloy-trie", "auto_impl", - "metrics", + "metrics 0.24.2", "reth-execution-errors", "reth-metrics", "reth-primitives-traits", @@ -9307,6 +9589,17 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_qs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror 1.0.69", +] + [[package]] name = "serde_spanned" version = "0.6.9" @@ -9597,6 +9890,15 @@ dependencies = [ "sha1", ] +[[package]] +name = "spinning_top" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -9798,7 +10100,7 @@ version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "fastrand", + "fastrand 2.3.0", "getrandom 0.3.3", "once_cell", "rustix 1.0.7", @@ -10096,6 +10398,21 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "tower" version = "0.5.2" @@ -10140,7 +10457,7 @@ dependencies = [ "pin-project-lite", "tokio", "tokio-util", - "tower", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -10609,6 +10926,12 @@ dependencies = [ "libc", ] +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + [[package]] name = "walkdir" version = "2.5.0" @@ -10628,6 +10951,12 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -11368,6 +11697,28 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wiremock" +version = "0.5.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a3a53eaf34f390dd30d7b1b078287dd05df2aa2e21a589ccb80f5c7253c2e9" +dependencies = [ + "assert-json-diff", + "async-trait", + "base64 0.21.7", + "deadpool", + "futures", + "futures-timer", + "http-types", + "hyper 0.14.32", + "log", + "once_cell", + "regex", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/Cargo.toml b/Cargo.toml index 3b337b1..3964a77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,71 +1,80 @@ [workspace] +members = [ + "bin/lumen", + "crates/common", + "crates/node", + "crates/rollkit", + "crates/tests", +] resolver = "2" -members = ["bin/lumen", "crates/common", "crates/node", "crates/rollkit", "crates/tests"] [workspace.package] -version = "0.1.0" +authors = ["Rollkit Contributors"] edition = "2021" -rust-version = "1.82" -license = "MIT OR Apache-2.0" homepage = "https://github.com/rollkit/lumen" +license = "MIT OR Apache-2.0" repository = "https://github.com/rollkit/lumen" -authors = ["Rollkit Contributors"] +rust-version = "1.82" +version = "0.1.0" [workspace.dependencies] # Reth dependencies - Using the latest stable versions +reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-chain-state = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-chainspec = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-cli = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-cli-util = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-node-api = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-node-builder = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-engine-local = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-engine-primitives = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-errors = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-optimism-node = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-trie-db = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-trie-common = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-rpc-eth-types = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-provider = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-storage-api = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-tracing = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-network = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-network-types = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-chain-state = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-ethereum = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-ethereum-cli = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-engine-local = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } -reth-engine-primitives = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-ethereum-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-ethereum-primitives = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-evm = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-evm-ethereum = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-execution-types = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-metrics = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-network = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-network-types = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-node-api = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-node-builder = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-node-core = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-node-types = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-optimism-node = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-optimism-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-payload-builder = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-payload-builder-primitives = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-primitives = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-primitives-traits = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-provider = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-revm = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-rpc = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-rpc-api = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-rpc-builder = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth-rpc-engine-api = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-rpc-eth-api = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-rpc-eth-types = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-storage-api = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-tracing = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-trie-common = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +reth-trie-db = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } reth_ethereum_primitives = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } +# Additional reth dependencies +reth-tasks = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0" } # Test dependencies reth-consensus = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0", default-features = false } -reth-testing-utils = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0", default-features = false } reth-db = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0", default-features = false } -reth-tasks = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0", default-features = false } +reth-testing-utils = { git = "https://github.com/paradigmxyz/reth.git", version = "1.5.0", default-features = false } # Alloy dependencies alloy = { version = "1.0.17", features = [ @@ -75,46 +84,48 @@ alloy = { version = "1.0.17", features = [ "signers", "reqwest-rustls-tls", ], default-features = false } +alloy-consensus = { version = "1.0.17", default-features = false } +alloy-dyn-abi = { version = "1.0.17", default-features = false } alloy-eips = { version = "1.0.17", default-features = false } +alloy-genesis = { version = "1.0.17", default-features = false } +alloy-json-rpc = { version = "1.0.17", default-features = false } alloy-network = { version = "1.0.17", default-features = false } +alloy-primitives = { version = "1.2.0", default-features = false } alloy-provider = { version = "1.0.17", default-features = false } +alloy-rlp = { version = "0.3", default-features = false } alloy-rpc-client = { version = "1.0.17", default-features = false } alloy-rpc-types = { version = "1.0.17", default-features = false } -alloy-json-rpc = { version = "1.0.17", default-features = false } -alloy-rpc-types-eth = { version = "1.0.17", default-features = false } alloy-rpc-types-engine = { version = "1.0.17", default-features = false } +alloy-rpc-types-eth = { version = "1.0.17", default-features = false } +alloy-serde = { version = "1.0.17", default-features = false } alloy-signer-local = { version = "1.0.17", features = ["mnemonic"] } -alloy-primitives = { version = "1.2.0", default-features = false } -alloy-consensus = { version = "1.0.17", default-features = false } -alloy-rlp = { version = "0.3", default-features = false } -alloy-genesis = { version = "1.0.17", default-features = false } # Core dependencies +async-trait = "0.1" +clap = { version = "4.5", features = ["derive", "env"] } eyre = "0.6" -tracing = "0.1" -tokio = { version = "1.38", features = ["full"] } +futures = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "2.0" -async-trait = "0.1" -futures = "0.3" -clap = { version = "4.5", features = ["derive", "env"] } +tokio = { version = "1.38", features = ["full"] } +tracing = "0.1" # Additional dependencies -reqwest = { version = "0.11", features = ["json"] } chrono = { version = "0.4", features = ["serde"] } +hex = "0.4" rand = "0.8" +reqwest = { version = "0.11", features = ["json"] } tempfile = "3.10" -hex = "0.4" [workspace.lints] rust.missing_debug_implementations = "warn" rust.missing_docs = "warn" rust.rust_2018_idioms = { level = "deny", priority = -1 } +rust.unnameable-types = "warn" rust.unreachable_pub = "warn" rust.unused_must_use = "deny" rustdoc.all = "warn" -rust.unnameable-types = "warn" [workspace.lints.clippy] # These are some of clippy's nursery (i.e., experimental) lints that we like. @@ -202,15 +213,15 @@ significant_drop_tightening = "allow" too_long_first_doc_paragraph = "allow" [profile.release] -opt-level = 3 +codegen-units = 1 lto = "thin" +opt-level = 3 strip = "debuginfo" -codegen-units = 1 # Memory-optimized release profile [profile.docker] -inherits = "release" -opt-level = 2 -lto = false codegen-units = 1 incremental = false +inherits = "release" +lto = false +opt-level = 2 diff --git a/bin/lumen/Cargo.toml b/bin/lumen/Cargo.toml index 2e6188b..23bca45 100644 --- a/bin/lumen/Cargo.toml +++ b/bin/lumen/Cargo.toml @@ -32,6 +32,9 @@ reth-payload-builder.workspace = true reth-revm.workspace = true reth-provider.workspace = true reth-trie-db.workspace = true +reth-network.workspace = true +reth-node-api.workspace = true +reth-transaction-pool.workspace = true # Alloy dependencies alloy-network.workspace = true diff --git a/bin/lumen/src/main.rs b/bin/lumen/src/main.rs index 31f3199..2a21a32 100644 --- a/bin/lumen/src/main.rs +++ b/bin/lumen/src/main.rs @@ -32,14 +32,14 @@ use reth_ethereum::{ NodeTypes, PayloadAttributes, PayloadBuilderAttributes, PayloadTypes, PayloadValidator, }, builder::{ - components::{BasicPayloadServiceBuilder, ComponentsBuilder, PayloadBuilderBuilder}, + components::{ + BasicPayloadServiceBuilder, ComponentsBuilder, NetworkBuilder, + PayloadBuilderBuilder, + }, rpc::{EngineValidatorBuilder, RpcAddOns}, BuilderContext, Node, NodeAdapter, NodeComponentsBuilder, }, - node::{ - EthereumConsensusBuilder, EthereumExecutorBuilder, EthereumNetworkBuilder, - EthereumPoolBuilder, - }, + node::{EthereumConsensusBuilder, EthereumExecutorBuilder, EthereumPoolBuilder}, EthEvmConfig, EthereumEthApiBuilder, }, pool::{PoolTransaction, TransactionPool}, @@ -48,9 +48,12 @@ use reth_ethereum::{ }; use reth_ethereum_cli::{chainspec::EthereumChainSpecParser, Cli}; use reth_ethereum_payload_builder::EthereumExecutionPayloadValidator; +use reth_network::{types::BasicNetworkPrimitives, NetworkHandle, PeersInfo}; +use reth_node_api::{PrimitivesTy, TxTy}; use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes, PayloadBuilderError}; use reth_provider::HeaderProvider; use reth_revm::cached::CachedReads; +use reth_transaction_pool::PoolPooledTx; use reth_trie_db::MerklePatriciaTrie; use serde::{Deserialize, Serialize}; use std::sync::Arc; @@ -70,6 +73,33 @@ pub struct RollkitArgs { help = "Enable Rollkit integration for transaction processing via Engine API" )] pub enable_rollkit: bool, + + /// Optional sequencer HTTP endpoint (e.g. ). + /// If unset, Lumen behaves as today (transactions land in local pool). + #[arg( + long = "sequencer-http", + value_name = "URI", + help = "Forward raw transactions to the given sequencer JSON-RPC endpoint" + )] + pub sequencer_http: Option, + + /// Optional Basic-Auth header (e.g. \"Basic dXNlcjpwYXNz\"). + /// Can also be supplied via the `SEQUENCER_AUTH` env var. + #[arg( + long = "sequencer-auth", + env = "SEQUENCER_AUTH", + value_name = "BASIC_AUTH", + help = "Basic-Auth string to attach when forwarding to the sequencer" + )] + pub sequencer_auth: Option, + + /// Disable local tx-pool gossip while forwarding to a sequencer (OP-Stack parity) + #[arg( + long = "disable-tx-pool-gossip", + default_value_t = false, + help = "Disable mempool gossip when --sequencer-http is set" + )] + pub disable_tx_pool_gossip: bool, } /// Rollkit payload attributes that support passing transactions via Engine API @@ -353,12 +383,20 @@ where pub struct RollkitNode { /// Rollkit-specific arguments pub args: RollkitArgs, + /// Transaction forwarding configuration + pub forwarding: lumen_node::ForwardingConfig, } impl RollkitNode { /// Create a new rollkit node with the given arguments - pub const fn new(args: RollkitArgs) -> Self { - Self { args } + pub fn new(args: RollkitArgs) -> Self { + let forwarding = lumen_node::ForwardingConfig { + sequencer_http: args.sequencer_http.clone(), + sequencer_auth: args.sequencer_auth.clone(), + disable_tx_pool_gossip: args.disable_tx_pool_gossip, + ..Default::default() + }; + Self { args, forwarding } } } @@ -370,7 +408,56 @@ impl NodeTypes for RollkitNode { type Payload = RollkitEngineTypes; } -/// Rollkit node addons configuring RPC types with custom engine validator +/// Rollkit network builder that optionally disables transaction pool gossip +#[derive(Debug, Clone, Default)] +pub struct RollkitNetworkBuilder { + /// Whether to disable transaction pool gossip + disable_tx_pool_gossip: bool, +} + +impl RollkitNetworkBuilder { + /// Create a new network builder with the given gossip configuration + pub const fn new(disable_tx_pool_gossip: bool) -> Self { + Self { + disable_tx_pool_gossip, + } + } +} + +impl NetworkBuilder for RollkitNetworkBuilder +where + Node: FullNodeTypes>, + Pool: TransactionPool>> + + Unpin + + 'static, +{ + type Network = + NetworkHandle, PoolPooledTx>>; + + async fn build_network( + self, + ctx: &BuilderContext, + pool: Pool, + ) -> eyre::Result { + // Configure the network with optional tx gossip disabled + let network_config = ctx + .network_config_builder()? + .disable_tx_gossip(self.disable_tx_pool_gossip) + .build(ctx.provider().clone()); + + if self.disable_tx_pool_gossip { + info!("Rollkit: Transaction pool gossip disabled"); + } + + // Build the network + let network = reth_network::NetworkManager::builder(network_config).await?; + let handle = ctx.start_network(network, pool); + + info!(target: "reth::cli", enode=%handle.local_node_record(), "P2P networking initialized"); + Ok(handle) + } +} + pub type RollkitNodeAddOns = RpcAddOns; impl Node for RollkitNode @@ -388,7 +475,7 @@ where N, EthereumPoolBuilder, BasicPayloadServiceBuilder, - EthereumNetworkBuilder, + RollkitNetworkBuilder, EthereumExecutorBuilder, EthereumConsensusBuilder, >; @@ -404,7 +491,7 @@ where .payload(BasicPayloadServiceBuilder::new( RollkitPayloadBuilderBuilder::new(&self.args), )) - .network(EthereumNetworkBuilder::default()) + .network(RollkitNetworkBuilder::new(self.args.disable_tx_pool_gossip)) .consensus(EthereumConsensusBuilder::default()) } @@ -607,6 +694,9 @@ where } } +#[cfg(test)] +mod network_test; + fn main() { info!("=== ROLLKIT-RETH NODE STARTING ==="); @@ -627,7 +717,7 @@ fn main() { info!("=== ROLLKIT-RETH: Using custom payload builder with transaction support ==="); let handle = builder - .node(RollkitNode::new(rollkit_args)) + .node(RollkitNode::new(rollkit_args.clone())) .launch() .await?; diff --git a/bin/lumen/src/network_test.rs b/bin/lumen/src/network_test.rs new file mode 100644 index 0000000..0def1fe --- /dev/null +++ b/bin/lumen/src/network_test.rs @@ -0,0 +1,39 @@ +#[cfg(test)] +mod tests { + use crate::{RollkitArgs, RollkitNetworkBuilder}; + use clap::Parser; + + #[test] + fn test_network_builder_with_gossip_disabled() { + // Create a network builder with gossip disabled + let builder = RollkitNetworkBuilder::new(true); + assert!(builder.disable_tx_pool_gossip); + } + + #[test] + fn test_network_builder_with_gossip_enabled() { + // Create a network builder with gossip enabled (default) + let builder = RollkitNetworkBuilder::new(false); + assert!(!builder.disable_tx_pool_gossip); + } + + #[test] + fn test_rollkit_args_default() { + let args = RollkitArgs::default(); + assert!(!args.disable_tx_pool_gossip); + // Note: enable_rollkit defaults to false with #[derive(Default)] + // but is set to true when parsed from CLI + assert!(!args.enable_rollkit); + } + + #[test] + fn test_rollkit_args_parsing() { + // Test parsing with gossip disabled + let args = RollkitArgs::parse_from(["lumen", "--disable-tx-pool-gossip"]); + assert!(args.disable_tx_pool_gossip); + + // Test parsing without the flag (default) + let args = RollkitArgs::parse_from(["lumen"]); + assert!(!args.disable_tx_pool_gossip); + } +} \ No newline at end of file diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 5148de5..e9a84b3 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -1,77 +1,104 @@ [package] -name = "lumen-node" -version.workspace = true +description = "Lumen node implementation" edition.workspace = true -rust-version.workspace = true -license.workspace = true homepage.workspace = true +license.workspace = true +name = "lumen-node" repository.workspace = true -description = "Lumen node implementation" +rust-version.workspace = true +version.workspace = true [dependencies] # Lumen dependencies -lumen-common = { path = "../common" } -lumen-rollkit = { path = "../rollkit" } +lumen-common = {path = "../common"} +lumen-rollkit = {path = "../rollkit"} # Reth dependencies -reth-node-builder.workspace = true +reth-basic-payload-builder.workspace = true reth-chainspec.workspace = true +reth-engine-local.workspace = true reth-ethereum-payload-builder.workspace = true -reth-payload-primitives.workspace = true -reth-primitives.workspace = true -reth-primitives-traits.workspace = true +reth-ethereum.workspace = true reth-node-api.workspace = true -reth-provider = { workspace = true, features = ["test-utils"] } +reth-node-builder.workspace = true reth-payload-builder.workspace = true -reth-basic-payload-builder.workspace = true -reth-engine-local.workspace = true +reth-payload-primitives.workspace = true +reth-primitives-traits.workspace = true +reth-primitives.workspace = true +reth-provider = {workspace = true, features = ["test-utils"]} reth-revm.workspace = true reth-trie-db.workspace = true # Additional reth dependencies for payload builder -reth-node-types.workspace = true -reth-payload-builder-primitives.workspace = true -reth-execution-types.workspace = true +reth-engine-primitives.workspace = true +reth-errors.workspace = true +reth-ethereum-primitives.workspace = true reth-evm-ethereum.workspace = true reth-evm.workspace = true -reth-errors.workspace = true +reth-execution-types.workspace = true +reth-network.workspace = true reth-node-core.workspace = true -reth-rpc-builder.workspace = true +reth-node-types.workspace = true +reth-payload-builder-primitives.workspace = true reth-rpc-api.workspace = true +reth-rpc-builder.workspace = true reth-rpc-engine-api.workspace = true -reth-engine-primitives.workspace = true -reth-ethereum-primitives.workspace = true +reth-rpc-eth-api.workspace = true +reth-rpc-eth-types.workspace = true +reth-rpc.workspace = true +reth-tasks.workspace = true +reth-transaction-pool.workspace = true # Alloy dependencies -alloy-rpc-types.workspace = true -alloy-rpc-types-engine.workspace = true -alloy-primitives.workspace = true -alloy-eips.workspace = true alloy-consensus.workspace = true +alloy-dyn-abi.workspace = true +alloy-eips.workspace = true +alloy-json-rpc.workspace = true +alloy-primitives.workspace = true alloy-rlp.workspace = true +alloy-rpc-types-engine.workspace = true +alloy-rpc-types.workspace = true +alloy-serde.workspace = true # Core dependencies +async-trait.workspace = true +delegate = "0.13.3" eyre.workspace = true -tracing.workspace = true -tokio = { workspace = true, features = ["full"] } -serde = { workspace = true, features = ["derive"] } +futures.workspace = true +jsonrpsee = {version = "0.25", features = ["server", "macros", "http-client"]} +parking_lot = "0.12" +serde = {workspace = true, features = ["derive"]} serde_json.workspace = true thiserror.workspace = true -async-trait.workspace = true -futures.workspace = true +tokio = {workspace = true, features = ["full"]} +tower = {version = "0.4", features = ["util"]} +tracing.workspace = true + +# --- added for TxForwarder --- +bytes = "1.6" +governor = "0.6" +hex = "0.4" +metrics = "0.22" +once_cell = "1.19" +prometheus = "0.13" +reqwest = {version = "0.11", features = ["json"]} +reth-metrics = {workspace = true} +# -------------------------------- [dev-dependencies] # Test dependencies -reth-testing-utils.workspace = true +alloy-genesis.workspace = true +hex = "0.4" +reth-consensus.workspace = true reth-db.workspace = true reth-evm-ethereum.workspace = true -reth-transaction-pool.workspace = true -reth-consensus.workspace = true reth-tasks.workspace = true +reth-testing-utils.workspace = true reth-tracing.workspace = true -alloy-genesis.workspace = true +reth-transaction-pool.workspace = true tempfile.workspace = true -hex = "0.4" +tokio = {workspace = true, features = ["full"]} +wiremock = "0.5" [lints] workspace = true diff --git a/crates/node/src/forwarder.rs b/crates/node/src/forwarder.rs new file mode 100644 index 0000000..9ce6afa --- /dev/null +++ b/crates/node/src/forwarder.rs @@ -0,0 +1,234 @@ +use std::{sync::Arc, time::Instant}; + +use alloy_primitives::B256; +use bytes::Bytes; +use governor::{ + clock::DefaultClock, + state::{direct::NotKeyed, InMemoryState}, + Quota, RateLimiter, +}; +use reqwest::StatusCode; +use serde_json::json; +use thiserror::Error; +use tokio::sync::Semaphore; + +use tracing::debug; + +/// Initialize metrics +fn init_metrics() { + metrics::describe_histogram!( + "tx_forwarder_latency_ms", + "End-to-end latency to the sequencer (ms)" + ); + metrics::describe_counter!( + "tx_forwarder_errors_total", + "Total errors encountered while forwarding" + ); +} + +/// Transaction forwarder for submitting transactions to the sequencer +#[derive(Clone, Debug)] +pub struct TxForwarder { + client: reqwest::Client, + endpoint: reqwest::Url, + limiter: Arc>, + queue: Arc, + /// Optional HTTP Basic-Auth header value (`"Basic base64(username:password)"`). + auth_header: Option, +} + +impl TxForwarder { + /// Construct a new forwarder. + /// + /// * `endpoint` – The sequencer endpoint (e.g. ). + /// * `queue_size` – Maximum number of in-flight requests (mapped onto a semaphore). + /// * `rate_limit_per_sec` – Maximum POST requests per second sent to the sequencer. + pub fn new( + endpoint: reqwest::Url, + queue_size: usize, + rate_limit_per_sec: u32, + auth_header: Option, + client: Option, + ) -> Self { + // Initialize metrics on first creation + static INIT: std::sync::Once = std::sync::Once::new(); + INIT.call_once(|| { + init_metrics(); + }); + + let quota = Quota::per_second( + core::num::NonZeroU32::new(rate_limit_per_sec) + .expect("rate_limit_per_sec must be non-zero"), + ); + Self { + client: client.unwrap_or_default(), + endpoint, + limiter: Arc::new(RateLimiter::direct(quota)), + queue: Arc::new(Semaphore::new(queue_size)), + auth_header, + } + } + + /// Forward a raw RLP-encoded transaction to the sequencer and return the hash it reports. + /// + /// The function: + /// 1. Waits for a queue permit (bounded concurrency). + /// 2. Observes rate-limit\n + /// 3. POSTs `raw_tx` bytes as-is (JSON RPC 2.0 `eth_sendRawTransaction`).\n + /// 4. Records latency & error metrics.\n + /// 5. Maps failures into [`ForwardError`]. + pub async fn forward_raw(&self, raw_tx: Bytes) -> Result { + // Step 1 – queue bound + let _permit = self + .queue + .clone() + .acquire_owned() + .await + .map_err(|_| ForwardError::Shutdown)?; + + // Step 2 – rate-limit (this is a lightweight async wait) + self.limiter.until_ready().await; + + // Step 3 – POST + let start = Instant::now(); + let payload = json!({ + "jsonrpc": "2.0", + "method": "eth_sendRawTransaction", + "params": [format!("0x{}", hex::encode(&raw_tx))], + "id": 1u64, + }); + + debug!(endpoint=%self.endpoint, "Forwarding tx to sequencer"); + let mut req = self.client.post(self.endpoint.clone()).json(&payload); + // + if let Some(ref hdr) = self.auth_header { + req = req.header(reqwest::header::AUTHORIZATION, hdr.clone()); + } + // + let resp = req.send().await.map_err(ForwardError::Network)?; + + let latency_ms = start.elapsed().as_millis() as f64; + metrics::histogram!("tx_forwarder_latency_ms").record(latency_ms); + + // Step 4 – map HTTP status + if !resp.status().is_success() { + let class = resp.status().as_u16().to_string(); + metrics::counter!("tx_forwarder_errors_total", "class" => class); + return Err(ForwardError::HttpStatus(resp.status())); + } + + // Step 5 – parse JSON-RPC result { "result": "0x…" } + let json: serde_json::Value = resp.json().await.map_err(ForwardError::InvalidJson)?; + if let Some(result) = json.get("result").and_then(|v| v.as_str()) { + let hash = result.trim_start_matches("0x"); + let mut b256_bytes = [0u8; 32]; + hex::decode_to_slice(hash, &mut b256_bytes).map_err(|_| ForwardError::InvalidHash)?; + return Ok(B256::from(b256_bytes)); + } + + if json.get("error").is_some() { + metrics::counter!("tx_forwarder_errors_total", "class" => "upstream"); + return Err(ForwardError::UpstreamError(json)); + } + + metrics::counter!("tx_forwarder_errors_total", "class" => "invalid_body"); + Err(ForwardError::UnexpectedBody(json)) + } +} + +/* -------------------------------------------------------------------------- */ +/* Error */ +/* -------------------------------------------------------------------------- */ + +/// Errors that can occur during transaction forwarding +#[derive(Debug, Error)] +pub enum ForwardError { + /// Service is shutting down + #[error("Service shutting down")] + Shutdown, + /// Request was rate-limited + #[error("Rate-limited")] + RateLimited, + /// Network error occurred + #[error("Network error: {0}")] + Network(reqwest::Error), + /// Sequencer returned non-success HTTP status + #[error("Sequencer returned HTTP status {0}")] + HttpStatus(StatusCode), + /// Failed to parse JSON response + #[error("Invalid JSON body")] + InvalidJson(reqwest::Error), + /// Response body was unexpected + #[error("Unexpected body: {0:?}")] + UnexpectedBody(serde_json::Value), + /// Transaction hash in response was invalid + #[error("Invalid transaction hash")] + InvalidHash, + /// Sequencer returned a JSON-RPC error object + #[error("Upstream JSON-RPC error: {0}")] + UpstreamError(serde_json::Value), +} + +#[cfg(test)] +mod tests { + use super::*; + use tokio; + use wiremock::{ + matchers::{method, path}, + Mock, MockServer, ResponseTemplate, + }; + + #[tokio::test] + async fn happy_path() { + // Spin up a mock sequencer. + let server = MockServer::start().await; + + // Mock 200 OK with a zero tx-hash. + let body = serde_json::json!({ + "jsonrpc": "2.0", + "id": 1, + "result": format!("0x{}", "00".repeat(32)) + }); + Mock::given(method("POST")) + .and(path("/")) + .respond_with(ResponseTemplate::new(200).set_body_json(body)) + .mount(&server) + .await; + + let forwarder = TxForwarder::new( + server.uri().parse().unwrap(), + /*queue_size*/ 10, + /*rate_limit*/ 1_000, + /*auth*/ None, + None, + ); + + let hash = forwarder + .forward_raw(Bytes::from_static(b"\x01\x02")) + .await + .expect("forwarding should succeed"); + + assert_eq!(hash, B256::ZERO); + } + + #[tokio::test] + async fn http_error() { + let server = MockServer::start().await; + + // Mock 500 Internal Server Error. + Mock::given(method("POST")) + .and(path("/")) + .respond_with(ResponseTemplate::new(500)) + .mount(&server) + .await; + + let forwarder = TxForwarder::new(server.uri().parse().unwrap(), 10, 1_000, None, None); + + let err = forwarder + .forward_raw(Bytes::from_static(b"\x03\x04")) + .await + .expect_err("should return error"); + + matches!(err, ForwardError::HttpStatus(status) if status == reqwest::StatusCode::INTERNAL_SERVER_ERROR); + } +} diff --git a/crates/node/src/lib.rs b/crates/node/src/lib.rs index 088eb87..15f7426 100644 --- a/crates/node/src/lib.rs +++ b/crates/node/src/lib.rs @@ -13,3 +13,30 @@ pub mod config; // Re-export public types pub use builder::{create_payload_builder_service, RollkitPayloadBuilder}; pub use config::{ConfigError, RollkitPayloadBuilderConfig}; + +/// Configuration for transaction forwarding to sequencer +#[derive(Debug, Clone)] +pub struct ForwardingConfig { + /// Optional sequencer HTTP endpoint + pub sequencer_http: Option, + /// Optional Basic-Auth header + pub sequencer_auth: Option, + /// Disable transaction pool gossip + pub disable_tx_pool_gossip: bool, + /// Maximum number of in-flight requests + pub queue_size: usize, + /// Maximum requests per second to sequencer + pub rate_limit_per_sec: u32, +} + +impl Default for ForwardingConfig { + fn default() -> Self { + Self { + sequencer_http: None, + sequencer_auth: None, + disable_tx_pool_gossip: false, + queue_size: 64, + rate_limit_per_sec: 1_000, + } + } +}