From 0a658e6fe038a1869c1c4248a6f7b17c673419e4 Mon Sep 17 00:00:00 2001 From: Karen Sarkisyan Date: Mon, 16 Feb 2026 17:06:37 +0100 Subject: [PATCH 1/2] feat: add customizable CORS headers to JSON-RPC server - update jsonrpsee version, add http and tower deps - add allow_origins argument, use it to instantiate CORS middleware --- Cargo.lock | 563 +++++++++++++++++++++---------------- README.md | 7 + cli/src/main.rs | 27 ++ core/Cargo.toml | 5 +- core/src/client/mod.rs | 20 +- core/src/errors.rs | 7 - core/src/jsonrpc/mod.rs | 74 ++++- ethereum/src/builder.rs | 35 ++- ethereum/src/config/cli.rs | 5 + ethereum/src/config/mod.rs | 3 + helios-ts/src/linea.rs | 1 + helios-ts/src/opstack.rs | 1 + linea/src/builder.rs | 31 +- linea/src/config.rs | 8 + opstack/src/builder.rs | 19 +- opstack/src/config.rs | 1 + 16 files changed, 538 insertions(+), 269 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29d83df75..d133ec322 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -319,7 +319,7 @@ checksum = "65f763621707fa09cece30b73ecc607eb43fd7a72451fe3b46f645b905086926" dependencies = [ "alloy-primitives", "alloy-sol-types", - "http 1.3.1", + "http 1.4.0", "serde", "serde_json", "thiserror 2.0.12", @@ -456,7 +456,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "wasmtimer 0.4.2", ] @@ -503,7 +503,7 @@ dependencies = [ "serde_json", "tokio", "tokio-stream", - "tower 0.5.2", + "tower", "tracing", "url", "wasmtimer 0.4.2", @@ -780,7 +780,7 @@ dependencies = [ "serde_json", "thiserror 2.0.12", "tokio", - "tower 0.5.2", + "tower", "tracing", "url", "wasmtimer 0.4.2", @@ -796,7 +796,7 @@ dependencies = [ "alloy-transport", "reqwest 0.12.22", "serde_json", - "tower 0.5.2", + "tower", "tracing", "url", ] @@ -830,7 +830,7 @@ dependencies = [ "alloy-pubsub", "alloy-transport", "futures", - "http 1.3.1", + "http 1.4.0", "rustls 0.23.29", "serde_json", "tokio", @@ -1317,7 +1317,7 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ - "async-lock 3.4.0", + "async-lock", "cfg-if", "concurrent-queue", "futures-io", @@ -1329,22 +1329,13 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "async-lock" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" -dependencies = [ - "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.4.0", + "event-listener", "event-listener-strategy", "pin-project-lite", ] @@ -1458,7 +1449,7 @@ dependencies = [ "axum-core 0.4.5", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", @@ -1476,7 +1467,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -1492,7 +1483,7 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", @@ -1510,7 +1501,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.2", "tokio", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -1525,7 +1516,7 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "mime", @@ -1545,7 +1536,7 @@ checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "mime", @@ -1568,7 +1559,7 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "mime", @@ -1577,7 +1568,7 @@ dependencies = [ "serde", "serde_html_form", "serde_path_to_error", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", ] @@ -1639,15 +1630,6 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -dependencies = [ - "serde", -] - [[package]] name = "bimap" version = "0.6.3" @@ -1793,16 +1775,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" -[[package]] -name = "bstr" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "bumpalo" version = "3.19.0" @@ -1883,6 +1855,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.1" @@ -2024,6 +2002,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "compact_str" version = "0.7.1" @@ -2105,6 +2093,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -2118,7 +2116,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "core-graphics-types", "foreign-types 0.5.0", "libc", @@ -2131,7 +2129,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "libc", ] @@ -2141,7 +2139,7 @@ version = "20.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" dependencies = [ - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "foreign-types 0.5.0", "libc", @@ -2988,12 +2986,6 @@ 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 = "event-listener" version = "5.4.0" @@ -3011,7 +3003,7 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 5.4.0", + "event-listener", "pin-project-lite", ] @@ -3141,7 +3133,7 @@ checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3" dependencies = [ "bitflags 2.9.1", "byteorder", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "core-text", "dirs 6.0.0", @@ -3434,30 +3426,17 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" -[[package]] -name = "globset" -version = "0.4.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" -dependencies = [ - "aho-corasick", - "bstr", - "log", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - [[package]] name = "gloo-net" -version = "0.3.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66b4e3c7d9ed8d315fd6b97c8b1f74a7c6ecbbc2320e65ae7ed38b7068cc620" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" dependencies = [ "futures-channel", "futures-core", "futures-sink", "gloo-utils", - "http 0.2.12", + "http 1.4.0", "js-sys", "pin-project", "serde", @@ -3492,9 +3471,9 @@ dependencies = [ [[package]] name = "gloo-utils" -version = "0.1.7" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037fcb07216cb3a30f7292bd0176b050b7b9a052ba830ef7d5d65f6dc64ba58e" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" dependencies = [ "js-sys", "serde", @@ -3554,7 +3533,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.3.1", + "http 1.4.0", "indexmap 2.10.0", "slab", "tokio", @@ -3736,6 +3715,7 @@ dependencies = [ "helios-test-utils", "helios-verifiable-api-client", "hex", + "http 1.4.0", "jsonrpsee", "openssl", "parking_lot 0.12.4", @@ -3745,6 +3725,8 @@ dependencies = [ "serde_json", "thiserror 1.0.69", "tokio", + "tower", + "tower-http", "tracing", "url", "wasm-bindgen-futures", @@ -4087,12 +4069,11 @@ dependencies = [ [[package]] name = "http" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ "bytes", - "fnv", "itoa", ] @@ -4114,7 +4095,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http 1.4.0", ] [[package]] @@ -4125,7 +4106,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "pin-project-lite", ] @@ -4176,7 +4157,7 @@ dependencies = [ "futures-channel", "futures-util", "h2 0.4.11", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "httparse", "httpdate", @@ -4187,35 +4168,20 @@ dependencies = [ "want", ] -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.32", - "log", - "rustls 0.21.12", - "rustls-native-certs", - "tokio", - "tokio-rustls 0.24.1", -] - [[package]] name = "hyper-rustls" version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ - "http 1.3.1", + "http 1.4.0", "hyper 1.6.0", "hyper-util", + "log", "rustls 0.23.29", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls", "tower-service", "webpki-roots 1.0.2", ] @@ -4260,7 +4226,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "hyper 1.6.0", "ipnet", @@ -4440,7 +4406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdf9d64cfcf380606e64f9a0bcf493616b65331199f984151a6fa11a7b3cde38" dependencies = [ "async-io", - "core-foundation", + "core-foundation 0.9.4", "fnv", "futures", "if-addrs", @@ -4658,6 +4624,28 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "jobserver" version = "0.1.33" @@ -4686,9 +4674,9 @@ dependencies = [ [[package]] name = "jsonrpsee" -version = "0.19.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5f3783308bddc49d0218307f66a09330c106fbd792c58bac5c8dc294fdd0f98" +checksum = "3f3f48dc3e6b8bd21e15436c1ddd0bc22a6a54e8ec46fedd6adf3425f396ec6a" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", @@ -4698,146 +4686,162 @@ dependencies = [ "jsonrpsee-types", "jsonrpsee-wasm-client", "jsonrpsee-ws-client", + "tokio", "tracing", ] [[package]] name = "jsonrpsee-client-transport" -version = "0.19.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc5630e4fa0096f00ec7b44d520701fda4504170cb85e22dca603ae5d7ad0d7" +checksum = "cf36eb27f8e13fa93dcb50ccb44c417e25b818cfa1a481b5470cd07b19c60b98" dependencies = [ + "base64 0.22.1", "futures-channel", "futures-util", "gloo-net", - "http 0.2.12", + "http 1.4.0", "jsonrpsee-core", "pin-project", - "rustls-native-certs", + "rustls 0.23.29", + "rustls-pki-types", + "rustls-platform-verifier", "soketto", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", - "tokio-rustls 0.24.1", + "tokio-rustls", "tokio-util", "tracing", - "webpki-roots 0.24.0", + "url", ] [[package]] name = "jsonrpsee-core" -version = "0.19.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaa4c4d5fb801dcc316d81f76422db259809037a86b3194ae538dd026b05ed7" +checksum = "316c96719901f05d1137f19ba598b5fe9c9bc39f4335f67f6be8613921946480" dependencies = [ - "anyhow", - "async-lock 2.8.0", "async-trait", - "beef", + "bytes", "futures-timer", "futures-util", - "globset", - "hyper 0.14.32", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", "jsonrpsee-types", "parking_lot 0.12.4", - "rand 0.8.5", - "rustc-hash 1.1.0", + "pin-project", + "rand 0.9.2", + "rustc-hash 2.1.1", "serde", "serde_json", - "soketto", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", "tokio-stream", + "tower", "tracing", "wasm-bindgen-futures", ] [[package]] name = "jsonrpsee-http-client" -version = "0.19.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa7165efcbfbc951d180162ff28fe91b657ed81925e37a35e4a396ce12109f96" +checksum = "790bedefcec85321e007ff3af84b4e417540d5c87b3c9779b9e247d1bcc3dab8" dependencies = [ - "async-trait", - "hyper 0.14.32", - "hyper-rustls 0.24.2", + "base64 0.22.1", + "http-body 1.0.1", + "hyper 1.6.0", + "hyper-rustls", + "hyper-util", "jsonrpsee-core", "jsonrpsee-types", + "rustls 0.23.29", + "rustls-platform-verifier", "serde", "serde_json", - "thiserror 1.0.69", + "thiserror 2.0.12", "tokio", - "tower 0.4.13", - "tracing", + "tower", + "url", ] [[package]] name = "jsonrpsee-proc-macros" -version = "0.19.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21dc12b1d4f16a86e8c522823c4fab219c88c03eb7c924ec0501a64bf12e058b" +checksum = "2da3f8ab5ce1bb124b6d082e62dffe997578ceaf0aeb9f3174a214589dc00f07" dependencies = [ - "heck 0.4.1", - "proc-macro-crate 1.1.3", + "heck 0.5.0", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.104", ] [[package]] name = "jsonrpsee-server" -version = "0.19.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e79d78cfd5abd8394da10753723093c3ff64391602941c9c4b1d80a3414fd53" +checksum = "4c51b7c290bb68ce3af2d029648148403863b982f138484a73f02a9dd52dbd7f" dependencies = [ "futures-util", - "hyper 0.14.32", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.6.0", + "hyper-util", "jsonrpsee-core", "jsonrpsee-types", + "pin-project", + "route-recognizer", "serde", "serde_json", "soketto", + "thiserror 2.0.12", "tokio", "tokio-stream", "tokio-util", - "tower 0.4.13", + "tower", "tracing", ] [[package]] name = "jsonrpsee-types" -version = "0.19.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00aa7cc87bc42e04e26c8ac3e7186142f7fd2949c763d9b6a7e64a69672d8fb2" +checksum = "bc88ff4688e43cc3fa9883a8a95c6fa27aa2e76c96e610b737b6554d650d7fd5" dependencies = [ - "anyhow", - "beef", + "http 1.4.0", "serde", "serde_json", - "thiserror 1.0.69", - "tracing", + "thiserror 2.0.12", ] [[package]] name = "jsonrpsee-wasm-client" -version = "0.19.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe953c2801356f214d3f4051f786b3d11134512a46763ee8c39a9e3fa2cc1c0" +checksum = "7902885de4779f711a95d82c8da2d7e5f9f3a7c7cfa44d51c067fd1c29d72a3c" dependencies = [ "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", + "tower", ] [[package]] name = "jsonrpsee-ws-client" -version = "0.19.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71b2597ec1c958c6d5bc94bb61b44d74eb28e69dc421731ab0035706f13882" +checksum = "9b6fceceeb05301cc4c065ab3bd2fa990d41ff4eb44e4ca1b30fa99c057c3e79" dependencies = [ - "http 0.2.12", + "http 1.4.0", "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-types", + "tower", + "url", ] [[package]] @@ -5538,10 +5542,10 @@ dependencies = [ "libc", "log", "openssl", - "openssl-probe", + "openssl-probe 0.1.6", "openssl-sys", "schannel", - "security-framework", + "security-framework 2.11.1", "security-framework-sys", "tempfile", ] @@ -5923,6 +5927,12 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "openssl-src" version = "300.5.1+3.5.1" @@ -6933,11 +6943,11 @@ dependencies = [ "futures-util", "h2 0.4.11", "hickory-resolver", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "http-body-util", "hyper 1.6.0", - "hyper-rustls 0.27.7", + "hyper-rustls", "hyper-tls 0.6.0", "hyper-util", "js-sys", @@ -6956,9 +6966,9 @@ dependencies = [ "sync_wrapper 1.0.2", "tokio", "tokio-native-tls", - "tokio-rustls 0.26.2", + "tokio-rustls", "tokio-util", - "tower 0.5.2", + "tower", "tower-http", "tower-service", "url", @@ -6976,7 +6986,7 @@ checksum = "57f17d28a6e6acfe1733fe24bcd30774d13bffa4b8a22535b4c8c98423088d4e" dependencies = [ "anyhow", "async-trait", - "http 1.3.1", + "http 1.4.0", "reqwest 0.12.22", "serde", "thiserror 1.0.69", @@ -6993,7 +7003,7 @@ dependencies = [ "async-trait", "futures", "getrandom 0.2.16", - "http 1.3.1", + "http 1.4.0", "hyper 1.6.0", "parking_lot 0.11.2", "reqwest 0.12.22", @@ -7277,6 +7287,12 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "rtnetlink" version = "0.13.1" @@ -7419,42 +7435,31 @@ dependencies = [ "webpki", ] -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring 0.17.14", - "rustls-webpki 0.101.7", - "sct", -] - [[package]] name = "rustls" version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" dependencies = [ + "log", "once_cell", "ring 0.17.14", "rustls-pki-types", - "rustls-webpki 0.103.4", + "rustls-webpki", "subtle", "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.6.3" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "openssl-probe", - "rustls-pemfile", + "openssl-probe 0.2.1", + "rustls-pki-types", "schannel", - "security-framework", + "security-framework 3.6.0", ] [[package]] @@ -7477,15 +7482,32 @@ dependencies = [ ] [[package]] -name = "rustls-webpki" -version = "0.101.7" +name = "rustls-platform-verifier" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "19787cda76408ec5404443dc8b31795c87cd8fec49762dc75fa727740d34acc1" dependencies = [ - "ring 0.17.14", - "untrusted 0.9.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls 0.23.29", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework 3.6.0", + "security-framework-sys", + "webpki-root-certs 0.26.11", + "windows-sys 0.59.0", ] +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.103.4" @@ -7668,7 +7690,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags 2.9.1", - "core-foundation", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d17b898a6d6948c3a8ee4372c17cb384f90d2e6e912ef00895b14fd7ab54ec38" +dependencies = [ + "bitflags 2.9.1", + "core-foundation 0.10.1", "core-foundation-sys", "libc", "security-framework-sys", @@ -7676,9 +7711,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "321c8673b092a9a42605034a9879d73cb79101ed5fd117bc9a597b89b4e9e61a" dependencies = [ "core-foundation-sys", "libc", @@ -7776,15 +7811,16 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.141" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "indexmap 2.10.0", "itoa", "memchr", - "ryu", "serde", + "serde_core", + "zmij", ] [[package]] @@ -7898,19 +7934,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "sha-1" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha1" version = "0.10.6" @@ -8103,18 +8126,18 @@ dependencies = [ [[package]] name = "soketto" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d1c5305e39e09653383c2c7244f2f78b3bcae37cf50c64cb4789c9f5096ec2" +checksum = "2e859df029d160cb88608f5d7df7fb4753fd20fdfb4de5644f3d8b8440841721" dependencies = [ - "base64 0.13.1", + "base64 0.22.1", "bytes", "futures", - "http 0.2.12", + "http 1.4.0", "httparse", "log", "rand 0.8.5", - "sha-1", + "sha1", ] [[package]] @@ -8325,7 +8348,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys 0.5.0", ] @@ -8336,7 +8359,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ "bitflags 2.9.1", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys 0.6.0", ] @@ -8553,16 +8576,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.2" @@ -8596,7 +8609,7 @@ dependencies = [ "rustls 0.23.29", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.2", + "tokio-rustls", "tungstenite", "webpki-roots 0.26.11", ] @@ -8668,24 +8681,9 @@ 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" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", @@ -8699,22 +8697,22 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", "bitflags 2.9.1", "bytes", "futures-core", "futures-util", - "http 1.3.1", + "http 1.4.0", "http-body 1.0.1", "iri-string", "pin-project-lite", "tokio", "tokio-util", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", @@ -8915,7 +8913,7 @@ checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ "bytes", "data-encoding", - "http 1.3.1", + "http 1.4.0", "httparse", "log", "rand 0.9.2", @@ -9325,12 +9323,21 @@ dependencies = [ ] [[package]] -name = "webpki-roots" -version = "0.24.0" +name = "webpki-root-certs" +version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" +checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "rustls-webpki 0.101.7", + "webpki-root-certs 1.0.6", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +dependencies = [ + "rustls-pki-types", ] [[package]] @@ -9493,6 +9500,15 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -9529,6 +9545,21 @@ dependencies = [ "windows-targets 0.53.3", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" @@ -9577,6 +9608,12 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -9595,6 +9632,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -9613,6 +9656,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -9643,6 +9692,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -9661,6 +9716,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -9679,6 +9740,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -9697,6 +9764,12 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -9974,6 +10047,12 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + [[package]] name = "zstd" version = "0.13.3" diff --git a/README.md b/README.md index 54abc1ec2..512ade146 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ Helios will now run a local RPC server at `http://127.0.0.1:8545`. `--rpc-bind-ip` or `-b` sets the ip that binds to the JSON-RPC server. By default, Helios will use `127.0.0.1`. Use `0.0.0.0` to allow remote access. +`--allowed-origins` specifies a comma-separated list of origins allowed to make CORS requests to the RPC server. Useful when exposing the RPC to web dApps, e.g. `--allowed-origins https://my-dapp.com,https://localhost:3000`. + `--data-dir` or `-d` sets the directory that Helios should use to store cached weak subjectivity checkpoints in. Each network only stores the latest checkpoint, which is just 32 bytes. `--fallback` or `-f` sets the checkpoint fallback url (a string). This is only used if the checkpoint provided by the `--checkpoint` flag is too outdated for Helios to use to sync. @@ -144,6 +146,11 @@ helios ethereum \ --checkpoint 0xe1912ca8ca3b45dac497cae7825bab055b0f60285533721b046e8fefb5b076f2 ``` +To expose the RPC to a web dApp, add `--rpc-bind-ip 0.0.0.0` and specify allowed CORS origins: +```bash +helios ethereum --execution-rpc $ETH_RPC_URL --rpc-bind-ip 0.0.0.0 --allowed-origins https://my-dapp.com,https://localhost:3000 +``` + If you wish to use a [Configuration File](#configuration-files) instead of CLI arguments then you should replace the example checkpoints in the configuration file with the latest checkpoints obtained above. ## Testing diff --git a/cli/src/main.rs b/cli/src/main.rs index 32ab326fc..f21d93809 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -155,6 +155,13 @@ struct EthereumArgs { rpc_bind_ip: Option, #[arg(short = 'p', long, env)] rpc_port: Option, + #[arg( + long, + env, + value_delimiter = ',', + help = "Comma-separated list of allowed CORS origins" + )] + allowed_origins: Option>, #[arg(short = 'w', long, env)] checkpoint: Option, #[arg(short, long, env, value_parser = parse_url)] @@ -204,6 +211,7 @@ impl EthereumArgs { .map(|s| PathBuf::from_str(s).expect("cannot find data dir")), rpc_bind_ip: self.rpc_bind_ip, rpc_port: self.rpc_port, + allowed_origins: self.allowed_origins.clone(), fallback: self.fallback.clone(), load_external_fallback: true_or_none(self.load_external_fallback), strict_checkpoint_age: true_or_none(self.strict_checkpoint_age), @@ -219,6 +227,13 @@ struct OpStackArgs { rpc_bind_ip: Option, #[arg(short = 'p', long, env, default_value = "8545")] rpc_port: Option, + #[arg( + long, + env, + value_delimiter = ',', + help = "Comma-separated list of allowed CORS origins" + )] + allowed_origins: Option>, #[arg(short, long, env, value_parser = parse_url)] execution_rpc: Option, #[arg(long, env, value_parser = parse_url)] @@ -276,6 +291,10 @@ impl OpStackArgs { user_dict.insert("rpc_socket", Value::from(rpc_socket.to_string())); } + if let Some(allowed_origins) = &self.allowed_origins { + user_dict.insert("allowed_origins", Value::from(allowed_origins.clone())); + } + if let Some(ip) = self.rpc_bind_ip { user_dict.insert("rpc_bind_ip", Value::from(ip.to_string())); } @@ -304,6 +323,13 @@ struct LineaArgs { rpc_bind_ip: Option, #[arg(short = 'p', long, env)] rpc_port: Option, + #[arg( + long, + env, + value_delimiter = ',', + help = "Comma-separated list of allowed CORS origins" + )] + allowed_origins: Option>, #[arg(short, long, env, value_parser = parse_url)] execution_rpc: Option, } @@ -328,6 +354,7 @@ impl LineaArgs { execution_rpc: self.execution_rpc.clone(), rpc_bind_ip: self.rpc_bind_ip, rpc_port: self.rpc_port, + allowed_origins: self.allowed_origins.clone(), } } } diff --git a/core/Cargo.toml b/core/Cargo.toml index c2f0aa2f3..aff8cd432 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -31,8 +31,11 @@ thiserror.workspace = true url.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -jsonrpsee = { version = "0.19.0", features = ["full"] } +jsonrpsee = { version = "0.26.0", features = ["full"] } openssl.workspace = true +tower-http = { version = "0.6", features = ["compression-full", "trace", "cors"] } +tower = "0.5" +http = "1" [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen-futures = "0.4.33" diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 5f4152ba9..5f1bae549 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -1,5 +1,3 @@ -#[cfg(not(target_arch = "wasm32"))] -use std::net::SocketAddr; use std::{ops::Deref, sync::Arc}; #[cfg(not(target_arch = "wasm32"))] @@ -10,7 +8,7 @@ use helios_common::{ use crate::consensus::Consensus; #[cfg(not(target_arch = "wasm32"))] -use crate::jsonrpc; +use crate::jsonrpc::{self, RpcServerConfig}; use self::{api::HeliosApi, node::Node}; @@ -26,16 +24,24 @@ impl HeliosClient { consensus: C, execution: E, fork_schedule: ForkSchedule, - #[cfg(not(target_arch = "wasm32"))] rpc_address: Option, + #[cfg(not(target_arch = "wasm32"))] rpc_config: Option, ) -> Self { let inner = Arc::new(Node::new(consensus, execution, fork_schedule)); #[cfg(not(target_arch = "wasm32"))] - if let Some(rpc_address) = rpc_address { + if let Some(rpc_config) = rpc_config { let inner_ref = inner.clone(); tokio::spawn(async move { - let _handle = jsonrpc::start(inner_ref, rpc_address).await; - let () = pending().await; + let shutdown_ref = inner_ref.clone(); + match jsonrpc::start(inner_ref, rpc_config).await { + Ok(_handle) => { + let () = pending().await; + } + Err(err) => { + tracing::error!(?err, "failed to start JSON-RPC server"); + shutdown_ref.shutdown().await; + } + } }); } diff --git a/core/src/errors.rs b/core/src/errors.rs index 4124fdf39..0699dfbea 100644 --- a/core/src/errors.rs +++ b/core/src/errors.rs @@ -30,13 +30,6 @@ impl From for ClientError { } } -#[cfg(not(target_arch = "wasm32"))] -impl From for jsonrpsee::core::Error { - fn from(value: ClientError) -> Self { - jsonrpsee::core::Error::Custom(value.to_string()) - } -} - #[derive(Debug, Error)] #[error("rpc error on method: {method}, message: {error}")] pub struct RpcError { diff --git a/core/src/jsonrpc/mod.rs b/core/src/jsonrpc/mod.rs index be22e7833..645b0e545 100644 --- a/core/src/jsonrpc/mod.rs +++ b/core/src/jsonrpc/mod.rs @@ -11,12 +11,15 @@ use alloy::rpc::types::{ Log, SyncStatus, }; use eyre::{eyre, Result}; +use http::Method; use jsonrpsee::{ core::{async_trait, server::Methods, SubscriptionResult}, proc_macros::rpc, - server::{PendingSubscriptionSink, ServerBuilder, ServerHandle, SubscriptionMessage}, + server::{PendingSubscriptionSink, ServerBuilder, ServerHandle}, types::error::{ErrorObject, ErrorObjectOwned}, }; +use tower_http::cors::{AllowOrigin, CorsLayer}; +use url::Url; use helios_common::{ network_spec::NetworkSpec, @@ -27,11 +30,24 @@ use crate::client::api::HeliosApi; pub type Handle = ServerHandle; +#[cfg(not(target_arch = "wasm32"))] +pub struct RpcServerConfig { + pub addr: SocketAddr, + pub allowed_origins: Option>, +} + pub async fn start( client: Arc>, - addr: SocketAddr, + config: RpcServerConfig, ) -> Result { - let server = ServerBuilder::default().build(addr).await?; + let cors = build_cors_layer(config.allowed_origins)?; + let middleware = tower::ServiceBuilder::new().option_layer(cors); + + let server = ServerBuilder::default() + .set_http_middleware(middleware) + .build(config.addr) + .await?; + let rpc = JsonRpc { client, phantom: PhantomData, @@ -51,6 +67,50 @@ pub async fn start( Ok(server.start(methods)) } +fn build_cors_layer(allowed_origins: Option>) -> Result> { + let origins = match allowed_origins { + Some(origins) if !origins.is_empty() => origins, + _ => return Ok(None), + }; + + let mut list = Vec::with_capacity(origins.len()); + let mut has_wildcard = false; + + for origin in origins { + if origin == "*" { + has_wildcard = true; + break; + } + list.push(parse_allowed_origin(&origin)?); + } + + let allow_origin = if has_wildcard { + AllowOrigin::any() + } else { + AllowOrigin::list(list) + }; + + Ok(Some( + CorsLayer::new() + .allow_methods([Method::POST, Method::OPTIONS]) + .allow_origin(allow_origin) + .allow_headers([http::header::CONTENT_TYPE]), + )) +} + +fn parse_allowed_origin(origin: &str) -> Result { + let url = Url::parse(origin).map_err(|e| eyre!("invalid allowed origin '{origin}': {e}"))?; + if !url.origin().is_tuple() { + return Err(eyre!( + "invalid allowed origin '{origin}': must be a tuple origin (scheme + host)" + )); + } + + origin + .parse() + .map_err(|e| eyre!("invalid allowed origin header value '{origin}': {e}")) +} + #[derive(Clone)] struct JsonRpc { client: Arc>, @@ -491,8 +551,8 @@ async fn handle_eth_subscription( tokio::spawn(async move { while let Ok(message) = stream.recv().await { - let msg = SubscriptionMessage::from_json(&message).unwrap(); - if sink.send(msg).await.is_err() { + let raw = serde_json::value::to_raw_value(&message).unwrap(); + if sink.send(raw).await.is_err() { break; } } @@ -523,8 +583,8 @@ async fn handle_checkpoint_subscription( tokio::spawn(async move { while rx.changed().await.is_ok() { let checkpoint = *rx.borrow_and_update(); - let msg = SubscriptionMessage::from_json(&checkpoint).unwrap(); - if sink.send(msg).await.is_err() { + let raw = serde_json::value::to_raw_value(&checkpoint).unwrap(); + if sink.send(raw).await.is_err() { break; } } diff --git a/ethereum/src/builder.rs b/ethereum/src/builder.rs index e27919a9e..c58e281c9 100644 --- a/ethereum/src/builder.rs +++ b/ethereum/src/builder.rs @@ -14,6 +14,8 @@ use helios_core::execution::providers::block::block_cache::BlockCache; use helios_core::execution::providers::historical::eip2935::Eip2935Provider; use helios_core::execution::providers::rpc::RpcExecutionProvider; use helios_core::execution::providers::verifiable_api::VerifiableApiExecutionProvider; +#[cfg(not(target_arch = "wasm32"))] +use helios_core::jsonrpc::RpcServerConfig; use crate::config::networks::Network; use crate::config::Config; @@ -34,6 +36,8 @@ pub struct EthereumClientBuilder { #[cfg(not(target_arch = "wasm32"))] rpc_address: Option, #[cfg(not(target_arch = "wasm32"))] + allowed_origins: Option>, + #[cfg(not(target_arch = "wasm32"))] data_dir: Option, config: Option, fallback: Option, @@ -53,6 +57,8 @@ impl Default for EthereumClientBuilder { #[cfg(not(target_arch = "wasm32"))] rpc_address: None, #[cfg(not(target_arch = "wasm32"))] + allowed_origins: None, + #[cfg(not(target_arch = "wasm32"))] data_dir: None, config: None, fallback: None, @@ -111,6 +117,12 @@ impl EthereumClientBuilder { self } + #[cfg(not(target_arch = "wasm32"))] + pub fn allowed_origins(mut self, allowed_origins: Vec) -> Self { + self.allowed_origins = Some(allowed_origins); + self + } + #[cfg(not(target_arch = "wasm32"))] pub fn data_dir(mut self, data_dir: PathBuf) -> Self { self.data_dir = Some(data_dir); @@ -203,6 +215,15 @@ impl EthereumClientBuilder { None }; + #[cfg(not(target_arch = "wasm32"))] + let allowed_origins = if self.allowed_origins.is_some() { + self.allowed_origins + } else if let Some(config) = &self.config { + config.allowed_origins.clone() + } else { + None + }; + let fallback = if self.fallback.is_some() { self.fallback } else if let Some(config) = &self.config { @@ -245,12 +266,22 @@ impl EthereumClientBuilder { forks: base_config.forks, execution_forks: base_config.execution_forks, max_checkpoint_age: base_config.max_checkpoint_age, + #[cfg(not(target_arch = "wasm32"))] + allowed_origins: allowed_origins.clone(), + #[cfg(target_arch = "wasm32")] + allowed_origins: None, fallback, load_external_fallback, strict_checkpoint_age, database_type, }; + #[cfg(not(target_arch = "wasm32"))] + let rpc_config = rpc_address.map(|addr| RpcServerConfig { + addr, + allowed_origins, + }); + let config = Arc::new(config); let consensus = ConsensusClient::::new( &config.consensus_rpc, @@ -272,7 +303,7 @@ impl EthereumClientBuilder { execution, config.execution_forks, #[cfg(not(target_arch = "wasm32"))] - rpc_address, + rpc_config, )) } else { let block_provider = BlockCache::::new(); @@ -290,7 +321,7 @@ impl EthereumClientBuilder { execution, config.execution_forks, #[cfg(not(target_arch = "wasm32"))] - rpc_address, + rpc_config, )) } } diff --git a/ethereum/src/config/cli.rs b/ethereum/src/config/cli.rs index 9a11b2ee5..fbdac3667 100644 --- a/ethereum/src/config/cli.rs +++ b/ethereum/src/config/cli.rs @@ -15,6 +15,7 @@ pub struct CliConfig { pub checkpoint: Option, pub rpc_bind_ip: Option, pub rpc_port: Option, + pub allowed_origins: Option>, pub data_dir: Option, pub fallback: Option, pub load_external_fallback: Option, @@ -49,6 +50,10 @@ impl CliConfig { user_dict.insert("rpc_port", Value::from(port)); } + if let Some(allowed_origins) = &self.allowed_origins { + user_dict.insert("allowed_origins", Value::from(allowed_origins.clone())); + } + if let Some(data_dir) = self.data_dir.as_ref() { user_dict.insert("data_dir", Value::from(data_dir.to_str().unwrap())); } diff --git a/ethereum/src/config/mod.rs b/ethereum/src/config/mod.rs index f51c34084..ad3851a69 100644 --- a/ethereum/src/config/mod.rs +++ b/ethereum/src/config/mod.rs @@ -32,6 +32,7 @@ pub struct Config { pub verifiable_api: Option, pub rpc_bind_ip: Option, pub rpc_port: Option, + pub allowed_origins: Option>, pub default_checkpoint: B256, pub checkpoint: Option, pub data_dir: Option, @@ -109,6 +110,7 @@ impl From for Config { verifiable_api: None, checkpoint: None, default_checkpoint: base.default_checkpoint, + allowed_origins: None, chain: base.chain, forks: base.forks, execution_forks: base.execution_forks, @@ -130,6 +132,7 @@ impl Default for Config { verifiable_api: None, rpc_bind_ip: None, rpc_port: None, + allowed_origins: None, default_checkpoint: B256::default(), checkpoint: None, data_dir: None, diff --git a/helios-ts/src/linea.rs b/helios-ts/src/linea.rs index c926a039b..3cd168118 100644 --- a/helios-ts/src/linea.rs +++ b/helios-ts/src/linea.rs @@ -55,6 +55,7 @@ impl LineaClient { chain: network_config.chain, rpc_bind_ip: None, rpc_port: None, + allowed_origins: None, }; let inner = map_err(LineaClientBuilder::new().config(config).build())?; diff --git a/helios-ts/src/opstack.rs b/helios-ts/src/opstack.rs index 99b2867f0..d39377b64 100644 --- a/helios-ts/src/opstack.rs +++ b/helios-ts/src/opstack.rs @@ -73,6 +73,7 @@ impl OpStackClient { load_external_fallback: None, checkpoint: None, verify_unsafe_signer: false, + allowed_origins: None, }; let inner = map_err(OpStackClientBuilder::new().config(config).build())?; diff --git a/linea/src/builder.rs b/linea/src/builder.rs index 491319b57..c84fd1045 100644 --- a/linea/src/builder.rs +++ b/linea/src/builder.rs @@ -1,6 +1,8 @@ use eyre::{eyre, Result}; use helios_core::execution::providers::block::block_cache::BlockCache; use helios_core::execution::providers::rpc::RpcExecutionProvider; +#[cfg(not(target_arch = "wasm32"))] +use helios_core::jsonrpc::RpcServerConfig; use reqwest::{IntoUrl, Url}; #[cfg(not(target_arch = "wasm32"))] use std::net::{IpAddr, SocketAddr}; @@ -22,6 +24,8 @@ pub struct LineaClientBuilder { rpc_bind_ip: Option, #[cfg(not(target_arch = "wasm32"))] rpc_port: Option, + #[cfg(not(target_arch = "wasm32"))] + allowed_origins: Option>, config: Option, } @@ -56,6 +60,12 @@ impl LineaClientBuilder { self } + #[cfg(not(target_arch = "wasm32"))] + pub fn allowed_origins(mut self, allowed_origins: Vec) -> Self { + self.allowed_origins = Some(allowed_origins); + self + } + pub fn config(mut self, config: Config) -> Self { self.config = Some(config); self @@ -98,6 +108,15 @@ impl LineaClientBuilder { None }; + #[cfg(not(target_arch = "wasm32"))] + let allowed_origins = if self.allowed_origins.is_some() { + self.allowed_origins + } else if let Some(config) = &self.config { + config.allowed_origins.clone() + } else { + None + }; + let config = Config { execution_rpc, #[cfg(not(target_arch = "wasm32"))] @@ -108,6 +127,10 @@ impl LineaClientBuilder { rpc_port, #[cfg(target_arch = "wasm32")] rpc_port: None, + #[cfg(not(target_arch = "wasm32"))] + allowed_origins: allowed_origins.clone(), + #[cfg(target_arch = "wasm32")] + allowed_origins: None, chain: base_config.chain, }; @@ -118,6 +141,12 @@ impl LineaClientBuilder { None }; + #[cfg(not(target_arch = "wasm32"))] + let rpc_config = socket.map(|addr| RpcServerConfig { + addr, + allowed_origins, + }); + let config = Arc::new(config); let consensus = ConsensusClient::new(&config); @@ -141,7 +170,7 @@ impl LineaClientBuilder { execution, fork_schedule, #[cfg(not(target_arch = "wasm32"))] - socket, + rpc_config, )) } } diff --git a/linea/src/config.rs b/linea/src/config.rs index 70f4f291c..ecd6adb4b 100644 --- a/linea/src/config.rs +++ b/linea/src/config.rs @@ -104,6 +104,7 @@ pub struct CliConfig { pub execution_rpc: Option, pub rpc_bind_ip: Option, pub rpc_port: Option, + pub allowed_origins: Option>, } impl CliConfig { @@ -122,6 +123,10 @@ impl CliConfig { user_dict.insert("rpc_port", Value::from(port)); } + if let Some(allowed_origins) = &self.allowed_origins { + user_dict.insert("allowed_origins", Value::from(allowed_origins.clone())); + } + Serialized::from(user_dict, network) } } @@ -149,6 +154,7 @@ pub struct Config { pub execution_rpc: Url, pub rpc_bind_ip: Option, pub rpc_port: Option, + pub allowed_origins: Option>, pub chain: ChainConfig, } @@ -202,6 +208,7 @@ impl Default for Config { execution_rpc: Url::parse("http://localhost:8545").unwrap(), rpc_bind_ip: None, rpc_port: None, + allowed_origins: None, chain: ChainConfig::default(), } } @@ -213,6 +220,7 @@ impl From for Config { rpc_bind_ip: Some(base.rpc_bind_ip), rpc_port: Some(base.rpc_port), execution_rpc: Url::parse("http://localhost:8545").unwrap(), + allowed_origins: None, chain: base.chain, } } diff --git a/opstack/src/builder.rs b/opstack/src/builder.rs index a12cebfa0..002c4fdc3 100644 --- a/opstack/src/builder.rs +++ b/opstack/src/builder.rs @@ -3,6 +3,8 @@ use helios_core::execution::providers::{ block::block_cache::BlockCache, historical::eip2935::Eip2935Provider, rpc::RpcExecutionProvider, verifiable_api::VerifiableApiExecutionProvider, }; +#[cfg(not(target_arch = "wasm32"))] +use helios_core::jsonrpc::RpcServerConfig; use reqwest::{IntoUrl, Url}; use std::net::SocketAddr; @@ -21,6 +23,7 @@ pub struct OpStackClientBuilder { execution_rpc: Option, verifiable_api: Option, rpc_socket: Option, + allowed_origins: Option>, verify_unsafe_signer: Option, } @@ -54,6 +57,11 @@ impl OpStackClientBuilder { self } + pub fn allowed_origins(mut self, allowed_origins: Vec) -> Self { + self.allowed_origins = Some(allowed_origins); + self + } + pub fn network(mut self, network: Network) -> Self { self.network = Some(network); self @@ -81,6 +89,7 @@ impl OpStackClientBuilder { execution_rpc: self.execution_rpc, verifiable_api: self.verifiable_api, rpc_socket: self.rpc_socket, + allowed_origins: self.allowed_origins, chain: NetworkConfig::from(network).chain, load_external_fallback: None, checkpoint: None, @@ -88,6 +97,12 @@ impl OpStackClientBuilder { } }; + #[cfg(not(target_arch = "wasm32"))] + let rpc_config = config.rpc_socket.map(|addr| RpcServerConfig { + addr, + allowed_origins: config.allowed_origins.clone(), + }); + let consensus = ConsensusClient::new(&config); if let Some(verifiable_api) = &config.verifiable_api { @@ -105,7 +120,7 @@ impl OpStackClientBuilder { execution, config.chain.forks, #[cfg(not(target_arch = "wasm32"))] - config.rpc_socket, + rpc_config, )) } else { let block_provider = BlockCache::::new(); @@ -123,7 +138,7 @@ impl OpStackClientBuilder { execution, config.chain.forks, #[cfg(not(target_arch = "wasm32"))] - config.rpc_socket, + rpc_config, )) } } diff --git a/opstack/src/config.rs b/opstack/src/config.rs index 74dad94dd..c8780579d 100644 --- a/opstack/src/config.rs +++ b/opstack/src/config.rs @@ -21,6 +21,7 @@ pub struct Config { pub execution_rpc: Option, pub verifiable_api: Option, pub rpc_socket: Option, + pub allowed_origins: Option>, pub chain: ChainConfig, pub load_external_fallback: Option, pub checkpoint: Option, From 9a38a7977066514f01e89714272151cdcd5a7b21 Mon Sep 17 00:00:00 2001 From: Karen Sarkisyan Date: Mon, 16 Feb 2026 17:22:35 +0100 Subject: [PATCH 2/2] add wildcard allowed origin to example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 512ade146..bba38ff6b 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ helios ethereum \ To expose the RPC to a web dApp, add `--rpc-bind-ip 0.0.0.0` and specify allowed CORS origins: ```bash -helios ethereum --execution-rpc $ETH_RPC_URL --rpc-bind-ip 0.0.0.0 --allowed-origins https://my-dapp.com,https://localhost:3000 +helios ethereum --execution-rpc $ETH_RPC_URL --rpc-bind-ip 0.0.0.0 --allowed-origins "*" ``` If you wish to use a [Configuration File](#configuration-files) instead of CLI arguments then you should replace the example checkpoints in the configuration file with the latest checkpoints obtained above.