Compare commits
9 Commits
Author | SHA1 | Date |
---|---|---|
Derrick Hammer | 7adc38ae1a | |
Derrick Hammer | 15fd1b8f88 | |
Derrick Hammer | 6747fe777c | |
Derrick Hammer | 086884b617 | |
Derrick Hammer | 2e116d3e2f | |
Derrick Hammer | 18742dcd1c | |
Derrick Hammer | 64eddcaa33 | |
Derrick Hammer | 602b6dfee5 | |
Derrick Hammer | 93b4d502ba |
File diff suppressed because it is too large
Load Diff
|
@ -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,3 +93,6 @@ harness = false
|
|||
[[bench]]
|
||||
name = "sync"
|
||||
harness = false
|
||||
|
||||
[replace]
|
||||
"ethers:1.0.2" = { git = "https://git.lumeweb.com/LumeWeb/ethers-rs.git", branch= "lume" }
|
||||
|
|
|
@ -23,3 +23,4 @@ futures = "0.3.23"
|
|||
client = { path = "../client" }
|
||||
config = { path = "../config" }
|
||||
common = { path = "../common" }
|
||||
wasm-bindgen = "0.2.84"
|
||||
|
|
|
@ -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 = "1.0.0"
|
||||
ethers = { version = "1.0.2", path = "../../helios-workspace/ethers-rs"}
|
||||
futures = "0.3.23"
|
||||
log = "0.4.17"
|
||||
thiserror = "1.0.37"
|
||||
|
@ -17,6 +17,7 @@ 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"] }
|
||||
|
|
|
@ -37,8 +37,6 @@ 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>,
|
||||
|
@ -60,16 +58,6 @@ 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");
|
||||
|
@ -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,8 +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, 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?;
|
||||
|
||||
|
@ -385,8 +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, 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(())
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use ethers::providers::HttpClientError;
|
||||
use ethers::providers::JsonRpcError;
|
||||
use ethers::providers::Provider;
|
||||
use ethers::types::H256;
|
||||
use thiserror::Error;
|
||||
|
||||
|
|
|
@ -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 = "1.0.0"
|
||||
ethers = { version = "1.0.2", path = "../../helios-workspace/ethers-rs"}
|
||||
figment = { version = "0.10.7", features = ["toml", "env"] }
|
||||
thiserror = "1.0.37"
|
||||
log = "0.4.17"
|
||||
|
@ -19,6 +19,9 @@ 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"] }
|
||||
|
|
|
@ -7,7 +7,6 @@ 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"
|
||||
|
|
|
@ -2,7 +2,7 @@ use figment::{
|
|||
providers::{Format, Serialized, Toml},
|
||||
Figment,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::{path::PathBuf, process::exit};
|
||||
|
||||
use crate::base::BaseConfig;
|
||||
|
@ -13,8 +13,7 @@ use crate::utils::{bytes_deserialize, bytes_opt_deserialize};
|
|||
|
||||
#[derive(Deserialize, Debug, Default)]
|
||||
pub struct Config {
|
||||
pub consensus_rpc: String,
|
||||
pub execution_rpc: String,
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub rpc_port: Option<u16>,
|
||||
#[serde(deserialize_with = "bytes_deserialize")]
|
||||
pub default_checkpoint: Vec<u8>,
|
||||
|
@ -30,6 +29,18 @@ 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 {
|
||||
|
@ -87,7 +98,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(),
|
||||
|
|
|
@ -40,7 +40,6 @@ 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,
|
||||
|
@ -78,7 +77,6 @@ pub fn goerli() -> BaseConfig {
|
|||
)
|
||||
.unwrap(),
|
||||
rpc_port: 8545,
|
||||
consensus_rpc: None,
|
||||
chain: ChainConfig {
|
||||
chain_id: 5,
|
||||
genesis_time: 1616508000,
|
||||
|
|
|
@ -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 = "1.0.0"
|
||||
ethers = { version = "1.0.2", path = "../../helios-workspace/ethers-rs"}
|
||||
bytes = "1.2.1"
|
||||
toml = "0.5.9"
|
||||
async-trait = "0.1.57"
|
||||
|
@ -23,6 +23,11 @@ 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"] }
|
||||
|
@ -30,4 +35,3 @@ tokio = { version = "1", features = ["full"] }
|
|||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
wasm-timer = "0.2.5"
|
||||
|
||||
|
|
|
@ -54,12 +54,8 @@ struct LightClientStore {
|
|||
}
|
||||
|
||||
impl<R: ConsensusRpc> ConsensusClient<R> {
|
||||
pub fn new(
|
||||
rpc: &str,
|
||||
checkpoint_block_root: &[u8],
|
||||
config: Arc<Config>,
|
||||
) -> Result<ConsensusClient<R>> {
|
||||
let rpc = R::new(rpc);
|
||||
pub fn new(checkpoint_block_root: &[u8], config: Arc<Config>) -> Result<ConsensusClient<R>> {
|
||||
let rpc = R::new();
|
||||
|
||||
Ok(ConsensusClient {
|
||||
rpc,
|
||||
|
@ -660,8 +656,8 @@ mod tests {
|
|||
async fn get_client(strict_checkpoint_age: bool) -> ConsensusClient<MockRpc> {
|
||||
let base_config = networks::goerli();
|
||||
let config = Config {
|
||||
consensus_rpc: String::new(),
|
||||
execution_rpc: String::new(),
|
||||
consensus_rpc: Arc::from(wasm_bindgen::JsValue::null()),
|
||||
execution_rpc: Arc::from(wasm_bindgen::JsValue::null()),
|
||||
chain: base_config.chain,
|
||||
forks: base_config.forks,
|
||||
strict_checkpoint_age,
|
||||
|
@ -672,7 +668,12 @@ mod tests {
|
|||
hex::decode("1e591af1e90f2db918b2a132991c7c2ee9a4ab26da496bd6e71e4f0bd65ea870")
|
||||
.unwrap();
|
||||
|
||||
let mut client = ConsensusClient::new("testdata/", &checkpoint, Arc::new(config)).unwrap();
|
||||
let mut client = ConsensusClient::new(
|
||||
Arc::from(wasm_bindgen::JsValue::null()),
|
||||
&checkpoint,
|
||||
Arc::new(config),
|
||||
)
|
||||
.unwrap();
|
||||
client.bootstrap().await.unwrap();
|
||||
client
|
||||
}
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
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")
|
||||
}
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
pub mod mock_rpc;
|
||||
pub mod nimbus_rpc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
|
@ -10,7 +9,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(path: &str) -> 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>;
|
||||
|
|
|
@ -1,110 +1,123 @@
|
|||
use std::cmp;
|
||||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use eyre::Result;
|
||||
use std::cmp;
|
||||
use js_sys::Promise;
|
||||
use serde::Deserialize;
|
||||
use wasm_bindgen::JsValue;
|
||||
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
|
||||
use super::ConsensusRpc;
|
||||
use crate::constants::MAX_REQUEST_LIGHT_CLIENT_UPDATES;
|
||||
use crate::types::*;
|
||||
use common::errors::RpcError;
|
||||
|
||||
use super::ConsensusRpc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NimbusRpc {
|
||||
rpc: String,
|
||||
pub struct RpcError {
|
||||
message: String,
|
||||
_type: String,
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
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)]
|
||||
impl ConsensusRpc for NimbusRpc {
|
||||
fn new(rpc: &str) -> Self {
|
||||
NimbusRpc {
|
||||
rpc: rpc.to_string(),
|
||||
}
|
||||
fn new() -> Self {
|
||||
NimbusRpc {}
|
||||
}
|
||||
|
||||
async fn get_bootstrap(&self, block_root: &'_ [u8]) -> Result<Bootstrap> {
|
||||
let root_hex = hex::encode(block_root);
|
||||
let req = format!(
|
||||
"{}/eth/v1/beacon/light_client/bootstrap/0x{}",
|
||||
self.rpc, root_hex
|
||||
);
|
||||
let path = format!("/eth/v1/beacon/light_client/bootstrap/0x{}", root_hex);
|
||||
|
||||
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))?;
|
||||
let res = self
|
||||
.request::<BootstrapResponse>(&path, "bootstrap")
|
||||
.await?;
|
||||
|
||||
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 req = format!(
|
||||
"{}/eth/v1/beacon/light_client/updates?start_period={}&count={}",
|
||||
self.rpc, period, count
|
||||
let path = format!(
|
||||
"/eth/v1/beacon/light_client/updates?start_period={}&count={}",
|
||||
period, count
|
||||
);
|
||||
|
||||
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))?;
|
||||
let res = self.request::<UpdateResponse>(&path, "updates").await?;
|
||||
|
||||
Ok(res.iter().map(|d| d.data.clone()).collect())
|
||||
}
|
||||
|
||||
async fn get_finality_update(&self) -> Result<FinalityUpdate> {
|
||||
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))?;
|
||||
let path = format!("/eth/v1/beacon/light_client/finality_update");
|
||||
|
||||
let res = self
|
||||
.request::<FinalityUpdateResponse>(&path, "finality_update")
|
||||
.await?;
|
||||
|
||||
Ok(res.data)
|
||||
}
|
||||
|
||||
async fn get_optimistic_update(&self) -> Result<OptimisticUpdate> {
|
||||
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))?;
|
||||
let path = format!("/eth/v1/beacon/light_client/optimistic_update");
|
||||
|
||||
let res = self
|
||||
.request::<OptimisticUpdateResponse>(&path, "optimistic_update")
|
||||
.await?;
|
||||
|
||||
Ok(res.data)
|
||||
}
|
||||
|
||||
async fn get_block(&self, slot: u64) -> Result<BeaconBlock> {
|
||||
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))?;
|
||||
let path = format!("/eth/v2/beacon/blocks/{}", slot);
|
||||
|
||||
let res = self.request::<BeaconBlockResponse>(&path, "blocks").await?;
|
||||
|
||||
Ok(res.data.message)
|
||||
}
|
||||
|
||||
async fn chain_id(&self) -> Result<u64> {
|
||||
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))?;
|
||||
let path = format!("/eth/v1/config/spec");
|
||||
|
||||
let res = self.request::<SpecResponse>(&path, "blocks").await?;
|
||||
|
||||
Ok(res.data.chain_id)
|
||||
}
|
||||
|
@ -152,3 +165,23 @@ 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = "1.0.0"
|
||||
ethers = { version = "1.0.2", path = "../../helios-workspace/ethers-rs"}
|
||||
bytes = "1.2.1"
|
||||
futures = "0.3.23"
|
||||
toml = "0.5.9"
|
||||
|
@ -22,6 +22,10 @@ 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"] }
|
||||
|
|
|
@ -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(rpc)?;
|
||||
pub fn new(_rpc: &str) -> Result<Self> {
|
||||
let rpc: R = ExecutionRpc::new()?;
|
||||
Ok(ExecutionClient { rpc })
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
use std::str::FromStr;
|
||||
use std::error::Error;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use ethers::prelude::{Address, Http};
|
||||
use ethers::providers::{HttpRateLimitRetryPolicy, Middleware, Provider, RetryClient};
|
||||
use ethers::prelude::Address;
|
||||
use ethers::providers::{
|
||||
HttpClientError, JsonRpcClient, Middleware, Provider, ProviderError, Response,
|
||||
};
|
||||
use ethers::types::transaction::eip2718::TypedTransaction;
|
||||
use ethers::types::transaction::eip2930::AccessList;
|
||||
use ethers::types::{
|
||||
|
@ -10,37 +13,105 @@ 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 {
|
||||
url: String,
|
||||
provider: Provider<RetryClient<Http>>,
|
||||
provider: Provider<LumeProvider>,
|
||||
}
|
||||
|
||||
impl Clone for HttpRpc {
|
||||
fn clone(&self) -> Self {
|
||||
Self::new(&self.url).unwrap()
|
||||
Self::new().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
|
||||
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
|
||||
impl ExecutionRpc for HttpRpc {
|
||||
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);
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct LumeProvider {}
|
||||
|
||||
impl LumeProvider {
|
||||
pub fn new() -> Self {
|
||||
LumeProvider {}
|
||||
}
|
||||
}
|
||||
#[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(¶ms).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();
|
||||
let provider = Provider::new(client);
|
||||
|
||||
Ok(HttpRpc {
|
||||
url: rpc.to_string(),
|
||||
provider,
|
||||
})
|
||||
Ok(HttpRpc { provider })
|
||||
}
|
||||
|
||||
async fn get_proof(
|
||||
|
|
|
@ -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)?)
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -13,20 +13,19 @@ 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 = "1.0.0"
|
||||
ethers = { version = "1.0.2", features = [ "abigen" ], path = "../../helios-workspace/ethers-rs" }
|
||||
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"
|
||||
features = [
|
||||
"console",
|
||||
]
|
||||
version = "0.3.61"
|
||||
|
|
|
@ -10,6 +10,7 @@ use wasm_bindgen::prelude::*;
|
|||
|
||||
use client::database::ConfigDB;
|
||||
use config::{networks, Config};
|
||||
use tracing_wasm;
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! log {
|
||||
|
@ -27,19 +28,11 @@ pub struct Client {
|
|||
#[wasm_bindgen]
|
||||
impl Client {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(
|
||||
execution_rpc: String,
|
||||
consensus_rpc: Option<String>,
|
||||
network: String,
|
||||
checkpoint: Option<String>,
|
||||
) -> Self {
|
||||
pub fn new(checkpoint: Option<String>) -> Self {
|
||||
console_error_panic_hook::set_once();
|
||||
tracing_wasm::set_as_global_default();
|
||||
|
||||
let base = match network.as_str() {
|
||||
"mainnet" => networks::mainnet(),
|
||||
"goerli" => networks::goerli(),
|
||||
_ => panic!("invalid network"),
|
||||
};
|
||||
let base = networks::mainnet();
|
||||
|
||||
let chain_id = base.chain.chain_id;
|
||||
|
||||
|
@ -51,11 +44,7 @@ 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,
|
||||
|
|
Loading…
Reference in New Issue