Compare commits

..

No commits in common. "lume" and "master" have entirely different histories.
lume ... master

23 changed files with 5418 additions and 253 deletions

5119
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -29,12 +29,12 @@ common = { path = "./common" }
consensus = { path = "./consensus" }
execution = { path = "./execution" }
serde = { version = "1.0.154", features = ["derive"] }
ethers = "1.0.2"
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
tokio = { version = "1", features = ["full"] }
eyre = "0.6.8"
dirs = "4.0.0"
ethers = { version = "1.0.2", features = [ "abigen" ] }
env_logger = "0.9.0"
log = "0.4.17"
tracing-test = "0.2.4"
@ -93,6 +93,3 @@ harness = false
[[bench]]
name = "sync"
harness = false
[replace]
"ethers:1.0.2" = { git = "https://git.lumeweb.com/LumeWeb/ethers-rs.git", branch= "lume" }

View File

@ -23,4 +23,3 @@ futures = "0.3.23"
client = { path = "../client" }
config = { path = "../config" }
common = { path = "../common" }
wasm-bindgen = "0.2.84"

View File

@ -8,7 +8,7 @@ eyre = "0.6.8"
serde = { version = "1.0.143", features = ["derive"] }
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
ethers = { version = "1.0.2", path = "../../helios-workspace/ethers-rs"}
ethers = "1.0.0"
futures = "0.3.23"
log = "0.4.17"
thiserror = "1.0.37"
@ -17,7 +17,6 @@ common = { path = "../common" }
consensus = { path = "../consensus" }
execution = { path = "../execution" }
config = { path = "../config" }
wasm-bindgen = "0.2.84"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
jsonrpsee = { version = "0.15.1", features = ["full"] }

View File

