From 9f6d7c147e39418827bc8d3eb07f6d62af5b7739 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 29 Nov 2024 09:07:41 +0500 Subject: [PATCH 1/7] Add RPC Type --- packages/ciphernode/Cargo.lock | 1 + .../ciphernode/enclave_node/src/aggregator.rs | 9 +- .../ciphernode/enclave_node/src/ciphernode.rs | 6 +- packages/ciphernode/evm/Cargo.toml | 1 + packages/ciphernode/evm/src/helpers.rs | 123 ++++++++++++++---- 5 files changed, 104 insertions(+), 36 deletions(-) diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index c8f87742..827303bc 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -2311,6 +2311,7 @@ dependencies = [ "alloy-primitives 0.6.4", "anyhow", "async-trait", + "base64", "cipher 0.1.0", "data", "enclave-core", diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index bdb4b8c5..5c17bb05 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -5,8 +5,7 @@ use config::AppConfig; use enclave_core::EventBus; use evm::{ helpers::{ - create_provider_with_signer, create_readonly_provider, ensure_http_rpc, ensure_ws_rpc, - get_signer_from_repository, + create_provider_with_signer, create_readonly_provider, get_signer_from_repository, RPC, }, CiphernodeRegistrySol, EnclaveSol, RegistryFilterSol, }; @@ -45,10 +44,10 @@ pub async fn setup_aggregator( .iter() .filter(|chain| chain.enabled.unwrap_or(true)) { - let rpc_url = &chain.rpc_url; - let read_provider = create_readonly_provider(&ensure_ws_rpc(rpc_url)).await?; + let rpc_url = RPC::from_url(&chain.rpc_url).unwrap(); + let read_provider = create_readonly_provider(&rpc_url.as_ws_url()).await?; let write_provider = - create_provider_with_signer(&ensure_http_rpc(rpc_url), &signer).await?; + create_provider_with_signer(&rpc_url.as_http_url(), &signer).await?; EnclaveSol::attach( &bus, diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index c8dedf14..58fc31c1 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -5,7 +5,7 @@ use cipher::Cipher; use config::AppConfig; use enclave_core::{get_tag, EventBus}; use evm::{ - helpers::{create_readonly_provider, ensure_ws_rpc}, + helpers::{create_readonly_provider, RPC}, CiphernodeRegistrySol, EnclaveSolReader, }; use logger::SimpleLogger; @@ -44,9 +44,9 @@ pub async fn setup_ciphernode( .iter() .filter(|chain| chain.enabled.unwrap_or(true)) { - let rpc_url = &chain.rpc_url; + let rpc_url = RPC::from_url(&chain.rpc_url).unwrap(); - let read_provider = create_readonly_provider(&ensure_ws_rpc(rpc_url)).await?; + let read_provider = create_readonly_provider(&rpc_url.as_ws_url()).await?; EnclaveSolReader::attach( &bus, &read_provider, diff --git a/packages/ciphernode/evm/Cargo.toml b/packages/ciphernode/evm/Cargo.toml index fba4aea4..6eb95378 100644 --- a/packages/ciphernode/evm/Cargo.toml +++ b/packages/ciphernode/evm/Cargo.toml @@ -9,6 +9,7 @@ alloy = { workspace = true } alloy-primitives = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } +base64 = { workspace = true } enclave-core = { path = "../core" } data = { path = "../data" } futures-util = { workspace = true } diff --git a/packages/ciphernode/evm/src/helpers.rs b/packages/ciphernode/evm/src/helpers.rs index 116f3c2c..256b0bc6 100644 --- a/packages/ciphernode/evm/src/helpers.rs +++ b/packages/ciphernode/evm/src/helpers.rs @@ -10,12 +10,61 @@ use alloy::{ signers::local::PrivateKeySigner, transports::{BoxTransport, Transport}, }; -use anyhow::{bail, Context, Result}; +use anyhow::{bail,Context, Result}; use cipher::Cipher; use data::Repository; use std::{env, marker::PhantomData, sync::Arc}; use zeroize::Zeroizing; + +#[derive(Clone)] +pub enum RPC { + Http(String), + Https(String), + Ws(String), + Wss(String), +} + +impl RPC { + pub fn from_url(url: &str) -> Result { + if url.starts_with("http://") { + Ok(RPC::Http(url.to_string())) + } else if url.starts_with("https://") { + Ok(RPC::Https(url.to_string())) + } else if url.starts_with("ws://") { + Ok(RPC::Ws(url.to_string())) + } else if url.starts_with("wss://") { + Ok(RPC::Wss(url.to_string())) + } else { + bail!("Invalid RPC URL, Please specify a protocol. (http://, https://, ws://, wss://)"); + } + } + + pub fn as_http_url(&self) -> String { + match self { + RPC::Http(url) | RPC::Https(url) => url.clone(), + RPC::Ws(url) => url.replacen("ws://", "http://", 1), + RPC::Wss(url) => url.replacen("wss://", "https://", 1), + } + } + + pub fn as_ws_url(&self) -> String { + match self { + RPC::Http(url) => url.replacen("http://", "ws://", 1), + RPC::Https(url) => url.replacen("https://", "wss://", 1), + RPC::Ws(url) | RPC::Wss(url) => url.clone(), + } + } + + pub fn is_websocket(&self) -> bool { + matches!(self, RPC::Ws(_) | RPC::Wss(_)) + } + + pub fn is_secure(&self) -> bool { + matches!(self, RPC::Https(_) | RPC::Wss(_)) + } +} + /// We need to cache the chainId so we can easily use it in a non-async situation /// This wrapper just stores the chain_id with the Provider #[derive(Clone)] @@ -117,40 +166,58 @@ pub async fn get_signer_from_repository( Ok(Arc::new(signer)) } -pub fn ensure_http_rpc(rpc_url: &str) -> String { - if rpc_url.starts_with("ws://") { - return rpc_url.replacen("ws://", "http://", 1); - } else if rpc_url.starts_with("wss://") { - return rpc_url.replacen("wss://", "https://", 1); - } - rpc_url.to_string() -} - -pub fn ensure_ws_rpc(rpc_url: &str) -> String { - if rpc_url.starts_with("http://") { - return rpc_url.replacen("http://", "ws://", 1); - } else if rpc_url.starts_with("https://") { - return rpc_url.replacen("https://", "wss://", 1); - } - rpc_url.to_string() -} #[cfg(test)] mod test { use super::*; #[test] - fn test_ensure_http_rpc() { - assert_eq!(ensure_http_rpc("http://foo.com"), "http://foo.com"); - assert_eq!(ensure_http_rpc("https://foo.com"), "https://foo.com"); - assert_eq!(ensure_http_rpc("ws://foo.com"), "http://foo.com"); - assert_eq!(ensure_http_rpc("wss://foo.com"), "https://foo.com"); + fn test_rpc_type_conversion() { + // Test HTTP URLs + let http = RPC::from_url("http://localhost:8545").unwrap(); + assert!(matches!(http, RPC::Http(_))); + assert_eq!(http.as_http_url(), "http://localhost:8545"); + assert_eq!(http.as_ws_url(), "ws://localhost:8545"); + + // Test HTTPS URLs + let https = RPC::from_url("https://example.com").unwrap(); + assert!(matches!(https, RPC::Https(_))); + assert_eq!(https.as_http_url(), "https://example.com"); + assert_eq!(https.as_ws_url(), "wss://example.com"); + + // Test WS URLs + let ws = RPC::from_url("ws://localhost:8545").unwrap(); + assert!(matches!(ws, RPC::Ws(_))); + assert_eq!(ws.as_http_url(), "http://localhost:8545"); + assert_eq!(ws.as_ws_url(), "ws://localhost:8545"); + + // Test WSS URLs + let wss = RPC::from_url("wss://example.com").unwrap(); + assert!(matches!(wss, RPC::Wss(_))); + assert_eq!(wss.as_http_url(), "https://example.com"); + assert_eq!(wss.as_ws_url(), "wss://example.com"); } + #[test] - fn test_ensure_ws_rpc() { - assert_eq!(ensure_ws_rpc("http://foo.com"), "ws://foo.com"); - assert_eq!(ensure_ws_rpc("https://foo.com"), "wss://foo.com"); - assert_eq!(ensure_ws_rpc("wss://foo.com"), "wss://foo.com"); - assert_eq!(ensure_ws_rpc("ws://foo.com"), "ws://foo.com"); + fn test_rpc_type_properties() { + assert!(!RPC::from_url("http://example.com").unwrap().is_secure()); + assert!(RPC::from_url("https://example.com") + .unwrap() + .is_secure()); + assert!(!RPC::from_url("ws://example.com").unwrap().is_secure()); + assert!(RPC::from_url("wss://example.com").unwrap().is_secure()); + + assert!(!RPC::from_url("http://example.com") + .unwrap() + .is_websocket()); + assert!(!RPC::from_url("https://example.com") + .unwrap() + .is_websocket()); + assert!(RPC::from_url("ws://example.com") + .unwrap() + .is_websocket()); + assert!(RPC::from_url("wss://example.com") + .unwrap() + .is_websocket()); } } From 46a491231799a9cb59555ec718320c9558f2dc8f Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 29 Nov 2024 10:20:57 +0500 Subject: [PATCH 2/7] add URL for scheme selection --- packages/ciphernode/Cargo.lock | 257 +++++++++++++++++- packages/ciphernode/Cargo.toml | 1 + .../ciphernode/enclave_node/src/aggregator.rs | 7 +- .../ciphernode/enclave_node/src/ciphernode.rs | 5 +- packages/ciphernode/evm/Cargo.toml | 1 + packages/ciphernode/evm/src/helpers.rs | 92 +++---- 6 files changed, 303 insertions(+), 60 deletions(-) diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 827303bc..589b36ac 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -2321,6 +2321,7 @@ dependencies = [ "sortition", "tokio", "tracing", + "url", "zeroize", ] @@ -3082,6 +3083,124 @@ dependencies = [ "tracing", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "idna" version = "0.4.0" @@ -3094,12 +3213,23 @@ dependencies = [ [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -3775,6 +3905,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.12" @@ -5652,6 +5788,12 @@ dependencies = [ "der", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -5932,6 +6074,16 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -6297,12 +6449,12 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.5.0", + "idna 1.0.3", "percent-encoding", ] @@ -6312,6 +6464,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -6731,6 +6895,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "ws_stream_wasm" version = "0.7.4" @@ -6800,6 +6976,30 @@ dependencies = [ "time", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.35" @@ -6821,6 +7021,27 @@ dependencies = [ "syn 2.0.72", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -6840,3 +7061,25 @@ dependencies = [ "quote", "syn 2.0.72", ] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] diff --git a/packages/ciphernode/Cargo.toml b/packages/ciphernode/Cargo.toml index a47e6edf..fa1f8ffe 100644 --- a/packages/ciphernode/Cargo.toml +++ b/packages/ciphernode/Cargo.toml @@ -54,6 +54,7 @@ sha2 = "0.10.8" tokio = { version = "1.38", features = ["full"] } tracing = "0.1.37" tracing-subscriber = { version = "0.3", features = ["env-filter"] } +url = "2.5.4" libp2p = { version = "0.54.1", features = [ "async-std", "identify", diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index 5c17bb05..e466b53b 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -44,10 +44,11 @@ pub async fn setup_aggregator( .iter() .filter(|chain| chain.enabled.unwrap_or(true)) { - let rpc_url = RPC::from_url(&chain.rpc_url).unwrap(); + let rpc_url = RPC::from_url(&chain.rpc_url).map_err(|e| { + anyhow::anyhow!("Failed to parse RPC URL for chain {}: {}", chain.name, e) + })?; let read_provider = create_readonly_provider(&rpc_url.as_ws_url()).await?; - let write_provider = - create_provider_with_signer(&rpc_url.as_http_url(), &signer).await?; + let write_provider = create_provider_with_signer(&rpc_url.as_http_url(), &signer).await?; EnclaveSol::attach( &bus, diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index 58fc31c1..bd7d95d6 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -44,8 +44,9 @@ pub async fn setup_ciphernode( .iter() .filter(|chain| chain.enabled.unwrap_or(true)) { - let rpc_url = RPC::from_url(&chain.rpc_url).unwrap(); - + let rpc_url = RPC::from_url(&chain.rpc_url).map_err(|e| { + anyhow::anyhow!("Failed to parse RPC URL for chain {}: {}", chain.name, e) + })?; let read_provider = create_readonly_provider(&rpc_url.as_ws_url()).await?; EnclaveSolReader::attach( &bus, diff --git a/packages/ciphernode/evm/Cargo.toml b/packages/ciphernode/evm/Cargo.toml index 6eb95378..9983eefc 100644 --- a/packages/ciphernode/evm/Cargo.toml +++ b/packages/ciphernode/evm/Cargo.toml @@ -18,6 +18,7 @@ cipher = { path = "../cipher" } tokio = { workspace = true } tracing = { workspace = true } serde = { workspace = true } +url = { workspace = true } zeroize = { workspace = true } [dev-dependencies] diff --git a/packages/ciphernode/evm/src/helpers.rs b/packages/ciphernode/evm/src/helpers.rs index 256b0bc6..aa171913 100644 --- a/packages/ciphernode/evm/src/helpers.rs +++ b/packages/ciphernode/evm/src/helpers.rs @@ -10,13 +10,13 @@ use alloy::{ signers::local::PrivateKeySigner, transports::{BoxTransport, Transport}, }; -use anyhow::{bail,Context, Result}; +use anyhow::{bail, Context, Result}; use cipher::Cipher; use data::Repository; use std::{env, marker::PhantomData, sync::Arc}; +use url::Url; use zeroize::Zeroizing; - #[derive(Clone)] pub enum RPC { Http(String), @@ -27,32 +27,39 @@ pub enum RPC { impl RPC { pub fn from_url(url: &str) -> Result { - if url.starts_with("http://") { - Ok(RPC::Http(url.to_string())) - } else if url.starts_with("https://") { - Ok(RPC::Https(url.to_string())) - } else if url.starts_with("ws://") { - Ok(RPC::Ws(url.to_string())) - } else if url.starts_with("wss://") { - Ok(RPC::Wss(url.to_string())) - } else { - bail!("Invalid RPC URL, Please specify a protocol. (http://, https://, ws://, wss://)"); + let parsed = Url::parse(url).context("Invalid URL format")?; + match parsed.scheme() { + "http" => Ok(RPC::Http(url.to_string())), + "https" => Ok(RPC::Https(url.to_string())), + "ws" => Ok(RPC::Ws(url.to_string())), + "wss" => Ok(RPC::Wss(url.to_string())), + _ => bail!("Invalid protocol. Expected: http://, https://, ws://, wss://"), } } pub fn as_http_url(&self) -> String { match self { RPC::Http(url) | RPC::Https(url) => url.clone(), - RPC::Ws(url) => url.replacen("ws://", "http://", 1), - RPC::Wss(url) => url.replacen("wss://", "https://", 1), + RPC::Ws(url) | RPC::Wss(url) => { + let mut parsed = Url::parse(url).expect("URL was validated in constructor"); + parsed + .set_scheme(if self.is_secure() { "https" } else { "http" }) + .expect("http(s) are valid schemes"); + parsed.to_string() + } } } pub fn as_ws_url(&self) -> String { match self { - RPC::Http(url) => url.replacen("http://", "ws://", 1), - RPC::Https(url) => url.replacen("https://", "wss://", 1), RPC::Ws(url) | RPC::Wss(url) => url.clone(), + RPC::Http(url) | RPC::Https(url) => { + let mut parsed = Url::parse(url).expect("URL was validated in constructor"); + parsed + .set_scheme(if self.is_secure() { "wss" } else { "ws" }) + .expect("ws(s) are valid schemes"); + parsed.to_string() + } } } @@ -166,7 +173,6 @@ pub async fn get_signer_from_repository( Ok(Arc::new(signer)) } - #[cfg(test)] mod test { use super::*; @@ -174,50 +180,40 @@ mod test { #[test] fn test_rpc_type_conversion() { // Test HTTP URLs - let http = RPC::from_url("http://localhost:8545").unwrap(); + let http = RPC::from_url("http://localhost:8545/").unwrap(); assert!(matches!(http, RPC::Http(_))); - assert_eq!(http.as_http_url(), "http://localhost:8545"); - assert_eq!(http.as_ws_url(), "ws://localhost:8545"); + assert_eq!(http.as_http_url(), "http://localhost:8545/"); + assert_eq!(http.as_ws_url(), "ws://localhost:8545/"); // Test HTTPS URLs - let https = RPC::from_url("https://example.com").unwrap(); + let https = RPC::from_url("https://example.com/").unwrap(); assert!(matches!(https, RPC::Https(_))); - assert_eq!(https.as_http_url(), "https://example.com"); - assert_eq!(https.as_ws_url(), "wss://example.com"); + assert_eq!(https.as_http_url(), "https://example.com/"); + assert_eq!(https.as_ws_url(), "wss://example.com/"); // Test WS URLs - let ws = RPC::from_url("ws://localhost:8545").unwrap(); + let ws = RPC::from_url("ws://localhost:8545/").unwrap(); assert!(matches!(ws, RPC::Ws(_))); - assert_eq!(ws.as_http_url(), "http://localhost:8545"); - assert_eq!(ws.as_ws_url(), "ws://localhost:8545"); + assert_eq!(ws.as_http_url(), "http://localhost:8545/"); + assert_eq!(ws.as_ws_url(), "ws://localhost:8545/"); // Test WSS URLs - let wss = RPC::from_url("wss://example.com").unwrap(); + let wss = RPC::from_url("wss://example.com/").unwrap(); assert!(matches!(wss, RPC::Wss(_))); - assert_eq!(wss.as_http_url(), "https://example.com"); - assert_eq!(wss.as_ws_url(), "wss://example.com"); + assert_eq!(wss.as_http_url(), "https://example.com/"); + assert_eq!(wss.as_ws_url(), "wss://example.com/"); } #[test] fn test_rpc_type_properties() { - assert!(!RPC::from_url("http://example.com").unwrap().is_secure()); - assert!(RPC::from_url("https://example.com") - .unwrap() - .is_secure()); - assert!(!RPC::from_url("ws://example.com").unwrap().is_secure()); - assert!(RPC::from_url("wss://example.com").unwrap().is_secure()); - - assert!(!RPC::from_url("http://example.com") - .unwrap() - .is_websocket()); - assert!(!RPC::from_url("https://example.com") - .unwrap() - .is_websocket()); - assert!(RPC::from_url("ws://example.com") - .unwrap() - .is_websocket()); - assert!(RPC::from_url("wss://example.com") - .unwrap() - .is_websocket()); + assert!(!RPC::from_url("http://example.com/").unwrap().is_secure()); + assert!(RPC::from_url("https://example.com/").unwrap().is_secure()); + assert!(!RPC::from_url("ws://example.com/").unwrap().is_secure()); + assert!(RPC::from_url("wss://example.com/").unwrap().is_secure()); + + assert!(!RPC::from_url("http://example.com/").unwrap().is_websocket()); + assert!(!RPC::from_url("https://example.com/").unwrap().is_websocket()); + assert!(RPC::from_url("ws://example.com/").unwrap().is_websocket()); + assert!(RPC::from_url("wss://example.com/").unwrap().is_websocket()); } } From c69f289066c691235804fa0131155a24ad7e52bb Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 29 Nov 2024 10:29:26 +0500 Subject: [PATCH 3/7] Add Auth Config --- packages/ciphernode/config/src/app_config.rs | 28 ++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index ec340ccb..b3c33527 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -45,11 +45,30 @@ pub struct ContractAddresses { pub filter_registry: Contract, } +#[derive(Debug, Deserialize, Serialize, PartialEq)] +#[serde(tag = "type", content = "credentials")] +pub enum RpcAuth { + None, + Basic { + username: String, + password: String, + }, + Bearer(String) +} + +impl Default for RpcAuth { + fn default() -> Self { + RpcAuth::None + } +} + #[derive(Debug, Deserialize, Serialize)] pub struct ChainConfig { pub enabled: Option, pub name: String, pub rpc_url: String, // We may need multiple per chain for redundancy at a later point + #[serde(default)] + pub rpc_auth: RpcAuth, pub contracts: ContractAddresses, } @@ -331,6 +350,11 @@ mod tests { chains: - name: "hardhat" rpc_url: "ws://localhost:8545" + rpc_auth: + type: "Basic" + credentials: + username: "testUser" + password: "testPassword" contracts: enclave: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" ciphernode_registry: @@ -353,6 +377,10 @@ chains: chain.contracts.ciphernode_registry.address(), "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" ); + assert_eq!(chain.rpc_auth, RpcAuth::Basic { + username: "testUser".to_string(), + password: "testPassword".to_string(), + }); assert_eq!(chain.contracts.enclave.deploy_block(), None); assert_eq!( chain.contracts.ciphernode_registry.deploy_block(), From d86468f11aa3c5bdb7c7e6e122744a3f095a09e0 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 29 Nov 2024 11:00:05 +0500 Subject: [PATCH 4/7] Add Auth Methods for http and ws --- packages/ciphernode/Cargo.lock | 1 + packages/ciphernode/evm/Cargo.toml | 7 +-- packages/ciphernode/evm/src/helpers.rs | 73 +++++++++++++++++++++++++- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/packages/ciphernode/Cargo.lock b/packages/ciphernode/Cargo.lock index 589b36ac..83bf4899 100644 --- a/packages/ciphernode/Cargo.lock +++ b/packages/ciphernode/Cargo.lock @@ -2313,6 +2313,7 @@ dependencies = [ "async-trait", "base64", "cipher 0.1.0", + "config", "data", "enclave-core", "enclave_node", diff --git a/packages/ciphernode/evm/Cargo.toml b/packages/ciphernode/evm/Cargo.toml index 9983eefc..0a50c22a 100644 --- a/packages/ciphernode/evm/Cargo.toml +++ b/packages/ciphernode/evm/Cargo.toml @@ -10,14 +10,15 @@ alloy-primitives = { workspace = true } anyhow = { workspace = true } async-trait = { workspace = true } base64 = { workspace = true } -enclave-core = { path = "../core" } +cipher = { path = "../cipher" } +config = { path = "../config" } data = { path = "../data" } +enclave-core = { path = "../core" } futures-util = { workspace = true } sortition = { path = "../sortition" } -cipher = { path = "../cipher" } +serde = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } -serde = { workspace = true } url = { workspace = true } zeroize = { workspace = true } diff --git a/packages/ciphernode/evm/src/helpers.rs b/packages/ciphernode/evm/src/helpers.rs index aa171913..fc844071 100644 --- a/packages/ciphernode/evm/src/helpers.rs +++ b/packages/ciphernode/evm/src/helpers.rs @@ -7,15 +7,29 @@ use alloy::{ }, Identity, Provider, ProviderBuilder, RootProvider, }, + pubsub::PubSubFrontend, + rpc::client::RpcClient, signers::local::PrivateKeySigner, - transports::{BoxTransport, Transport}, + transports::{ + http::{ + reqwest::{ + header::{HeaderMap, HeaderValue, AUTHORIZATION}, + Client, + }, + Http, + }, + ws::WsConnect, + Authorization, BoxTransport, Transport, + }, }; use anyhow::{bail, Context, Result}; +use base64::{engine::general_purpose::STANDARD, Engine}; use cipher::Cipher; use data::Repository; use std::{env, marker::PhantomData, sync::Arc}; use url::Url; use zeroize::Zeroizing; +use config::RpcAuth as ConfigRpcAuth; #[derive(Clone)] pub enum RPC { @@ -110,6 +124,63 @@ where } } +#[derive(Clone)] +pub enum RpcAuth { + None, + Basic { + username: String, + password: String, + }, + Bearer(String) +} + +impl RpcAuth { + fn to_header_value(&self) -> Option { + match self { + RpcAuth::None => None, + RpcAuth::Basic { username, password } => { + let auth = format!( + "Basic {}", + STANDARD.encode(format!("{}:{}", username, password)) + ); + HeaderValue::from_str(&auth).ok() + } + RpcAuth::Bearer(token) => HeaderValue::from_str(&format!("Bearer {}", token)).ok(), + } + } + + fn to_ws_auth(&self) -> Option { + match self { + RpcAuth::None => None, + RpcAuth::Basic { username, password } => { + Some(Authorization::basic(username, password)) + } + RpcAuth::Bearer(token) => Some(Authorization::bearer(token)), + } + } +} + + +impl From for RpcAuth { + fn from(value: ConfigRpcAuth) -> Self { + match value { + ConfigRpcAuth::None => RpcAuth::None, + ConfigRpcAuth::Basic { username, password } => RpcAuth::Basic { username, password }, + ConfigRpcAuth::Bearer(token) => RpcAuth::Bearer(token), + } + } +} + +impl From for ConfigRpcAuth { + fn from(value: RpcAuth) -> Self { + match value { + RpcAuth::None => ConfigRpcAuth::None, + RpcAuth::Basic { username, password } => ConfigRpcAuth::Basic { username, password }, + RpcAuth::Bearer(token) => ConfigRpcAuth::Bearer(token), + } + } +} + pub type ReadonlyProvider = RootProvider; pub async fn create_readonly_provider( From e720c8a70ad6ad456c103013f66384270a3f5221 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 29 Nov 2024 11:46:53 +0500 Subject: [PATCH 5/7] Add ProviderConfig for auth --- packages/ciphernode/config/src/app_config.rs | 2 +- .../ciphernode/enclave_node/src/aggregator.rs | 9 +- .../ciphernode/enclave_node/src/ciphernode.rs | 5 +- .../evm/src/ciphernode_registry_sol.rs | 6 +- packages/ciphernode/evm/src/enclave_sol.rs | 7 +- .../ciphernode/evm/src/enclave_sol_writer.rs | 11 +- packages/ciphernode/evm/src/helpers.rs | 186 ++++++++++++------ .../ciphernode/evm/src/registry_filter_sol.rs | 13 +- 8 files changed, 150 insertions(+), 89 deletions(-) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index b3c33527..de225b04 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -45,7 +45,7 @@ pub struct ContractAddresses { pub filter_registry: Contract, } -#[derive(Debug, Deserialize, Serialize, PartialEq)] +#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)] #[serde(tag = "type", content = "credentials")] pub enum RpcAuth { None, diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index e466b53b..a90ce4f5 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -5,7 +5,7 @@ use config::AppConfig; use enclave_core::EventBus; use evm::{ helpers::{ - create_provider_with_signer, create_readonly_provider, get_signer_from_repository, RPC, + get_signer_from_repository, RPC, ProviderConfig }, CiphernodeRegistrySol, EnclaveSol, RegistryFilterSol, }; @@ -46,9 +46,10 @@ pub async fn setup_aggregator( { let rpc_url = RPC::from_url(&chain.rpc_url).map_err(|e| { anyhow::anyhow!("Failed to parse RPC URL for chain {}: {}", chain.name, e) - })?; - let read_provider = create_readonly_provider(&rpc_url.as_ws_url()).await?; - let write_provider = create_provider_with_signer(&rpc_url.as_http_url(), &signer).await?; + })?; + let provider_config = ProviderConfig::new(rpc_url, chain.rpc_auth.clone().into()); + let read_provider = provider_config.create_readonly_provider().await?; + let write_provider = provider_config.create_ws_signer_provider(&signer).await?; EnclaveSol::attach( &bus, diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index bd7d95d6..3edece4c 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -5,7 +5,7 @@ use cipher::Cipher; use config::AppConfig; use enclave_core::{get_tag, EventBus}; use evm::{ - helpers::{create_readonly_provider, RPC}, + helpers::{RPC, ProviderConfig}, CiphernodeRegistrySol, EnclaveSolReader, }; use logger::SimpleLogger; @@ -47,7 +47,8 @@ pub async fn setup_ciphernode( let rpc_url = RPC::from_url(&chain.rpc_url).map_err(|e| { anyhow::anyhow!("Failed to parse RPC URL for chain {}: {}", chain.name, e) })?; - let read_provider = create_readonly_provider(&rpc_url.as_ws_url()).await?; + let provider_config = ProviderConfig::new(rpc_url, chain.rpc_auth.clone().into()); + let read_provider = provider_config.create_readonly_provider().await?; EnclaveSolReader::attach( &bus, &read_provider, diff --git a/packages/ciphernode/evm/src/ciphernode_registry_sol.rs b/packages/ciphernode/evm/src/ciphernode_registry_sol.rs index d88e6552..5367487e 100644 --- a/packages/ciphernode/evm/src/ciphernode_registry_sol.rs +++ b/packages/ciphernode/evm/src/ciphernode_registry_sol.rs @@ -7,7 +7,7 @@ use actix::{Actor, Addr}; use alloy::{ primitives::{LogData, B256}, sol, - sol_types::SolEvent, + sol_types::SolEvent, transports::BoxTransport, }; use anyhow::Result; use data::Repository; @@ -102,7 +102,7 @@ pub struct CiphernodeRegistrySolReader; impl CiphernodeRegistrySolReader { pub async fn attach( bus: &Addr, - provider: &WithChainId, + provider: &WithChainId, contract_address: &str, repository: &Repository, start_block: Option, @@ -128,7 +128,7 @@ pub struct CiphernodeRegistrySol; impl CiphernodeRegistrySol { pub async fn attach( bus: &Addr, - provider: &WithChainId, + provider: &WithChainId, contract_address: &str, repository: &Repository, start_block: Option, diff --git a/packages/ciphernode/evm/src/enclave_sol.rs b/packages/ciphernode/evm/src/enclave_sol.rs index ed1f76bc..a766bcde 100644 --- a/packages/ciphernode/evm/src/enclave_sol.rs +++ b/packages/ciphernode/evm/src/enclave_sol.rs @@ -2,9 +2,10 @@ use crate::{ enclave_sol_reader::EnclaveSolReader, enclave_sol_writer::EnclaveSolWriter, event_reader::EvmEventReaderState, - helpers::{ReadonlyProvider, SignerProvider, WithChainId}, + helpers::{ReadonlyProvider, SignerProvider, WithChainId, RpcWSClient}, }; use actix::Addr; +use alloy::transports::BoxTransport; use anyhow::Result; use data::Repository; use enclave_core::EventBus; @@ -13,8 +14,8 @@ pub struct EnclaveSol; impl EnclaveSol { pub async fn attach( bus: &Addr, - read_provider: &WithChainId, - write_provider: &WithChainId, + read_provider: &WithChainId, + write_provider: &WithChainId, RpcWSClient>, contract_address: &str, repository: &Repository, start_block: Option, diff --git a/packages/ciphernode/evm/src/enclave_sol_writer.rs b/packages/ciphernode/evm/src/enclave_sol_writer.rs index 77d53ab7..9e595198 100644 --- a/packages/ciphernode/evm/src/enclave_sol_writer.rs +++ b/packages/ciphernode/evm/src/enclave_sol_writer.rs @@ -1,5 +1,4 @@ -use crate::helpers::SignerProvider; -use crate::helpers::WithChainId; +use crate::helpers::{SignerProvider, WithChainId, RpcWSClient}; use actix::prelude::*; use actix::Addr; use alloy::{primitives::Address, sol}; @@ -21,7 +20,7 @@ sol!( /// Consumes events from the event bus and calls EVM methods on the Enclave.sol contract pub struct EnclaveSolWriter { - provider: WithChainId, + provider: WithChainId, RpcWSClient>, contract_address: Address, bus: Addr, } @@ -29,7 +28,7 @@ pub struct EnclaveSolWriter { impl EnclaveSolWriter { pub fn new( bus: &Addr, - provider: &WithChainId, + provider: &WithChainId, RpcWSClient>, contract_address: Address, ) -> Result { Ok(Self { @@ -41,7 +40,7 @@ impl EnclaveSolWriter { pub async fn attach( bus: &Addr, - provider: &WithChainId, + provider: &WithChainId, RpcWSClient>, contract_address: &str, ) -> Result> { let addr = EnclaveSolWriter::new(bus, provider, contract_address.parse()?)?.start(); @@ -108,7 +107,7 @@ impl Handler for EnclaveSolWriter { } async fn publish_plaintext_output( - provider: WithChainId, + provider: WithChainId, RpcWSClient>, contract_address: Address, e3_id: E3id, decrypted_output: Vec, diff --git a/packages/ciphernode/evm/src/helpers.rs b/packages/ciphernode/evm/src/helpers.rs index fc844071..3f104b4e 100644 --- a/packages/ciphernode/evm/src/helpers.rs +++ b/packages/ciphernode/evm/src/helpers.rs @@ -86,44 +86,6 @@ impl RPC { } } -/// We need to cache the chainId so we can easily use it in a non-async situation -/// This wrapper just stores the chain_id with the Provider -#[derive(Clone)] -// We have to be generic over T as the transport provider in order to handle different transport -// mechanisms such as the HttpClient etc. -pub struct WithChainId -where - P: Provider, - T: Transport + Clone, -{ - provider: Arc

, - chain_id: u64, - _t: PhantomData, -} - -impl WithChainId -where - P: Provider, - T: Transport + Clone, -{ - pub async fn new(provider: P) -> Result { - let chain_id = provider.get_chain_id().await?; - Ok(Self { - provider: Arc::new(provider), - chain_id, - _t: PhantomData, - }) - } - - pub fn get_provider(&self) -> Arc

{ - self.provider.clone() - } - - pub fn get_chain_id(&self) -> u64 { - self.chain_id - } -} - #[derive(Clone)] pub enum RpcAuth { None, @@ -181,20 +143,48 @@ impl From for ConfigRpcAuth { } } -pub type ReadonlyProvider = RootProvider; -pub async fn create_readonly_provider( - rpc_url: &str, -) -> Result> { - let provider = ProviderBuilder::new() - .on_builtin(rpc_url) - .await - .context("Could not create ReadOnlyProvider")? - .into(); - Ok(WithChainId::new(provider).await?) +/// We need to cache the chainId so we can easily use it in a non-async situation +/// This wrapper just stores the chain_id with the Provider +#[derive(Clone)] +// We have to be generic over T as the transport provider in order to handle different transport +// mechanisms such as the HttpClient etc. +pub struct WithChainId +where + P: Provider, + T: Transport + Clone, +{ + provider: Arc

, + chain_id: u64, + _t: PhantomData, } -pub type SignerProvider = FillProvider< +impl WithChainId +where + P: Provider, + T: Transport + Clone, +{ + pub async fn new(provider: P) -> Result { + let chain_id = provider.get_chain_id().await?; + Ok(Self { + provider: Arc::new(provider), + chain_id, + _t: PhantomData, + }) + } + + pub fn get_provider(&self) -> Arc

{ + self.provider.clone() + } + + pub fn get_chain_id(&self) -> u64 { + self.chain_id + } +} + +pub type RpcWSClient = PubSubFrontend; +pub type RpcHttpClient = Http; +pub type SignerProvider = FillProvider< JoinFill< JoinFill< Identity, @@ -202,23 +192,93 @@ pub type SignerProvider = FillProvider< >, WalletFiller, >, - RootProvider, - BoxTransport, + RootProvider, + T, Ethereum, >; -pub async fn create_provider_with_signer( - rpc_url: &str, - signer: &Arc, -) -> Result> { - let wallet = EthereumWallet::from(signer.clone()); - let provider = ProviderBuilder::new() - .with_recommended_fillers() - .wallet(wallet) - .on_builtin(rpc_url) - .await?; - - Ok(WithChainId::new(provider).await?) +pub type ReadonlyProvider = RootProvider; + + +#[derive(Clone)] +pub struct ProviderConfig { + rpc: RPC, + auth: RpcAuth, +} + +impl ProviderConfig { + pub fn new(rpc: RPC, auth: RpcAuth) -> Self { + Self { rpc, auth } + } + + async fn create_ws_provider(&self) -> Result> { + Ok(ProviderBuilder::new() + .on_ws(self.create_ws_connect()) + .await? + .boxed()) + } + + async fn create_http_provider(&self) -> Result> { + Ok(ProviderBuilder::new() + .on_client(self.create_http_client()?) + .boxed()) + } + + + pub async fn create_readonly_provider(&self) -> Result> { + let provider = if self.rpc.is_websocket() { + self.create_ws_provider().await? + } else { + self.create_http_provider().await? + }; + WithChainId::new(provider).await + } + + pub async fn create_ws_signer_provider( + &self, + signer: &Arc, + ) -> Result, RpcWSClient>> { + let wallet = EthereumWallet::from(signer.clone()); + let provider = ProviderBuilder::new() + .with_recommended_fillers() + .wallet(wallet) + .on_ws(self.create_ws_connect()) + .await.context("Failed to create WS signer provider")?; + + WithChainId::new(provider).await + } + + pub async fn create_http_signer_provider( + &self, + signer: &Arc, + ) -> Result, RpcHttpClient>> { + let wallet = EthereumWallet::from(signer.clone()); + let provider = ProviderBuilder::new() + .with_recommended_fillers() + .wallet(wallet) + .on_client(self.create_http_client()?); + WithChainId::new(provider).await + } + + fn create_ws_connect(&self) -> WsConnect { + if let Some(ws_auth) = self.auth.to_ws_auth() { + WsConnect::new(self.rpc.as_ws_url()).with_auth(ws_auth) + } else { + WsConnect::new(self.rpc.as_ws_url()) + } + } + + fn create_http_client(&self) -> Result>> { + let mut headers = HeaderMap::new(); + if let Some(auth_header) = self.auth.to_header_value() { + headers.insert(AUTHORIZATION, auth_header); + } + let client = Client::builder() + .default_headers(headers) + .build()?; + let http = Http::with_client(client, self.rpc.as_http_url().parse()?); + Ok(RpcClient::new(http, false)) + } } pub async fn pull_eth_signer_from_env(var: &str) -> Result> { diff --git a/packages/ciphernode/evm/src/registry_filter_sol.rs b/packages/ciphernode/evm/src/registry_filter_sol.rs index 2c8dedf9..7360e05e 100644 --- a/packages/ciphernode/evm/src/registry_filter_sol.rs +++ b/packages/ciphernode/evm/src/registry_filter_sol.rs @@ -1,10 +1,9 @@ -use crate::helpers::{SignerProvider, WithChainId}; +use crate::helpers::{SignerProvider, WithChainId, RpcWSClient}; use actix::prelude::*; use alloy::{ primitives::{Address, Bytes, U256}, rpc::types::TransactionReceipt, sol, - transports::BoxTransport, }; use anyhow::Result; use enclave_core::{ @@ -20,7 +19,7 @@ sol!( ); pub struct RegistryFilterSolWriter { - provider: WithChainId, + provider: WithChainId, RpcWSClient>, contract_address: Address, bus: Addr, } @@ -28,7 +27,7 @@ pub struct RegistryFilterSolWriter { impl RegistryFilterSolWriter { pub async fn new( bus: &Addr, - provider: &WithChainId, + provider: &WithChainId, RpcWSClient>, contract_address: Address, ) -> Result { Ok(Self { @@ -40,7 +39,7 @@ impl RegistryFilterSolWriter { pub async fn attach( bus: &Addr, - provider: &WithChainId, + provider: &WithChainId, RpcWSClient>, contract_address: &str, ) -> Result> { let addr = RegistryFilterSolWriter::new(bus, provider, contract_address.parse()?) @@ -107,7 +106,7 @@ impl Handler for RegistryFilterSolWriter { } pub async fn publish_committee( - provider: WithChainId, + provider: WithChainId, RpcWSClient>, contract_address: Address, e3_id: E3id, nodes: OrderedSet, @@ -129,7 +128,7 @@ pub struct RegistryFilterSol; impl RegistryFilterSol { pub async fn attach( bus: &Addr, - provider: &WithChainId, + provider: &WithChainId, RpcWSClient>, contract_address: &str, ) -> Result<()> { RegistryFilterSolWriter::attach(bus, provider, contract_address).await?; From 89d410bffcc2a40306a757b15afbdeaba1b955ef Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 29 Nov 2024 12:02:24 +0500 Subject: [PATCH 6/7] fix formating --- packages/ciphernode/config/src/app_config.rs | 18 +++++----- .../ciphernode/enclave_node/src/aggregator.rs | 6 ++-- .../ciphernode/enclave_node/src/ciphernode.rs | 2 +- .../evm/src/ciphernode_registry_sol.rs | 3 +- packages/ciphernode/evm/src/enclave_sol.rs | 2 +- .../ciphernode/evm/src/enclave_sol_writer.rs | 2 +- packages/ciphernode/evm/src/helpers.rs | 34 ++++++++----------- .../ciphernode/evm/src/registry_filter_sol.rs | 2 +- 8 files changed, 31 insertions(+), 38 deletions(-) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index de225b04..d7790879 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -49,11 +49,8 @@ pub struct ContractAddresses { #[serde(tag = "type", content = "credentials")] pub enum RpcAuth { None, - Basic { - username: String, - password: String, - }, - Bearer(String) + Basic { username: String, password: String }, + Bearer(String), } impl Default for RpcAuth { @@ -377,10 +374,13 @@ chains: chain.contracts.ciphernode_registry.address(), "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" ); - assert_eq!(chain.rpc_auth, RpcAuth::Basic { - username: "testUser".to_string(), - password: "testPassword".to_string(), - }); + assert_eq!( + chain.rpc_auth, + RpcAuth::Basic { + username: "testUser".to_string(), + password: "testPassword".to_string(), + } + ); assert_eq!(chain.contracts.enclave.deploy_block(), None); assert_eq!( chain.contracts.ciphernode_registry.deploy_block(), diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index a90ce4f5..04bcb9d8 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -4,9 +4,7 @@ use cipher::Cipher; use config::AppConfig; use enclave_core::EventBus; use evm::{ - helpers::{ - get_signer_from_repository, RPC, ProviderConfig - }, + helpers::{get_signer_from_repository, ProviderConfig, RPC}, CiphernodeRegistrySol, EnclaveSol, RegistryFilterSol, }; use logger::SimpleLogger; @@ -46,7 +44,7 @@ pub async fn setup_aggregator( { let rpc_url = RPC::from_url(&chain.rpc_url).map_err(|e| { anyhow::anyhow!("Failed to parse RPC URL for chain {}: {}", chain.name, e) - })?; + })?; let provider_config = ProviderConfig::new(rpc_url, chain.rpc_auth.clone().into()); let read_provider = provider_config.create_readonly_provider().await?; let write_provider = provider_config.create_ws_signer_provider(&signer).await?; diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index 3edece4c..ecb20e09 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -5,7 +5,7 @@ use cipher::Cipher; use config::AppConfig; use enclave_core::{get_tag, EventBus}; use evm::{ - helpers::{RPC, ProviderConfig}, + helpers::{ProviderConfig, RPC}, CiphernodeRegistrySol, EnclaveSolReader, }; use logger::SimpleLogger; diff --git a/packages/ciphernode/evm/src/ciphernode_registry_sol.rs b/packages/ciphernode/evm/src/ciphernode_registry_sol.rs index 5367487e..a616411b 100644 --- a/packages/ciphernode/evm/src/ciphernode_registry_sol.rs +++ b/packages/ciphernode/evm/src/ciphernode_registry_sol.rs @@ -7,7 +7,8 @@ use actix::{Actor, Addr}; use alloy::{ primitives::{LogData, B256}, sol, - sol_types::SolEvent, transports::BoxTransport, + sol_types::SolEvent, + transports::BoxTransport, }; use anyhow::Result; use data::Repository; diff --git a/packages/ciphernode/evm/src/enclave_sol.rs b/packages/ciphernode/evm/src/enclave_sol.rs index a766bcde..bcdcab98 100644 --- a/packages/ciphernode/evm/src/enclave_sol.rs +++ b/packages/ciphernode/evm/src/enclave_sol.rs @@ -2,7 +2,7 @@ use crate::{ enclave_sol_reader::EnclaveSolReader, enclave_sol_writer::EnclaveSolWriter, event_reader::EvmEventReaderState, - helpers::{ReadonlyProvider, SignerProvider, WithChainId, RpcWSClient}, + helpers::{ReadonlyProvider, RpcWSClient, SignerProvider, WithChainId}, }; use actix::Addr; use alloy::transports::BoxTransport; diff --git a/packages/ciphernode/evm/src/enclave_sol_writer.rs b/packages/ciphernode/evm/src/enclave_sol_writer.rs index 9e595198..36b519b0 100644 --- a/packages/ciphernode/evm/src/enclave_sol_writer.rs +++ b/packages/ciphernode/evm/src/enclave_sol_writer.rs @@ -1,4 +1,4 @@ -use crate::helpers::{SignerProvider, WithChainId, RpcWSClient}; +use crate::helpers::{RpcWSClient, SignerProvider, WithChainId}; use actix::prelude::*; use actix::Addr; use alloy::{primitives::Address, sol}; diff --git a/packages/ciphernode/evm/src/helpers.rs b/packages/ciphernode/evm/src/helpers.rs index 3f104b4e..851eefe5 100644 --- a/packages/ciphernode/evm/src/helpers.rs +++ b/packages/ciphernode/evm/src/helpers.rs @@ -25,11 +25,11 @@ use alloy::{ use anyhow::{bail, Context, Result}; use base64::{engine::general_purpose::STANDARD, Engine}; use cipher::Cipher; +use config::RpcAuth as ConfigRpcAuth; use data::Repository; use std::{env, marker::PhantomData, sync::Arc}; use url::Url; use zeroize::Zeroizing; -use config::RpcAuth as ConfigRpcAuth; #[derive(Clone)] pub enum RPC { @@ -89,11 +89,8 @@ impl RPC { #[derive(Clone)] pub enum RpcAuth { None, - Basic { - username: String, - password: String, - }, - Bearer(String) + Basic { username: String, password: String }, + Bearer(String), } impl RpcAuth { @@ -114,15 +111,12 @@ impl RpcAuth { fn to_ws_auth(&self) -> Option { match self { RpcAuth::None => None, - RpcAuth::Basic { username, password } => { - Some(Authorization::basic(username, password)) - } + RpcAuth::Basic { username, password } => Some(Authorization::basic(username, password)), RpcAuth::Bearer(token) => Some(Authorization::bearer(token)), } } } - impl From for RpcAuth { fn from(value: ConfigRpcAuth) -> Self { match value { @@ -143,7 +137,6 @@ impl From for ConfigRpcAuth { } } - /// We need to cache the chainId so we can easily use it in a non-async situation /// This wrapper just stores the chain_id with the Provider #[derive(Clone)] @@ -199,7 +192,6 @@ pub type SignerProvider = FillProvider< pub type ReadonlyProvider = RootProvider; - #[derive(Clone)] pub struct ProviderConfig { rpc: RPC, @@ -223,9 +215,10 @@ impl ProviderConfig { .on_client(self.create_http_client()?) .boxed()) } - - pub async fn create_readonly_provider(&self) -> Result> { + pub async fn create_readonly_provider( + &self, + ) -> Result> { let provider = if self.rpc.is_websocket() { self.create_ws_provider().await? } else { @@ -243,8 +236,9 @@ impl ProviderConfig { .with_recommended_fillers() .wallet(wallet) .on_ws(self.create_ws_connect()) - .await.context("Failed to create WS signer provider")?; - + .await + .context("Failed to create WS signer provider")?; + WithChainId::new(provider).await } @@ -273,9 +267,7 @@ impl ProviderConfig { if let Some(auth_header) = self.auth.to_header_value() { headers.insert(AUTHORIZATION, auth_header); } - let client = Client::builder() - .default_headers(headers) - .build()?; + let client = Client::builder().default_headers(headers).build()?; let http = Http::with_client(client, self.rpc.as_http_url().parse()?); Ok(RpcClient::new(http, false)) } @@ -343,7 +335,9 @@ mod test { assert!(RPC::from_url("wss://example.com/").unwrap().is_secure()); assert!(!RPC::from_url("http://example.com/").unwrap().is_websocket()); - assert!(!RPC::from_url("https://example.com/").unwrap().is_websocket()); + assert!(!RPC::from_url("https://example.com/") + .unwrap() + .is_websocket()); assert!(RPC::from_url("ws://example.com/").unwrap().is_websocket()); assert!(RPC::from_url("wss://example.com/").unwrap().is_websocket()); } diff --git a/packages/ciphernode/evm/src/registry_filter_sol.rs b/packages/ciphernode/evm/src/registry_filter_sol.rs index 7360e05e..b4b8f770 100644 --- a/packages/ciphernode/evm/src/registry_filter_sol.rs +++ b/packages/ciphernode/evm/src/registry_filter_sol.rs @@ -1,4 +1,4 @@ -use crate::helpers::{SignerProvider, WithChainId, RpcWSClient}; +use crate::helpers::{RpcWSClient, SignerProvider, WithChainId}; use actix::prelude::*; use alloy::{ primitives::{Address, Bytes, U256}, From f159b96acb625115bb658729540a1dbca8638e78 Mon Sep 17 00:00:00 2001 From: Hamza Khalid Date: Fri, 29 Nov 2024 15:07:26 +0500 Subject: [PATCH 7/7] impl trait for rpc auth --- packages/ciphernode/config/src/app_config.rs | 48 +++++++++++++++++-- .../ciphernode/enclave_node/src/aggregator.rs | 2 +- .../ciphernode/enclave_node/src/ciphernode.rs | 2 +- packages/ciphernode/evm/src/helpers.rs | 43 +++++------------ 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/packages/ciphernode/config/src/app_config.rs b/packages/ciphernode/config/src/app_config.rs index d7790879..903af725 100644 --- a/packages/ciphernode/config/src/app_config.rs +++ b/packages/ciphernode/config/src/app_config.rs @@ -342,7 +342,7 @@ mod tests { let filedir = format!("{}/.config/enclave", home); jail.create_dir(filedir)?; jail.create_file( - filename, + filename.clone(), r#" chains: - name: "hardhat" @@ -361,8 +361,8 @@ chains: "#, )?; - let config: AppConfig = load_config(None).map_err(|err| err.to_string())?; - let chain = config.chains().first().unwrap(); + let mut config: AppConfig = load_config(None).map_err(|err| err.to_string())?; + let mut chain = config.chains().first().unwrap(); assert_eq!(chain.name, "hardhat"); assert_eq!(chain.rpc_url, "ws://localhost:8545"); @@ -386,6 +386,48 @@ chains: chain.contracts.ciphernode_registry.deploy_block(), Some(1764352873645) ); + + jail.create_file( + filename.clone(), + r#" +chains: + - name: "hardhat" + rpc_url: "ws://localhost:8545" + contracts: + enclave: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + ciphernode_registry: + address: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" + deploy_block: 1764352873645 + filter_registry: "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" +"#, + )?; + config = load_config(None).map_err(|err| err.to_string())?; + chain = config.chains().first().unwrap(); + + assert_eq!(chain.rpc_auth, RpcAuth::None); + + jail.create_file( + filename, + r#" +chains: + - name: "hardhat" + rpc_url: "ws://localhost:8545" + rpc_auth: + type: "Bearer" + credentials: "testToken" + contracts: + enclave: "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0" + ciphernode_registry: + address: "0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9" + deploy_block: 1764352873645 + filter_registry: "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9" +"#, + )?; + + config = load_config(None).map_err(|err| err.to_string())?; + chain = config.chains().first().unwrap(); + assert_eq!(chain.rpc_auth, RpcAuth::Bearer("testToken".to_string())); + Ok(()) }); } diff --git a/packages/ciphernode/enclave_node/src/aggregator.rs b/packages/ciphernode/enclave_node/src/aggregator.rs index 04bcb9d8..a63bf636 100644 --- a/packages/ciphernode/enclave_node/src/aggregator.rs +++ b/packages/ciphernode/enclave_node/src/aggregator.rs @@ -45,7 +45,7 @@ pub async fn setup_aggregator( let rpc_url = RPC::from_url(&chain.rpc_url).map_err(|e| { anyhow::anyhow!("Failed to parse RPC URL for chain {}: {}", chain.name, e) })?; - let provider_config = ProviderConfig::new(rpc_url, chain.rpc_auth.clone().into()); + let provider_config = ProviderConfig::new(rpc_url, chain.rpc_auth.clone()); let read_provider = provider_config.create_readonly_provider().await?; let write_provider = provider_config.create_ws_signer_provider(&signer).await?; diff --git a/packages/ciphernode/enclave_node/src/ciphernode.rs b/packages/ciphernode/enclave_node/src/ciphernode.rs index ecb20e09..30386ab2 100644 --- a/packages/ciphernode/enclave_node/src/ciphernode.rs +++ b/packages/ciphernode/enclave_node/src/ciphernode.rs @@ -47,7 +47,7 @@ pub async fn setup_ciphernode( let rpc_url = RPC::from_url(&chain.rpc_url).map_err(|e| { anyhow::anyhow!("Failed to parse RPC URL for chain {}: {}", chain.name, e) })?; - let provider_config = ProviderConfig::new(rpc_url, chain.rpc_auth.clone().into()); + let provider_config = ProviderConfig::new(rpc_url, chain.rpc_auth.clone()); let read_provider = provider_config.create_readonly_provider().await?; EnclaveSolReader::attach( &bus, diff --git a/packages/ciphernode/evm/src/helpers.rs b/packages/ciphernode/evm/src/helpers.rs index 851eefe5..9f6d7373 100644 --- a/packages/ciphernode/evm/src/helpers.rs +++ b/packages/ciphernode/evm/src/helpers.rs @@ -25,7 +25,7 @@ use alloy::{ use anyhow::{bail, Context, Result}; use base64::{engine::general_purpose::STANDARD, Engine}; use cipher::Cipher; -use config::RpcAuth as ConfigRpcAuth; +use config::RpcAuth; use data::Repository; use std::{env, marker::PhantomData, sync::Arc}; use url::Url; @@ -55,7 +55,7 @@ impl RPC { match self { RPC::Http(url) | RPC::Https(url) => url.clone(), RPC::Ws(url) | RPC::Wss(url) => { - let mut parsed = Url::parse(url).expect("URL was validated in constructor"); + let mut parsed = Url::parse(url).expect(&format!("Failed to parse URL: {}", url)); parsed .set_scheme(if self.is_secure() { "https" } else { "http" }) .expect("http(s) are valid schemes"); @@ -68,7 +68,7 @@ impl RPC { match self { RPC::Ws(url) | RPC::Wss(url) => url.clone(), RPC::Http(url) | RPC::Https(url) => { - let mut parsed = Url::parse(url).expect("URL was validated in constructor"); + let mut parsed = Url::parse(url).expect(&format!("Failed to parse URL: {}", url)); parsed .set_scheme(if self.is_secure() { "wss" } else { "ws" }) .expect("ws(s) are valid schemes"); @@ -86,21 +86,19 @@ impl RPC { } } -#[derive(Clone)] -pub enum RpcAuth { - None, - Basic { username: String, password: String }, - Bearer(String), +pub trait AuthConversions { + fn to_header_value(&self) -> Option; + fn to_ws_auth(&self) -> Option; } -impl RpcAuth { +impl AuthConversions for RpcAuth { fn to_header_value(&self) -> Option { match self { RpcAuth::None => None, RpcAuth::Basic { username, password } => { let auth = format!( "Basic {}", - STANDARD.encode(format!("{}:{}", username, password)) + STANDARD.encode(Zeroizing::new(format!("{}:{}", username, password))) ); HeaderValue::from_str(&auth).ok() } @@ -117,26 +115,6 @@ impl RpcAuth { } } -impl From for RpcAuth { - fn from(value: ConfigRpcAuth) -> Self { - match value { - ConfigRpcAuth::None => RpcAuth::None, - ConfigRpcAuth::Basic { username, password } => RpcAuth::Basic { username, password }, - ConfigRpcAuth::Bearer(token) => RpcAuth::Bearer(token), - } - } -} - -impl From for ConfigRpcAuth { - fn from(value: RpcAuth) -> Self { - match value { - RpcAuth::None => ConfigRpcAuth::None, - RpcAuth::Basic { username, password } => ConfigRpcAuth::Basic { username, password }, - RpcAuth::Bearer(token) => ConfigRpcAuth::Bearer(token), - } - } -} - /// We need to cache the chainId so we can easily use it in a non-async situation /// This wrapper just stores the chain_id with the Provider #[derive(Clone)] @@ -267,7 +245,10 @@ impl ProviderConfig { if let Some(auth_header) = self.auth.to_header_value() { headers.insert(AUTHORIZATION, auth_header); } - let client = Client::builder().default_headers(headers).build()?; + let client = Client::builder() + .default_headers(headers) + .build() + .context("Failed to create HTTP client")?; let http = Http::with_client(client, self.rpc.as_http_url().parse()?); Ok(RpcClient::new(http, false)) }