This commit is contained in:
Derrick Hammer 2023-03-24 10:51:21 -04:00
parent 602b6dfee5
commit 64eddcaa33
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
16 changed files with 106 additions and 209 deletions

1
Cargo.lock generated
View File

@ -709,6 +709,7 @@ dependencies = [
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-timer",
"web-sys",
]
[[package]]

View File

@ -37,8 +37,6 @@ use crate::rpc::Rpc;
#[derive(Default)]
pub struct ClientBuilder {
network: Option<Network>,
consensus_rpc: Option<Arc<wasm_bindgen::JsValue>>,
execution_rpc: Option<Arc<wasm_bindgen::JsValue>>,
checkpoint: Option<Vec<u8>>,
#[cfg(not(target_arch = "wasm32"))]
rpc_port: Option<u16>,
@ -60,16 +58,6 @@ impl ClientBuilder {
self
}
pub fn consensus_rpc(mut self, consensus_rpc: Arc<wasm_bindgen::JsValue>) -> Self {
self.consensus_rpc = Some(consensus_rpc);
self
}
pub fn execution_rpc(mut self, execution_rpc: Arc<wasm_bindgen::JsValue>) -> 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<DB: Database> Client<DB> {
// 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<DB: Database> Client<DB> {
// 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(())

View File

@ -34,15 +34,12 @@ pub struct Node {
impl Node {
pub fn new(config: Arc<Config>) -> Result<Self, NodeError> {
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();

View File

@ -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<Arc<JsValue>>,
#[serde(
deserialize_with = "bytes_deserialize",
serialize_with = "bytes_serialize"

View File

@ -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<wasm_bindgen::JsValue>,
#[serde(skip_serializing, skip_deserializing)]
pub execution_rpc: Arc<wasm_bindgen::JsValue>,
pub rpc_port: Option<u16>,
#[serde(deserialize_with = "bytes_deserialize")]
pub default_checkpoint: Vec<u8>,
@ -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(),

View File

@ -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,

View File

@ -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"] }

View File

@ -54,12 +54,8 @@ struct LightClientStore {
}
impl<R: ConsensusRpc> ConsensusClient<R> {
pub fn new(
rpc_handler: Arc<wasm_bindgen::JsValue>,
checkpoint_block_root: &[u8],
config: Arc<Config>,
) -> Result<ConsensusClient<R>> {
let rpc = R::new(rpc_handler);
pub fn new(checkpoint_block_root: &[u8], config: Arc<Config>) -> Result<ConsensusClient<R>> {
let rpc = R::new();
Ok(ConsensusClient {
rpc,

View File

@ -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<wasm_bindgen::JsValue>) -> Self;
fn new() -> Self;
async fn get_bootstrap(&self, block_root: &'_ [u8]) -> Result<Bootstrap>;
async fn get_updates(&self, period: u64, count: u8) -> Result<Vec<Update>>;
async fn get_finality_update(&self) -> Result<FinalityUpdate>;

View File

@ -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<JsValue> 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<JsValue>,
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<JsValue>) -> Self {
NimbusRpc { rpc_handler }
fn new() -> Self {
NimbusRpc {}
}
async fn get_bootstrap(&self, block_root: &'_ [u8]) -> Result<Bootstrap> {
let root_hex = hex::encode(block_root);
let path = format!("/eth/v1/beacon/light_client/bootstrap/0x{}", root_hex);
let res = self.request::<Bootstrap>(path, "bootstrap").await?;
let res = self.request::<Bootstrap>(&path, "bootstrap").await?;
Ok(res)
}
@ -55,7 +85,7 @@ impl ConsensusRpc for NimbusRpc {
period, count
);
let res = self.request::<UpdateResponse>(path, "updates").await?;
let res = self.request::<UpdateResponse>(&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::<FinalityUpdateResponse>(path, "finality_update")
.request::<FinalityUpdateResponse>(&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::<OptimisticUpdateResponse>(path, "optimistic_update")
.request::<OptimisticUpdateResponse>(&path, "optimistic_update")
.await?;
Ok(res.data)
@ -83,7 +113,7 @@ impl ConsensusRpc for NimbusRpc {
async fn get_block(&self, slot: u64) -> Result<BeaconBlock> {
let path = format!("/eth/v2/beacon/blocks/{}", slot);
let res = self.request::<BeaconBlockResponse>(path, "blocks").await?;
let res = self.request::<BeaconBlockResponse>(&path, "blocks").await?;
Ok(res.data.message)
}
@ -91,7 +121,7 @@ impl ConsensusRpc for NimbusRpc {
async fn chain_id(&self) -> Result<u64> {
let path = format!("/eth/v1/config/spec");
let res = self.request::<SpecResponse>(path, "blocks").await?;
let res = self.request::<SpecResponse>(&path, "blocks").await?;
Ok(res.data.chain_id)
}
@ -141,7 +171,7 @@ struct Spec {
}
impl NimbusRpc {
async fn request<T>(&self, path: String, error_type: &str) -> Result<T>
async fn request<T>(&self, path: &str, error_type: &str) -> Result<T, RpcError>
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::<js_sys::Function>()
.unwrap()
.call1(&JsValue::null(), &js_params)
.expect("Failed to make JSON-RPC request");
let result = match promise.dyn_into::<js_sys::Promise>() {
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)
}

View File

@ -32,7 +32,7 @@ pub struct ExecutionClient<R: ExecutionRpc> {
impl<R: ExecutionRpc> ExecutionClient<R> {
pub fn new(rpc: &str) -> Result<Self> {
let rpc: R = ExecutionRpc::new(rpc)?;
let rpc: R = ExecutionRpc::new()?;
Ok(ExecutionClient { rpc })
}

View File

@ -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<LumeProvider>,
rpc_handler: Arc<JsValue>,
}
impl Clone for HttpRpc {
fn clone(&self) -> Self {
Self::new(self.rpc_handler.clone()).unwrap()
Self::new().unwrap()
}
}
@ -64,18 +70,32 @@ impl From<LumeError> for ProviderError {
}
}
#[derive(Clone, Debug)]
pub struct LumeProvider {
pub rpc_handler: Arc<JsValue>,
impl From<JsValue> 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<JsValue>) -> Self {
LumeProvider { rpc_handler }
impl From<RpcError<String>> for LumeError {
fn from(error: RpcError<String>) -> 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(&params).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::<Function>()
.unwrap()
.call2(&js_method, &js_params)
.unwrap();
let result = match promise.dyn_into::<js_sys::Promise>() {
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<JsValue>) -> Result<Self> {
let mut client = LumeProvider::new(rpc_handler.clone());
fn new() -> Result<Self> {
let mut client = LumeProvider::new();
let provider = Provider::new(client);
Ok(HttpRpc {
provider,
rpc_handler,
})
Ok(HttpRpc { provider })
}
async fn get_proof(

View File

@ -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<Self> {
let path = PathBuf::from(rpc);
Ok(MockRpc { path })
}
async fn get_proof(
&self,
_address: &Address,
_slots: &[H256],
_block: u64,
) -> Result<EIP1186ProofResponse> {
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<AccessList> {
Err(eyre!("not implemented"))
}
async fn get_code(&self, _address: &Address, _block: u64) -> Result<Vec<u8>> {
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<H256> {
Err(eyre!("not implemented"))
}
async fn get_transaction_receipt(&self, _tx_hash: &H256) -> Result<Option<TransactionReceipt>> {
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<Option<Transaction>> {
let tx = read_to_string(self.path.join("transaction.json"))?;
Ok(serde_json::from_str(&tx)?)
}
async fn get_logs(&self, _filter: &Filter) -> Result<Vec<Log>> {
let logs = read_to_string(self.path.join("logs.json"))?;
Ok(serde_json::from_str(&logs)?)
}
async fn chain_id(&self) -> Result<u64> {
Err(eyre!("not implemented"))
}
async fn get_fee_history(
&self,
_block_count: u64,
_last_block: u64,
_reward_percentiles: &[f64],
) -> Result<FeeHistory> {
let fee_history = read_to_string(self.path.join("fee_history.json"))?;
Ok(serde_json::from_str(&fee_history)?)
}
}

View File

@ -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<Self>
fn new() -> Result<Self>
where
Self: Sized;

View File

@ -30,7 +30,4 @@ execution = { path = "../execution" }
config = { path = "../config" }
[dependencies.web-sys]
version = "0.3"
features = [
"console",
]
version = "0.3.61"

View File

@ -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<String>,
) -> Self {
pub fn new(network: String, checkpoint: Option<String>) -> 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,