@ -37,6 +37,8 @@ use crate::rpc::Rpc;
#[derive(Default)]
pub struct ClientBuilder {
network: Option<Network>,
consensus_rpc: Option<String>,
execution_rpc: Option<String>,
checkpoint: Option<Vec<u8>>,
#[cfg(not(target_arch = "wasm32"))]
rpc_port: Option<u16>,
@ -58,6 +60,16 @@ impl ClientBuilder {
self
}
pub fn consensus_rpc(mut self, consensus_rpc: &str) -> Self {
self.consensus_rpc = Some(consensus_rpc.to_string());
self
}
pub fn execution_rpc(mut self, execution_rpc: &str) -> Self {
self.execution_rpc = Some(execution_rpc.to_string());
self
}
pub fn checkpoint(mut self, checkpoint: &str) -> Self {
let checkpoint = hex::decode(checkpoint.strip_prefix("0x").unwrap_or(checkpoint))
.expect("cannot parse checkpoint");
@ -108,6 +120,22 @@ 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 {
@ -161,6 +189,8 @@ impl ClientBuilder {
};
let config = Config {
consensus_rpc,
execution_rpc,
checkpoint,
default_checkpoint,
#[cfg(not(target_arch = "wasm32"))]
@ -314,7 +344,8 @@ 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(checkpoint.as_bytes(), config.clone())?;
let consensus =
ConsensusClient::new(&config.consensus_rpc, checkpoint.as_bytes(), config.clone())?;
self.node.write().await.consensus = consensus;
self.node.write().await.sync().await?;
@ -354,7 +385,8 @@ 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(checkpoint.as_bytes(), config.clone())?;
let consensus =
ConsensusClient::new(&config.consensus_rpc, checkpoint.as_bytes(), config.clone())?;
self.node.write().await.consensus = consensus;
self.node.write().await.sync().await?;
Ok(())

View File

@ -34,12 +34,15 @@ 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(checkpoint_hash, config.clone())
let consensus = ConsensusClient::new(consensus_rpc, checkpoint_hash, config.clone())
.map_err(NodeError::ConsensusClientCreationError)?;
let execution =
Arc::new(ExecutionClient::new("").map_err(NodeError::ExecutionClientCreationError)?);
let execution = Arc::new(
ExecutionClient::new(execution_rpc).map_err(NodeError::ExecutionClientCreationError)?,
);
let payloads = BTreeMap::new();
let finalized_payloads = BTreeMap::new();

View File

@ -1,6 +1,3 @@
use ethers::providers::HttpClientError;
use ethers::providers::JsonRpcError;
use ethers::providers::Provider;
use ethers::types::H256;
use thiserror::Error;

View File

@ -9,7 +9,7 @@ eyre = "0.6.8"
serde = { version = "1.0.143", features = ["derive"] }
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
ethers = { version = "1.0.2", path = "../../helios-workspace/ethers-rs"}
ethers = "1.0.0"
figment = { version = "0.10.7", features = ["toml", "env"] }
thiserror = "1.0.37"
log = "0.4.17"
@ -19,9 +19,6 @@ strum = "0.24.1"
futures = "0.3.25"
common = { path = "../common" }
wasm-bindgen = "0.2.84"
serde_json = "1.0.94"
js-sys = "0.3.61"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
tokio = { version = "1", features = ["full"] }

View File

@ -7,6 +7,7 @@ use crate::utils::bytes_serialize;
#[derive(Serialize, Default)]
pub struct BaseConfig {
pub rpc_port: u16,
pub consensus_rpc: Option<String>,
#[serde(
deserialize_with = "bytes_deserialize",
serialize_with = "bytes_serialize"

View File

@ -2,7 +2,7 @@ use figment::{
providers::{Format, Serialized, Toml},
Figment,
};
use serde::{Deserialize, Deserializer};
use serde::Deserialize;
use std::{path::PathBuf, process::exit};
use crate::base::BaseConfig;
@ -13,7 +13,8 @@ use crate::utils::{bytes_deserialize, bytes_opt_deserialize};
#[derive(Deserialize, Debug, Default)]
pub struct Config {
#[serde(skip_serializing, skip_deserializing)]
pub consensus_rpc: String,
pub execution_rpc: String,
pub rpc_port: Option<u16>,
#[serde(deserialize_with = "bytes_deserialize")]
pub default_checkpoint: Vec<u8>,
@ -29,18 +30,6 @@ pub struct Config {
pub strict_checkpoint_age: bool,
}
struct JsonString(String);
impl<'de> Deserialize<'de> for JsonString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let json_string = String::deserialize(deserializer)?;
Ok(JsonString(json_string))
}
}
impl Config {
pub fn from_file(config_path: &PathBuf, network: &str, cli_config: &CliConfig) -> Self {
let base_config = match network {
@ -98,6 +87,7 @@ 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

@ -40,6 +40,7 @@ pub fn mainnet() -> BaseConfig {
)
.unwrap(),
rpc_port: 8545,
consensus_rpc: Some("https://www.lightclientdata.org".to_string()),
chain: ChainConfig {
chain_id: 1,
genesis_time: 1606824023,
@ -77,6 +78,7 @@ pub fn goerli() -> BaseConfig {
)
.unwrap(),
rpc_port: 8545,
consensus_rpc: None,
chain: ChainConfig {
chain_id: 5,
genesis_time: 1616508000,

View File

@ -11,7 +11,7 @@ serde_json = "1.0.85"
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
milagro_bls = { git = "https://github.com/Snowfork/milagro_bls" }
ethers = { version = "1.0.2", path = "../../helios-workspace/ethers-rs"}
ethers = "1.0.0"
bytes = "1.2.1"
toml = "0.5.9"
async-trait = "0.1.57"
@ -23,11 +23,6 @@ superstruct = "0.7.0"
common = { path = "../common" }
config = { path = "../config" }
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" }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
openssl = { version = "0.10", features = ["vendored"] }
@ -35,3 +30,4 @@ tokio = { version = "1", features = ["full"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-timer = "0.2.5"

View File

@ -54,8 +54,12 @@ struct LightClientStore {
}
impl<R: ConsensusRpc> ConsensusClient<R> {
pub fn new(checkpoint_block_root: &[u8], config: Arc<Config>) -> Result<ConsensusClient<R>> {
let rpc = R::new();
pub fn new(
rpc: &str,
checkpoint_block_root: &[u8],
config: Arc<Config>,
) -> Result<ConsensusClient<R>> {
let rpc = R::new(rpc);
Ok(ConsensusClient {
rpc,
@ -656,8 +660,8 @@ mod tests {
async fn get_client(strict_checkpoint_age: bool) -> ConsensusClient<MockRpc> {
let base_config = networks::goerli();
let config = Config {
consensus_rpc: Arc::from(wasm_bindgen::JsValue::null()),
execution_rpc: Arc::from(wasm_bindgen::JsValue::null()),
consensus_rpc: String::new(),
execution_rpc: String::new(),
chain: base_config.chain,
forks: base_config.forks,
strict_checkpoint_age,
@ -668,12 +672,7 @@ mod tests {
hex::decode("1e591af1e90f2db918b2a132991c7c2ee9a4ab26da496bd6e71e4f0bd65ea870")
.unwrap();
let mut client = ConsensusClient::new(
Arc::from(wasm_bindgen::JsValue::null()),
&checkpoint,
Arc::new(config),
)
.unwrap();
let mut client = ConsensusClient::new("testdata/", &checkpoint, Arc::new(config)).unwrap();
client.bootstrap().await.unwrap();
client
}

View File

@ -0,0 +1,48 @@
use std::{fs::read_to_string, path::PathBuf};
use super::ConsensusRpc;
use crate::types::{BeaconBlock, Bootstrap, FinalityUpdate, OptimisticUpdate, Update};
use async_trait::async_trait;
use eyre::Result;
pub struct MockRpc {
testdata: PathBuf,
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl ConsensusRpc for MockRpc {
fn new(path: &str) -> Self {
MockRpc {
testdata: PathBuf::from(path),
}
}
async fn get_bootstrap(&self, _block_root: &'_ [u8]) -> Result<Bootstrap> {
let bootstrap = read_to_string(self.testdata.join("bootstrap.json"))?;
Ok(serde_json::from_str(&bootstrap)?)
}
async fn get_updates(&self, _period: u64, _count: u8) -> Result<Vec<Update>> {
let updates = read_to_string(self.testdata.join("updates.json"))?;
Ok(serde_json::from_str(&updates)?)
}
async fn get_finality_update(&self) -> Result<FinalityUpdate> {
let finality = read_to_string(self.testdata.join("finality.json"))?;
Ok(serde_json::from_str(&finality)?)
}
async fn get_optimistic_update(&self) -> Result<OptimisticUpdate> {
let optimistic = read_to_string(self.testdata.join("optimistic.json"))?;
Ok(serde_json::from_str(&optimistic)?)
}
async fn get_block(&self, _slot: u64) -> Result<BeaconBlock> {
let block = read_to_string(self.testdata.join("blocks.json"))?;
Ok(serde_json::from_str(&block)?)
}
async fn chain_id(&self) -> Result<u64> {
eyre::bail!("not implemented")
}
}

View File

@ -1,3 +1,4 @@
pub mod mock_rpc;
pub mod nimbus_rpc;
use async_trait::async_trait;
@ -9,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() -> Self;
fn new(path: &str) -> 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,123 +1,110 @@
use std::cmp;
use std::fmt::{Display, Formatter, Result as FmtResult};
use async_trait::async_trait;
use eyre::Result;
use js_sys::Promise;
use serde::Deserialize;
use wasm_bindgen::JsValue;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen_futures::JsFuture;
use crate::constants::MAX_REQUEST_LIGHT_CLIENT_UPDATES;
use crate::types::*;
use std::cmp;
use super::ConsensusRpc;
use crate::constants::MAX_REQUEST_LIGHT_CLIENT_UPDATES;
use crate::types::*;
use common::errors::RpcError;
#[derive(Debug)]
pub struct RpcError {
message: String,
_type: String,
pub struct NimbusRpc {
rpc: String,
}
impl RpcError {
pub fn new(message: String, _type: String) -> Self {
Self { message, _type }
}
}
impl Display for RpcError {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{} error {}", self._type, self.message)
}
}
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, String::from(""))
}
}
impl std::error::Error for RpcError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}
#[derive(Debug)]
pub struct NimbusRpc {}
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = self)]
fn consensus_rpc_handler(data: JsValue) -> Promise;
}
#[async_trait(?Send)]
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl ConsensusRpc for NimbusRpc {
fn new() -> Self {
NimbusRpc {}
fn new(rpc: &str) -> Self {
NimbusRpc {
rpc: rpc.to_string(),
}
}
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 req = format!(
"{}/eth/v1/beacon/light_client/bootstrap/0x{}",
self.rpc, root_hex
);
let res = self
.request::<BootstrapResponse>(&path, "bootstrap")
.await?;
let client = reqwest::Client::new();
let res = client
.get(req)
.send()
.await
.map_err(|e| RpcError::new("bootstrap", e))?
.json::<BootstrapResponse>()
.await
.map_err(|e| RpcError::new("bootstrap", e))?;
Ok(res.data)
}
async fn get_updates(&self, period: u64, count: u8) -> Result<Vec<Update>> {
let count = cmp::min(count, MAX_REQUEST_LIGHT_CLIENT_UPDATES);
let path = format!(
"/eth/v1/beacon/light_client/updates?start_period={}&count={}",
period, count
let req = format!(
"{}/eth/v1/beacon/light_client/updates?start_period={}&count={}",
self.rpc, period, count
);
let res = self.request::<UpdateResponse>(&path, "updates").await?;
let client = reqwest::Client::new();
let res = client
.get(req)
.send()
.await
.map_err(|e| RpcError::new("updates", e))?
.json::<UpdateResponse>()
.await
.map_err(|e| RpcError::new("updates", e))?;
Ok(res.iter().map(|d| d.data.clone()).collect())
}
async fn get_finality_update(&self) -> Result<FinalityUpdate> {
let path = format!("/eth/v1/beacon/light_client/finality_update");
let res = self
.request::<FinalityUpdateResponse>(&path, "finality_update")
.await?;
let req = format!("{}/eth/v1/beacon/light_client/finality_update", self.rpc);
let res = reqwest::get(req)
.await
.map_err(|e| RpcError::new("finality_update", e))?
.json::<FinalityUpdateResponse>()
.await
.map_err(|e| RpcError::new("finality_update", e))?;
Ok(res.data)
}
async fn get_optimistic_update(&self) -> Result<OptimisticUpdate> {
let path = format!("/eth/v1/beacon/light_client/optimistic_update");
let res = self
.request::<OptimisticUpdateResponse>(&path, "optimistic_update")
.await?;
let req = format!("{}/eth/v1/beacon/light_client/optimistic_update", self.rpc);
let res = reqwest::get(req)
.await
.map_err(|e| RpcError::new("optimistic_update", e))?
.json::<OptimisticUpdateResponse>()
.await
.map_err(|e| RpcError::new("optimistic_update", e))?;
Ok(res.data)
}
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 req = format!("{}/eth/v2/beacon/blocks/{}", self.rpc, slot);
let res = reqwest::get(req)
.await
.map_err(|e| RpcError::new("blocks", e))?
.json::<BeaconBlockResponse>()
.await
.map_err(|e| RpcError::new("blocks", e))?;
Ok(res.data.message)
}
async fn chain_id(&self) -> Result<u64> {
let path = format!("/eth/v1/config/spec");
let res = self.request::<SpecResponse>(&path, "blocks").await?;
let req = format!("{}/eth/v1/config/spec", self.rpc);
let res = reqwest::get(req)
.await
.map_err(|e| RpcError::new("spec", e))?
.json::<SpecResponse>()
.await
.map_err(|e| RpcError::new("spec", e))?;
Ok(res.data.chain_id)
}
@ -165,23 +152,3 @@ struct Spec {
#[serde(rename = "DEPOSIT_NETWORK_ID", deserialize_with = "u64_deserialize")]
chain_id: u64,
}
impl NimbusRpc {
async fn request<T>(&self, path: &str, error_type: &str) -> Result<T, RpcError>
where
for<'a> T: Deserialize<'a>,
{
let js_params = js_sys::Map::new();
js_params.set(&JsValue::from_str("method"), &JsValue::from_str("GET"));
js_params.set(&JsValue::from_str("path"), &JsValue::from_str(&path));
let js_future = JsFuture::from(consensus_rpc_handler(JsValue::from(js_params)));
let result = js_future.await?;
let json = result.as_string().unwrap();
let response: T = serde_json::from_str(&json)
.map_err(|e| RpcError::new(e.to_string(), String::from(error_type)))?;
Ok(response)
}
}

View File

@ -11,7 +11,7 @@ serde_json = "1.0.85"
hex = "0.4.3"
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "d09f55b4f8554491e3431e01af1c32347a8781cd" }
revm = { version = "2.3", default-features = false, features = ["std", "k256", "with-serde"] }
ethers = { version = "1.0.2", path = "../../helios-workspace/ethers-rs"}
ethers = "1.0.0"
bytes = "1.2.1"
futures = "0.3.23"
toml = "0.5.9"
@ -22,10 +22,6 @@ thiserror = "1.0.37"
common = { path = "../common" }
consensus = { path = "../consensus" }
wasm-bindgen = "0.2.84"
js-sys = "0.3.61"
wasm-bindgen-futures = "0.4.34"
web-sys = {version="0.3.61", features=["console"]}
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
openssl = { version = "0.10", features = ["vendored"] }

View File

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

View File

@ -1,11 +1,8 @@
use std::error::Error;
use std::fmt::{Debug, Display, Formatter};
use std::str::FromStr;
use async_trait::async_trait;
use ethers::prelude::Address;
use ethers::providers::{
HttpClientError, JsonRpcClient, Middleware, Provider, ProviderError, Response,
};
use ethers::prelude::{Address, Http};
use ethers::providers::{HttpRateLimitRetryPolicy, Middleware, Provider, RetryClient};
use ethers::types::transaction::eip2718::TypedTransaction;
use ethers::types::transaction::eip2930::AccessList;
use ethers::types::{
@ -13,105 +10,37 @@ use ethers::types::{
Filter, Log, Transaction, TransactionReceipt, H256, U256,
};
use eyre::Result;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json::to_value;
use wasm_bindgen::JsValue;
use crate::types::CallOpts;
use common::errors::RpcError;
use super::ExecutionRpc;
use js_sys::Promise;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen_futures::JsFuture;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = self)]
fn execution_rpc_handler(data: JsValue) -> Promise;
}
pub struct HttpRpc {
provider: Provider<LumeProvider>,
url: String,
provider: Provider<RetryClient<Http>>,
}
impl Clone for HttpRpc {
fn clone(&self) -> Self {
Self::new().unwrap()
Self::new(&self.url).unwrap()
}
}
#[derive(Clone, Debug)]
pub struct LumeProvider {}
impl LumeProvider {
pub fn new() -> Self {
LumeProvider {}
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
impl JsonRpcClient for LumeProvider {
type Error = HttpClientError;
async fn request<T, R>(&self, method: &str, params: T) -> Result<R, Self::Error>
where
T: Debug + Serialize + Send + Sync,
R: DeserializeOwned,
{
let request = js_sys::Map::new();
request.set(&JsValue::from_str("method"), &JsValue::from_str(method));
if let Some(params) = Option::from(params) {
let js_params = to_value(&params).unwrap();
let json_str = serde_json::to_string(&js_params).unwrap();
request.set(&JsValue::from_str("params"), &JsValue::from_str(&json_str));
} else {
request.set(&JsValue::from_str("params"), &JsValue::null());
}
let js_future = JsFuture::from(execution_rpc_handler(JsValue::from(request)));
let result = js_future.await?;
let json = result.as_string().expect("response is not a string");
let body = json.as_bytes();
let raw = match serde_json::from_slice(&body) {
Ok(Response::Success { result, .. }) => result.to_owned(),
Ok(Response::Error { error, .. }) => return Err(error.into()),
Ok(_) => {
panic!(
"{} {}",
"unexpected notification over HTTP transport",
String::from_utf8_lossy(&body).to_string()
);
}
Err(err) => {
panic!(
"{} {}",
err.to_string(),
String::from_utf8_lossy(&body).to_string()
);
}
};
let res = serde_json::from_str(raw.get()).map_err(|err| HttpClientError::SerdeJson {
err,
text: raw.to_string(),
})?;
Ok(res)
}
}
#[async_trait(?Send)]
impl ExecutionRpc for HttpRpc {
fn new() -> Result<Self> {
let client = LumeProvider::new();
fn new(rpc: &str) -> Result<Self> {
let http = Http::from_str(rpc)?;
let mut client = RetryClient::new(http, Box::new(HttpRateLimitRetryPolicy), 100, 50);
client.set_compute_units(300);
let provider = Provider::new(client);
Ok(HttpRpc { provider })
Ok(HttpRpc {
url: rpc.to_string(),
provider,
})
}
async fn get_proof(

View File

@ -0,0 +1,79 @@
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,10 +8,12 @@ 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() -> Result<Self>
fn new(rpc: &str) -> Result<Self>
where
Self: Sized;

View File

@ -13,19 +13,20 @@ wasm-bindgen = "0.2.84"
wasm-bindgen-futures = "0.4.33"
serde-wasm-bindgen = "0.4.5"
console_error_panic_hook = "0.1.7"
futures = "0.3"
serde = { version = "1.0.85", features = ["derive"] }
ethers = { version = "1.0.2", features = [ "abigen" ], path = "../../helios-workspace/ethers-rs" }
ethers = "1.0.0"
hex = "0.4.3"
serde = { version = "1.0.143", features = ["derive"] }
serde_json = "1.0.85"
client = { path = "../client" }
common = { path = "../common" }
consensus = { path = "../consensus" }
execution = { path = "../execution" }
config = { path = "../config" }
tracing-wasm = "0.2.1"
[dependencies.web-sys]
version = "0.3.61"
version = "0.3"
features = [
"console",
]

View File

@ -10,7 +10,6 @@ use wasm_bindgen::prelude::*;
use client::database::ConfigDB;
use config::{networks, Config};
use tracing_wasm;
#[allow(unused_macros)]
macro_rules! log {
@ -28,11 +27,19 @@ pub struct Client {
#[wasm_bindgen]
impl Client {
#[wasm_bindgen(constructor)]
pub fn new(checkpoint: Option<String>) -> Self {
pub fn new(
execution_rpc: String,
consensus_rpc: Option<String>,
network: String,
checkpoint: Option<String>,
) -> Self {
console_error_panic_hook::set_once();
tracing_wasm::set_as_global_default();
let base = networks::mainnet();
let base = match network.as_str() {
"mainnet" => networks::mainnet(),
"goerli" => networks::goerli(),
_ => panic!("invalid network"),
};
let chain_id = base.chain.chain_id;
@ -44,7 +51,11 @@ impl Client {
.unwrap_or(base.default_checkpoint),
);
let consensus_rpc = consensus_rpc.unwrap_or(base.consensus_rpc.unwrap());
let config = Config {
execution_rpc,
consensus_rpc,
checkpoint,
chain: base.chain,