From 64eddcaa3351ba0861f4ffce702fc135162ad55a Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Fri, 24 Mar 2023 10:51:21 -0400 Subject: [PATCH] *WIP --- Cargo.lock | 1 + client/src/client.rs | 42 +----------------- client/src/node.rs | 9 ++-- config/src/base.rs | 2 - config/src/config.rs | 4 -- config/src/networks.rs | 2 - consensus/Cargo.toml | 1 + consensus/src/consensus.rs | 8 +--- consensus/src/rpc/mod.rs | 2 +- consensus/src/rpc/nimbus_rpc.rs | 71 +++++++++++++++++++---------- execution/src/execution.rs | 2 +- execution/src/rpc/http_rpc.rs | 71 ++++++++++++++++++----------- execution/src/rpc/mock_rpc.rs | 79 --------------------------------- execution/src/rpc/mod.rs | 4 +- helios-ts/Cargo.toml | 5 +-- helios-ts/src/lib.rs | 12 +---- 16 files changed, 106 insertions(+), 209 deletions(-) delete mode 100644 execution/src/rpc/mock_rpc.rs diff --git a/Cargo.lock b/Cargo.lock index bd64289..de2a5a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -709,6 +709,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "wasm-timer", + "web-sys", ] [[package]] diff --git a/client/src/client.rs b/client/src/client.rs index 8fec402..64c126c 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -37,8 +37,6 @@ use crate::rpc::Rpc; #[derive(Default)] pub struct ClientBuilder { network: Option, - consensus_rpc: Option>, - execution_rpc: Option>, checkpoint: Option>, #[cfg(not(target_arch = "wasm32"))] rpc_port: Option, @@ -60,16 +58,6 @@ impl ClientBuilder { self } - pub fn consensus_rpc(mut self, consensus_rpc: Arc) -> Self { - self.consensus_rpc = Some(consensus_rpc); - self - } - - pub fn execution_rpc(mut self, execution_rpc: Arc) -> Self { - self.execution_rpc = Some(execution_rpc); - self - } - pub fn checkpoint(mut self, checkpoint: &str) -> Self { let checkpoint = hex::decode(checkpoint.strip_prefix("0x").unwrap_or(checkpoint)) .expect("cannot parse checkpoint"); @@ -120,22 +108,6 @@ impl ClientBuilder { config.to_base_config() }; - let consensus_rpc = self.consensus_rpc.unwrap_or_else(|| { - self.config - .as_ref() - .expect("missing consensus rpc") - .consensus_rpc - .clone() - }); - - let execution_rpc = self.execution_rpc.unwrap_or_else(|| { - self.config - .as_ref() - .expect("missing execution rpc") - .execution_rpc - .clone() - }); - let checkpoint = if let Some(checkpoint) = self.checkpoint { Some(checkpoint) } else if let Some(config) = &self.config { @@ -189,8 +161,6 @@ impl ClientBuilder { }; let config = Config { - consensus_rpc, - execution_rpc, checkpoint, default_checkpoint, #[cfg(not(target_arch = "wasm32"))] @@ -344,11 +314,7 @@ impl Client { // Try to sync again with the new checkpoint by reconstructing the consensus client // We fail fast here since the node is unrecoverable at this point let config = self.node.read().await.config.clone(); - let consensus = ConsensusClient::new( - config.consensus_rpc.clone(), - checkpoint.as_bytes(), - config.clone(), - )?; + let consensus = ConsensusClient::new(checkpoint.as_bytes(), config.clone())?; self.node.write().await.consensus = consensus; self.node.write().await.sync().await?; @@ -388,11 +354,7 @@ impl Client { // Try to sync again with the new checkpoint by reconstructing the consensus client // We fail fast here since the node is unrecoverable at this point let config = self.node.read().await.config.clone(); - let consensus = ConsensusClient::new( - config.consensus_rpc.clone(), - checkpoint.as_bytes(), - config.clone(), - )?; + let consensus = ConsensusClient::new(checkpoint.as_bytes(), config.clone())?; self.node.write().await.consensus = consensus; self.node.write().await.sync().await?; Ok(()) diff --git a/client/src/node.rs b/client/src/node.rs index 75c299d..d5bf2ec 100644 --- a/client/src/node.rs +++ b/client/src/node.rs @@ -34,15 +34,12 @@ pub struct Node { impl Node { pub fn new(config: Arc) -> Result { - let consensus_rpc = &config.consensus_rpc; let checkpoint_hash = &config.checkpoint.as_ref().unwrap(); - let execution_rpc = &config.execution_rpc; - let consensus = ConsensusClient::new(consensus_rpc, checkpoint_hash, config.clone()) + let consensus = ConsensusClient::new(checkpoint_hash, config.clone()) .map_err(NodeError::ConsensusClientCreationError)?; - let execution = Arc::new( - ExecutionClient::new(execution_rpc).map_err(NodeError::ExecutionClientCreationError)?, - ); + let execution = + Arc::new(ExecutionClient::new("").map_err(NodeError::ExecutionClientCreationError)?); let payloads = BTreeMap::new(); let finalized_payloads = BTreeMap::new(); diff --git a/config/src/base.rs b/config/src/base.rs index f28f476..196ba8b 100644 --- a/config/src/base.rs +++ b/config/src/base.rs @@ -9,8 +9,6 @@ use crate::utils::bytes_serialize; #[derive(Serialize, Default)] pub struct BaseConfig { pub rpc_port: u16, - #[serde(skip_serializing, skip_deserializing)] - pub consensus_rpc: Option>, #[serde( deserialize_with = "bytes_deserialize", serialize_with = "bytes_serialize" diff --git a/config/src/config.rs b/config/src/config.rs index 10440ad..0b8cc3f 100644 --- a/config/src/config.rs +++ b/config/src/config.rs @@ -17,9 +17,6 @@ use crate::utils::{bytes_deserialize, bytes_opt_deserialize}; #[derive(Deserialize, Debug, Default)] pub struct Config { #[serde(skip_serializing, skip_deserializing)] - pub consensus_rpc: Arc, - #[serde(skip_serializing, skip_deserializing)] - pub execution_rpc: Arc, pub rpc_port: Option, #[serde(deserialize_with = "bytes_deserialize")] pub default_checkpoint: Vec, @@ -106,7 +103,6 @@ impl Config { pub fn to_base_config(&self) -> BaseConfig { BaseConfig { rpc_port: self.rpc_port.unwrap_or(8545), - consensus_rpc: Some(self.consensus_rpc.clone()), default_checkpoint: self.default_checkpoint.clone(), chain: self.chain.clone(), forks: self.forks.clone(), diff --git a/config/src/networks.rs b/config/src/networks.rs index a97816b..12f67c1 100644 --- a/config/src/networks.rs +++ b/config/src/networks.rs @@ -42,7 +42,6 @@ pub fn mainnet() -> BaseConfig { ) .unwrap(), rpc_port: 8545, - consensus_rpc: Some(Arc::from(JsValue::null())), chain: ChainConfig { chain_id: 1, genesis_time: 1606824023, @@ -80,7 +79,6 @@ pub fn goerli() -> BaseConfig { ) .unwrap(), rpc_port: 8545, - consensus_rpc: None, chain: ChainConfig { chain_id: 5, genesis_time: 1616508000, diff --git a/consensus/Cargo.toml b/consensus/Cargo.toml index 5d6c365..c80bde8 100644 --- a/consensus/Cargo.toml +++ b/consensus/Cargo.toml @@ -27,6 +27,7 @@ wasm-bindgen = "0.2.84" js-sys = "0.3.61" serde-wasm-bindgen = "0.5.0" wasm-bindgen-futures = "0.4.34" +web-sys = {version ="0.3.61", features=["Window","WindowClient"]} [target.'cfg(not(target_arch = "wasm32"))'.dependencies] openssl = { version = "0.10", features = ["vendored"] } diff --git a/consensus/src/consensus.rs b/consensus/src/consensus.rs index e3cbc6e..727bc77 100644 --- a/consensus/src/consensus.rs +++ b/consensus/src/consensus.rs @@ -54,12 +54,8 @@ struct LightClientStore { } impl ConsensusClient { - pub fn new( - rpc_handler: Arc, - checkpoint_block_root: &[u8], - config: Arc, - ) -> Result> { - let rpc = R::new(rpc_handler); + pub fn new(checkpoint_block_root: &[u8], config: Arc) -> Result> { + let rpc = R::new(); Ok(ConsensusClient { rpc, diff --git a/consensus/src/rpc/mod.rs b/consensus/src/rpc/mod.rs index f907448..5543345 100644 --- a/consensus/src/rpc/mod.rs +++ b/consensus/src/rpc/mod.rs @@ -10,7 +10,7 @@ use crate::types::{BeaconBlock, Bootstrap, FinalityUpdate, OptimisticUpdate, Upd #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] pub trait ConsensusRpc { - fn new(rpc_handler: Arc) -> Self; + fn new() -> Self; async fn get_bootstrap(&self, block_root: &'_ [u8]) -> Result; async fn get_updates(&self, period: u64, count: u8) -> Result>; async fn get_finality_update(&self) -> Result; diff --git a/consensus/src/rpc/nimbus_rpc.rs b/consensus/src/rpc/nimbus_rpc.rs index deddccc..8c9fede 100644 --- a/consensus/src/rpc/nimbus_rpc.rs +++ b/consensus/src/rpc/nimbus_rpc.rs @@ -1,17 +1,24 @@ use std::cmp; +use std::collections::HashMap; use std::fmt::{Display, Formatter, Result as FmtResult}; use std::sync::Arc; use async_trait::async_trait; use eyre::{Result, WrapErr}; +use js_sys::{Function, Map, Promise}; +use serde::de::DeserializeOwned; use serde::Deserialize; use serde_json::from_str as from_json_str; +use serde_wasm_bindgen::to_value; +use wasm_bindgen::closure::Closure; use wasm_bindgen::{JsCast, JsValue}; +use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen_futures::JsFuture; use crate::constants::MAX_REQUEST_LIGHT_CLIENT_UPDATES; use crate::types::*; +use web_sys; use super::ConsensusRpc; @@ -20,30 +27,53 @@ pub struct RpcError { message: String, } +impl RpcError { + pub fn new(message: String) -> Self { + Self { message } + } +} + impl Display for RpcError { fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { write!(f, "{}", self.message) } } -impl std::error::Error for RpcError {} +impl From for RpcError { + fn from(js_error: JsValue) -> Self { + let message = js_error + .as_string() + .unwrap_or_else(|| "Unknown error".to_string()); + RpcError::new(message) + } +} + +impl std::error::Error for RpcError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + None + } +} #[derive(Debug)] -pub struct NimbusRpc { - rpc_handler: Arc, +pub struct NimbusRpc {} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = window)] + fn consensus_rpc_handler(data: JsValue) -> Promise; } #[async_trait(?Send)] impl ConsensusRpc for NimbusRpc { - fn new(rpc_handler: Arc) -> Self { - NimbusRpc { rpc_handler } + fn new() -> Self { + NimbusRpc {} } async fn get_bootstrap(&self, block_root: &'_ [u8]) -> Result { let root_hex = hex::encode(block_root); let path = format!("/eth/v1/beacon/light_client/bootstrap/0x{}", root_hex); - let res = self.request::(path, "bootstrap").await?; + let res = self.request::(&path, "bootstrap").await?; Ok(res) } @@ -55,7 +85,7 @@ impl ConsensusRpc for NimbusRpc { period, count ); - let res = self.request::(path, "updates").await?; + let res = self.request::(&path, "updates").await?; Ok(res.iter().map(|d| d.data.clone()).collect()) } @@ -64,7 +94,7 @@ impl ConsensusRpc for NimbusRpc { let path = format!("/eth/v1/beacon/light_client/finality_update"); let res = self - .request::(path, "finality_update") + .request::(&path, "finality_update") .await?; Ok(res.data) @@ -74,7 +104,7 @@ impl ConsensusRpc for NimbusRpc { let path = format!("/eth/v1/beacon/light_client/optimistic_update"); let res = self - .request::(path, "optimistic_update") + .request::(&path, "optimistic_update") .await?; Ok(res.data) @@ -83,7 +113,7 @@ impl ConsensusRpc for NimbusRpc { async fn get_block(&self, slot: u64) -> Result { let path = format!("/eth/v2/beacon/blocks/{}", slot); - let res = self.request::(path, "blocks").await?; + let res = self.request::(&path, "blocks").await?; Ok(res.data.message) } @@ -91,7 +121,7 @@ impl ConsensusRpc for NimbusRpc { async fn chain_id(&self) -> Result { let path = format!("/eth/v1/config/spec"); - let res = self.request::(path, "blocks").await?; + let res = self.request::(&path, "blocks").await?; Ok(res.data.chain_id) } @@ -141,7 +171,7 @@ struct Spec { } impl NimbusRpc { - async fn request(&self, path: String, error_type: &str) -> Result + async fn request(&self, path: &str, error_type: &str) -> Result where for<'a> T: Deserialize<'a>, { @@ -149,20 +179,13 @@ impl NimbusRpc { js_params.set(&JsValue::from_str("method"), &JsValue::from_str("GET")); js_params.set(&JsValue::from_str("path"), &JsValue::from_str(&path)); - let rpc_handler_clone = self.rpc_handler.clone(); - let promise = Arc::clone(&rpc_handler_clone) - .dyn_into::() - .unwrap() - .call1(&JsValue::null(), &js_params) - .expect("Failed to make JSON-RPC request"); - - let result = match promise.dyn_into::() { - Ok(promise) => JsFuture::from(promise).await.unwrap(), - Err(_) => panic!("Unable to convert JS value to Promise"), + let result = unsafe { + let js_future = JsFuture::from(consensus_rpc_handler(JsValue::from(js_params))); + let result = js_future.await?; + result }; - let json = result.as_string().unwrap(); - let response: T = serde_json::from_str(&json)?; + let response: T = serde_json::from_str(&json).map_err(|e| RpcError::new(e.to_string()))?; Ok(response) } diff --git a/execution/src/execution.rs b/execution/src/execution.rs index 38768d9..3d35418 100644 --- a/execution/src/execution.rs +++ b/execution/src/execution.rs @@ -32,7 +32,7 @@ pub struct ExecutionClient { impl ExecutionClient { pub fn new(rpc: &str) -> Result { - let rpc: R = ExecutionRpc::new(rpc)?; + let rpc: R = ExecutionRpc::new()?; Ok(ExecutionClient { rpc }) } diff --git a/execution/src/rpc/http_rpc.rs b/execution/src/rpc/http_rpc.rs index bbb0343..d465cb0 100644 --- a/execution/src/rpc/http_rpc.rs +++ b/execution/src/rpc/http_rpc.rs @@ -27,16 +27,22 @@ use common::errors::RpcError; use super::ExecutionRpc; use js_sys::{Function, Promise}; +use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen_futures::JsFuture; +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_namespace = window)] + fn consensus_rpc_handler(data: JsValue) -> Promise; +} + pub struct HttpRpc { provider: Provider, - rpc_handler: Arc, } impl Clone for HttpRpc { fn clone(&self) -> Self { - Self::new(self.rpc_handler.clone()).unwrap() + Self::new().unwrap() } } @@ -64,18 +70,32 @@ impl From for ProviderError { } } -#[derive(Clone, Debug)] -pub struct LumeProvider { - pub rpc_handler: Arc, +impl From for LumeError { + fn from(error: JsValue) -> Self { + LumeError { + message: error + .as_string() + .unwrap_or_else(|| "Unknown error".to_string()), + } + } } - -impl LumeProvider { - pub fn new(rpc_handler: Arc) -> Self { - LumeProvider { rpc_handler } +impl From> for LumeError { + fn from(error: RpcError) -> Self { + LumeError { + message: error.to_string(), + } + } +} + +#[derive(Clone, Debug)] +pub struct LumeProvider {} + +impl LumeProvider { + pub fn new() -> Self { + LumeProvider {} } } -#[async_trait] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] impl JsonRpcClient for LumeProvider { type Error = LumeError; @@ -85,32 +105,31 @@ impl JsonRpcClient for LumeProvider { T: Debug + Serialize + Send + Sync, R: DeserializeOwned, { - let js_method = JsValue::from(method); let js_params = to_value(¶ms).unwrap(); + let json_str = serde_json::to_string(&js_params).unwrap(); + let js_value = JsValue::from_str(&json_str); - let promise = self - .rpc_handler - .dyn_into::() - .unwrap() - .call2(&js_method, &js_params) - .unwrap(); - let result = match promise.dyn_into::() { - Ok(promise) => JsFuture::from(promise).await.unwrap(), - Err(_) => panic!("Unable to convert JS value to Promise"), + let result = unsafe { + let js_future = JsFuture::from(consensus_rpc_handler(js_value)); + let result = js_future.await?; + result }; + + let json = result.as_string().unwrap(); + let response: R = + serde_json::from_str(&json).map_err(|e| RpcError::new(method, e.to_string()))?; + + Ok(response) } } #[async_trait(?Send)] impl ExecutionRpc for HttpRpc { - fn new(rpc_handler: Arc) -> Result { - let mut client = LumeProvider::new(rpc_handler.clone()); + fn new() -> Result { + let mut client = LumeProvider::new(); let provider = Provider::new(client); - Ok(HttpRpc { - provider, - rpc_handler, - }) + Ok(HttpRpc { provider }) } async fn get_proof( diff --git a/execution/src/rpc/mock_rpc.rs b/execution/src/rpc/mock_rpc.rs deleted file mode 100644 index 42bec5e..0000000 --- a/execution/src/rpc/mock_rpc.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::{fs::read_to_string, path::PathBuf}; - -use async_trait::async_trait; -use common::utils::hex_str_to_bytes; -use ethers::types::{ - transaction::eip2930::AccessList, Address, EIP1186ProofResponse, FeeHistory, Filter, Log, - Transaction, TransactionReceipt, H256, -}; -use eyre::{eyre, Result}; - -use crate::types::CallOpts; - -use super::ExecutionRpc; - -#[derive(Clone)] -pub struct MockRpc { - path: PathBuf, -} - -#[cfg_attr(not(target_arch = "wasm32"), async_trait)] -#[cfg_attr(target_arch = "wasm32", async_trait(?Send))] -impl ExecutionRpc for MockRpc { - fn new(rpc: &str) -> Result { - let path = PathBuf::from(rpc); - Ok(MockRpc { path }) - } - - async fn get_proof( - &self, - _address: &Address, - _slots: &[H256], - _block: u64, - ) -> Result { - let proof = read_to_string(self.path.join("proof.json"))?; - Ok(serde_json::from_str(&proof)?) - } - - async fn create_access_list(&self, _opts: &CallOpts, _block: u64) -> Result { - Err(eyre!("not implemented")) - } - - async fn get_code(&self, _address: &Address, _block: u64) -> Result> { - let code = read_to_string(self.path.join("code.json"))?; - hex_str_to_bytes(&code[0..code.len() - 1]) - } - - async fn send_raw_transaction(&self, _bytes: &[u8]) -> Result { - Err(eyre!("not implemented")) - } - - async fn get_transaction_receipt(&self, _tx_hash: &H256) -> Result> { - let receipt = read_to_string(self.path.join("receipt.json"))?; - Ok(serde_json::from_str(&receipt)?) - } - - async fn get_transaction(&self, _tx_hash: &H256) -> Result> { - let tx = read_to_string(self.path.join("transaction.json"))?; - Ok(serde_json::from_str(&tx)?) - } - - async fn get_logs(&self, _filter: &Filter) -> Result> { - let logs = read_to_string(self.path.join("logs.json"))?; - Ok(serde_json::from_str(&logs)?) - } - - async fn chain_id(&self) -> Result { - Err(eyre!("not implemented")) - } - - async fn get_fee_history( - &self, - _block_count: u64, - _last_block: u64, - _reward_percentiles: &[f64], - ) -> Result { - let fee_history = read_to_string(self.path.join("fee_history.json"))?; - Ok(serde_json::from_str(&fee_history)?) - } -} diff --git a/execution/src/rpc/mod.rs b/execution/src/rpc/mod.rs index d4dd8a5..d568622 100644 --- a/execution/src/rpc/mod.rs +++ b/execution/src/rpc/mod.rs @@ -8,12 +8,10 @@ use eyre::Result; use crate::types::CallOpts; pub mod http_rpc; -pub mod mock_rpc; - #[cfg_attr(not(target_arch = "wasm32"), async_trait)] #[cfg_attr(target_arch = "wasm32", async_trait(?Send))] pub trait ExecutionRpc: Send + Clone + Sync + 'static { - fn new(rpc: &str) -> Result + fn new() -> Result where Self: Sized; diff --git a/helios-ts/Cargo.toml b/helios-ts/Cargo.toml index 143ec93..dd96f77 100644 --- a/helios-ts/Cargo.toml +++ b/helios-ts/Cargo.toml @@ -30,7 +30,4 @@ execution = { path = "../execution" } config = { path = "../config" } [dependencies.web-sys] -version = "0.3" -features = [ - "console", -] +version = "0.3.61" diff --git a/helios-ts/src/lib.rs b/helios-ts/src/lib.rs index a7936aa..f0218ae 100644 --- a/helios-ts/src/lib.rs +++ b/helios-ts/src/lib.rs @@ -28,21 +28,13 @@ pub struct Client { #[wasm_bindgen] impl Client { #[wasm_bindgen(constructor)] - pub fn new( - execution_rpc: JsValue, - consensus_rpc: JsValue, - network: String, - checkpoint: Option, - ) -> Self { + pub fn new(network: String, checkpoint: Option) -> Self { console_error_panic_hook::set_once(); let base = networks::mainnet(); let chain_id = base.chain.chain_id; - let execution_rpc = Arc::new(execution_rpc); - let consensus_rpc = Arc::new(consensus_rpc); - let checkpoint = Some( checkpoint .as_ref() @@ -52,8 +44,6 @@ impl Client { ); let config = Config { - execution_rpc, - consensus_rpc, checkpoint, chain: base.chain,