:caution: rfc
This commit is contained in:
parent
4f99cfef95
commit
38091aaa00
File diff suppressed because it is too large
Load Diff
|
@ -21,9 +21,6 @@ common = { path = "./common" }
|
|||
consensus = { path = "./consensus" }
|
||||
execution = { path = "./execution" }
|
||||
|
||||
[patch.crates-io]
|
||||
ethers = { git = "https://github.com/ncitron/ethers-rs", branch = "fix-retry" }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
eyre = "0.6.8"
|
||||
|
|
|
@ -17,7 +17,6 @@ eyre = "0.6.8"
|
|||
dirs = "4.0.0"
|
||||
env_logger = "0.9.0"
|
||||
log = "0.4.17"
|
||||
ctrlc = "3.2.3"
|
||||
futures = "0.3.23"
|
||||
|
||||
client = { path = "../client" }
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
use std::{fs, path::PathBuf, str::FromStr};
|
||||
|
||||
use clap::Parser;
|
||||
use common::utils::hex_str_to_bytes;
|
||||
use dirs::home_dir;
|
||||
|
||||
use config::{CliConfig, Config};
|
||||
|
||||
#[derive(Parser)]
|
||||
pub struct Cli {
|
||||
#[clap(short, long, default_value = "mainnet")]
|
||||
network: String,
|
||||
#[clap(short = 'p', long, env)]
|
||||
rpc_port: Option<u16>,
|
||||
#[clap(short = 'w', long, env)]
|
||||
checkpoint: Option<String>,
|
||||
#[clap(short, long, env)]
|
||||
execution_rpc: Option<String>,
|
||||
#[clap(short, long, env)]
|
||||
consensus_rpc: Option<String>,
|
||||
#[clap(short, long, env)]
|
||||
data_dir: Option<String>,
|
||||
#[clap(short = 'f', long, env)]
|
||||
fallback: Option<String>,
|
||||
#[clap(short = 'l', long, env)]
|
||||
load_external_fallback: bool,
|
||||
#[clap(short = 's', long, env)]
|
||||
with_ws: bool,
|
||||
#[clap(short = 'h', long, env)]
|
||||
with_http: bool,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
pub fn to_config() -> Config {
|
||||
let cli = Cli::parse();
|
||||
let config_path = home_dir().unwrap().join(".helios/helios.toml");
|
||||
let cli_config = cli.as_cli_config();
|
||||
Config::from_file(&config_path, &cli.network, &cli_config)
|
||||
}
|
||||
|
||||
fn as_cli_config(&self) -> CliConfig {
|
||||
let checkpoint = match &self.checkpoint {
|
||||
Some(checkpoint) => Some(hex_str_to_bytes(checkpoint).expect("invalid checkpoint")),
|
||||
None => self.get_cached_checkpoint(),
|
||||
};
|
||||
|
||||
CliConfig {
|
||||
checkpoint,
|
||||
execution_rpc: self.execution_rpc.clone(),
|
||||
consensus_rpc: self.consensus_rpc.clone(),
|
||||
data_dir: self.get_data_dir(),
|
||||
rpc_port: self.rpc_port,
|
||||
fallback: self.fallback.clone(),
|
||||
load_external_fallback: self.load_external_fallback,
|
||||
with_ws: self.with_ws,
|
||||
with_http: self.with_http,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cached_checkpoint(&self) -> Option<Vec<u8>> {
|
||||
let data_dir = self.get_data_dir();
|
||||
let checkpoint_file = data_dir.join("checkpoint");
|
||||
|
||||
if checkpoint_file.exists() {
|
||||
let checkpoint_res = fs::read(checkpoint_file);
|
||||
match checkpoint_res {
|
||||
Ok(checkpoint) => Some(checkpoint),
|
||||
Err(_) => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_data_dir(&self) -> PathBuf {
|
||||
if let Some(dir) = &self.data_dir {
|
||||
PathBuf::from_str(dir).expect("cannot find data dir")
|
||||
} else {
|
||||
home_dir()
|
||||
.unwrap()
|
||||
.join(format!(".helios/data/{}", self.network))
|
||||
}
|
||||
}
|
||||
}
|
133
cli/src/main.rs
133
cli/src/main.rs
|
@ -1,142 +1,19 @@
|
|||
use std::{
|
||||
fs,
|
||||
path::PathBuf,
|
||||
process::exit,
|
||||
str::FromStr,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use clap::Parser;
|
||||
use common::utils::hex_str_to_bytes;
|
||||
use dirs::home_dir;
|
||||
use env_logger::Env;
|
||||
use eyre::Result;
|
||||
|
||||
use client::{database::FileDB, Client, ClientBuilder};
|
||||
use config::{CliConfig, Config};
|
||||
use futures::executor::block_on;
|
||||
use log::info;
|
||||
use client::{Client, ClientBuilder};
|
||||
|
||||
mod cli;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
env_logger::Builder::from_env(Env::default().default_filter_or("info")).init();
|
||||
|
||||
let config = get_config();
|
||||
let config = cli::Cli::to_config();
|
||||
let mut client = ClientBuilder::new().config(config).build()?;
|
||||
|
||||
client.start().await?;
|
||||
|
||||
register_shutdown_handler(client);
|
||||
Client::register_shutdown_handler(client);
|
||||
std::future::pending().await
|
||||
}
|
||||
|
||||
fn register_shutdown_handler(client: Client<FileDB>) {
|
||||
let client = Arc::new(client);
|
||||
let shutdown_counter = Arc::new(Mutex::new(0));
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
let mut counter = shutdown_counter.lock().unwrap();
|
||||
*counter += 1;
|
||||
|
||||
let counter_value = *counter;
|
||||
|
||||
if counter_value == 3 {
|
||||
info!("forced shutdown");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
info!(
|
||||
"shutting down... press ctrl-c {} more times to force quit",
|
||||
3 - counter_value
|
||||
);
|
||||
|
||||
if counter_value == 1 {
|
||||
let client = client.clone();
|
||||
std::thread::spawn(move || {
|
||||
block_on(client.shutdown());
|
||||
exit(0);
|
||||
});
|
||||
}
|
||||
})
|
||||
.expect("could not register shutdown handler");
|
||||
}
|
||||
|
||||
fn get_config() -> Config {
|
||||
let cli = Cli::parse();
|
||||
|
||||
let config_path = home_dir().unwrap().join(".helios/helios.toml");
|
||||
|
||||
let cli_config = cli.as_cli_config();
|
||||
|
||||
Config::from_file(&config_path, &cli.network, &cli_config)
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Cli {
|
||||
#[clap(short, long, default_value = "mainnet")]
|
||||
network: String,
|
||||
#[clap(short = 'p', long, env)]
|
||||
rpc_port: Option<u16>,
|
||||
#[clap(short = 'w', long, env)]
|
||||
checkpoint: Option<String>,
|
||||
#[clap(short, long, env)]
|
||||
execution_rpc: Option<String>,
|
||||
#[clap(short, long, env)]
|
||||
consensus_rpc: Option<String>,
|
||||
#[clap(short, long, env)]
|
||||
data_dir: Option<String>,
|
||||
#[clap(short = 'f', long, env)]
|
||||
fallback: Option<String>,
|
||||
#[clap(short = 'l', long, env)]
|
||||
load_external_fallback: bool,
|
||||
#[clap(short = 's', long, env)]
|
||||
with_ws: bool,
|
||||
#[clap(short = 'h', long, env)]
|
||||
with_http: bool,
|
||||
}
|
||||
|
||||
impl Cli {
|
||||
fn as_cli_config(&self) -> CliConfig {
|
||||
let checkpoint = match &self.checkpoint {
|
||||
Some(checkpoint) => Some(hex_str_to_bytes(checkpoint).expect("invalid checkpoint")),
|
||||
None => self.get_cached_checkpoint(),
|
||||
};
|
||||
|
||||
CliConfig {
|
||||
checkpoint,
|
||||
execution_rpc: self.execution_rpc.clone(),
|
||||
consensus_rpc: self.consensus_rpc.clone(),
|
||||
data_dir: self.get_data_dir(),
|
||||
rpc_port: self.rpc_port,
|
||||
fallback: self.fallback.clone(),
|
||||
load_external_fallback: self.load_external_fallback,
|
||||
with_ws: self.with_ws,
|
||||
with_http: self.with_http,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_cached_checkpoint(&self) -> Option<Vec<u8>> {
|
||||
let data_dir = self.get_data_dir();
|
||||
let checkpoint_file = data_dir.join("checkpoint");
|
||||
|
||||
if checkpoint_file.exists() {
|
||||
let checkpoint_res = fs::read(checkpoint_file);
|
||||
match checkpoint_res {
|
||||
Ok(checkpoint) => Some(checkpoint),
|
||||
Err(_) => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_data_dir(&self) -> PathBuf {
|
||||
if let Some(dir) = &self.data_dir {
|
||||
PathBuf::from_str(dir).expect("cannot find data dir")
|
||||
} else {
|
||||
home_dir()
|
||||
.unwrap()
|
||||
.join(format!(".helios/data/{}", self.network))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,12 @@ 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 = "cb08f18ca919cc1b685b861d0fa9e2daabe89737" }
|
||||
ethers = "1.0.0"
|
||||
ethers = { version = "1.0.2", features = [ "ws", "default" ] }
|
||||
jsonrpsee = { version = "0.15.1", features = ["full"] }
|
||||
futures = "0.3.23"
|
||||
log = "0.4.17"
|
||||
thiserror = "1.0.37"
|
||||
ctrlc = "3.2.3"
|
||||
|
||||
common = { path = "../common" }
|
||||
consensus = { path = "../consensus" }
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use config::{Network, Config};
|
||||
use execution::rpc::ExecutionRpc;
|
||||
use config::{Config, Network};
|
||||
use execution::rpc::WsRpc;
|
||||
|
||||
use crate::{database::FileDB, Client};
|
||||
|
||||
|
@ -100,7 +100,7 @@ impl ClientBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> eyre::Result<Client<FileDB, _>> {
|
||||
fn build_base_config(&self) -> eyre::Result<Config> {
|
||||
let base_config = if let Some(network) = self.network {
|
||||
network.to_base_config()
|
||||
} else {
|
||||
|
@ -111,7 +111,7 @@ impl ClientBuilder {
|
|||
config.to_base_config()
|
||||
};
|
||||
|
||||
let consensus_rpc = self.consensus_rpc.unwrap_or_else(|| {
|
||||
let consensus_rpc = self.consensus_rpc.clone().unwrap_or_else(|| {
|
||||
self.config
|
||||
.as_ref()
|
||||
.expect("missing consensus rpc")
|
||||
|
@ -119,7 +119,7 @@ impl ClientBuilder {
|
|||
.clone()
|
||||
});
|
||||
|
||||
let execution_rpc = self.execution_rpc.unwrap_or_else(|| {
|
||||
let execution_rpc = self.execution_rpc.clone().unwrap_or_else(|| {
|
||||
self.config
|
||||
.as_ref()
|
||||
.expect("missing execution rpc")
|
||||
|
@ -127,8 +127,8 @@ impl ClientBuilder {
|
|||
.clone()
|
||||
});
|
||||
|
||||
let checkpoint = if let Some(checkpoint) = self.checkpoint {
|
||||
checkpoint
|
||||
let checkpoint = if let Some(checkpoint) = &self.checkpoint {
|
||||
checkpoint.clone()
|
||||
} else if let Some(config) = &self.config {
|
||||
config.checkpoint.clone()
|
||||
} else {
|
||||
|
@ -144,7 +144,7 @@ impl ClientBuilder {
|
|||
};
|
||||
|
||||
let data_dir = if self.data_dir.is_some() {
|
||||
self.data_dir
|
||||
self.data_dir.clone()
|
||||
} else if let Some(config) = &self.config {
|
||||
config.data_dir.clone()
|
||||
} else {
|
||||
|
@ -152,7 +152,7 @@ impl ClientBuilder {
|
|||
};
|
||||
|
||||
let fallback = if self.fallback.is_some() {
|
||||
self.fallback
|
||||
self.fallback.clone()
|
||||
} else if let Some(config) = &self.config {
|
||||
config.fallback.clone()
|
||||
} else {
|
||||
|
@ -177,7 +177,7 @@ impl ClientBuilder {
|
|||
self.with_http
|
||||
};
|
||||
|
||||
let config = Config {
|
||||
Ok(Config {
|
||||
consensus_rpc,
|
||||
execution_rpc,
|
||||
checkpoint,
|
||||
|
@ -190,8 +190,13 @@ impl ClientBuilder {
|
|||
load_external_fallback,
|
||||
with_ws,
|
||||
with_http,
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientBuilder {
|
||||
pub fn build(self) -> eyre::Result<Client<FileDB, WsRpc>> {
|
||||
let config = self.build_base_config()?;
|
||||
Client::new(config)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use std::sync::Arc;
|
||||
use std::process::exit;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use config::networks::Network;
|
||||
use ethers::prelude::{Address, U256};
|
||||
|
@ -7,8 +8,9 @@ use ethers::types::{Filter, Log, Transaction, TransactionReceipt, H256};
|
|||
use common::types::BlockTag;
|
||||
use config::{CheckpointFallback, Config};
|
||||
use consensus::{types::Header, ConsensusClient};
|
||||
use execution::rpc::ExecutionRpc;
|
||||
use execution::rpc::{ExecutionRpc, WsRpc};
|
||||
use execution::types::{CallOpts, ExecutionBlock};
|
||||
use futures::executor::block_on;
|
||||
use log::{info, warn};
|
||||
use tokio::spawn;
|
||||
use tokio::sync::RwLock;
|
||||
|
@ -28,9 +30,10 @@ pub struct Client<DB: Database, R: ExecutionRpc> {
|
|||
http: bool,
|
||||
}
|
||||
|
||||
impl<R> Client<FileDB, R> where R: ExecutionRpc {
|
||||
impl Client<FileDB, WsRpc> {
|
||||
pub fn new(config: Config) -> eyre::Result<Self> {
|
||||
let config = Arc::new(config);
|
||||
|
||||
let node = Node::new(config.clone())?;
|
||||
let node = Arc::new(RwLock::new(node));
|
||||
|
||||
|
@ -53,11 +56,48 @@ impl<R> Client<FileDB, R> where R: ExecutionRpc {
|
|||
}
|
||||
}
|
||||
|
||||
impl Client<FileDB, WsRpc> {
|
||||
pub fn register_shutdown_handler(client: Client<FileDB, WsRpc>) {
|
||||
let client = Arc::new(client);
|
||||
let shutdown_counter = Arc::new(Mutex::new(0));
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
let mut counter = shutdown_counter.lock().unwrap();
|
||||
*counter += 1;
|
||||
|
||||
let counter_value = *counter;
|
||||
|
||||
if counter_value == 3 {
|
||||
info!("forced shutdown");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
info!(
|
||||
"shutting down... press ctrl-c {} more times to force quit",
|
||||
3 - counter_value
|
||||
);
|
||||
|
||||
if counter_value == 1 {
|
||||
let client = client.clone();
|
||||
std::thread::spawn(move || {
|
||||
block_on(client.shutdown());
|
||||
exit(0);
|
||||
});
|
||||
}
|
||||
})
|
||||
.expect("could not register shutdown handler");
|
||||
}
|
||||
}
|
||||
|
||||
impl<DB: Database, R: ExecutionRpc> Client<DB, R> {
|
||||
pub async fn start(&mut self) -> eyre::Result<()> {
|
||||
if let Some(rpc) = &mut self.rpc {
|
||||
if self.ws { rpc.start_ws().await?; }
|
||||
if self.http { rpc.start_http().await?; }
|
||||
if self.ws {
|
||||
rpc.start_ws().await?;
|
||||
}
|
||||
if self.http {
|
||||
rpc.start_http().await?;
|
||||
}
|
||||
}
|
||||
|
||||
if self.node.write().await.sync().await.is_err() {
|
||||
|
@ -222,7 +262,10 @@ impl<DB: Database, R: ExecutionRpc> Client<DB, R> {
|
|||
.await
|
||||
}
|
||||
|
||||
pub async fn get_transaction_by_hash(&self, tx_hash: &H256) -> eyre::Result<Option<Transaction>> {
|
||||
pub async fn get_transaction_by_hash(
|
||||
&self,
|
||||
tx_hash: &H256,
|
||||
) -> eyre::Result<Option<Transaction>> {
|
||||
self.node
|
||||
.read()
|
||||
.await
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/// Re-export builder logic
|
||||
mod builder;
|
||||
pub use crate::builder::*;
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::time::Duration;
|
|||
|
||||
use ethers::prelude::{Address, U256};
|
||||
use ethers::types::{Filter, Log, Transaction, TransactionReceipt, H256};
|
||||
use execution::rpc::ExecutionRpc;
|
||||
use execution::rpc::{ExecutionRpc, WsRpc};
|
||||
use eyre::{eyre, Result};
|
||||
|
||||
use common::errors::BlockNotFoundError;
|
||||
|
@ -20,7 +20,7 @@ use execution::ExecutionClient;
|
|||
|
||||
use crate::errors::NodeError;
|
||||
|
||||
pub struct Node<R> where R: ExecutionRpc, {
|
||||
pub struct Node<R: ExecutionRpc> {
|
||||
pub consensus: ConsensusClient<NimbusRpc>,
|
||||
pub execution: Arc<ExecutionClient<R>>,
|
||||
pub config: Arc<Config>,
|
||||
|
@ -29,7 +29,7 @@ pub struct Node<R> where R: ExecutionRpc, {
|
|||
pub history_size: usize,
|
||||
}
|
||||
|
||||
impl<R> Node<R> where R: ExecutionRpc {
|
||||
impl Node<WsRpc> {
|
||||
pub fn new(config: Arc<Config>) -> Result<Self, NodeError> {
|
||||
let consensus_rpc = &config.consensus_rpc;
|
||||
let checkpoint_hash = &config.checkpoint;
|
||||
|
@ -37,9 +37,18 @@ impl<R> Node<R> where R: ExecutionRpc {
|
|||
|
||||
let consensus = ConsensusClient::new(consensus_rpc, checkpoint_hash, config.clone())
|
||||
.map_err(NodeError::ConsensusClientCreationError)?;
|
||||
let execution = Arc::new(
|
||||
ExecutionClient::new(execution_rpc).map_err(NodeError::ExecutionClientCreationError)?,
|
||||
);
|
||||
|
||||
let execution = if config.with_ws {
|
||||
Arc::new(
|
||||
ExecutionClient::new_with_ws(execution_rpc)
|
||||
.map_err(NodeError::ExecutionClientCreationError)?,
|
||||
)
|
||||
} else {
|
||||
Arc::new(
|
||||
ExecutionClient::new(execution_rpc)
|
||||
.map_err(NodeError::ExecutionClientCreationError)?,
|
||||
)
|
||||
};
|
||||
|
||||
let payloads = BTreeMap::new();
|
||||
let finalized_payloads = BTreeMap::new();
|
||||
|
@ -53,7 +62,12 @@ impl<R> Node<R> where R: ExecutionRpc {
|
|||
history_size: 64,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> Node<R>
|
||||
where
|
||||
R: ExecutionRpc,
|
||||
{
|
||||
pub async fn sync(&mut self) -> Result<(), NodeError> {
|
||||
self.consensus
|
||||
.sync()
|
||||
|
|
|
@ -16,9 +16,12 @@ use common::{
|
|||
types::BlockTag,
|
||||
utils::{hex_str_to_bytes, u64_to_hex_string},
|
||||
};
|
||||
use execution::{types::{CallOpts, ExecutionBlock}, rpc::ExecutionRpc};
|
||||
use execution::{
|
||||
rpc::ExecutionRpc,
|
||||
types::{CallOpts, ExecutionBlock},
|
||||
};
|
||||
|
||||
pub struct Rpc<R> where R: ExecutionRpc {
|
||||
pub struct Rpc<R: ExecutionRpc> {
|
||||
node: Arc<RwLock<Node<R>>>,
|
||||
http_handle: Option<HttpServerHandle>,
|
||||
ws_handle: Option<WsServerHandle>,
|
||||
|
@ -27,7 +30,7 @@ pub struct Rpc<R> where R: ExecutionRpc {
|
|||
port: u16,
|
||||
}
|
||||
|
||||
impl<R> Rpc<R> where R: ExecutionRpc {
|
||||
impl<R: ExecutionRpc> Rpc<R> {
|
||||
pub fn new(node: Arc<RwLock<Node<R>>>, with_http: bool, with_ws: bool, port: u16) -> Self {
|
||||
Rpc {
|
||||
node,
|
||||
|
@ -112,13 +115,13 @@ trait NetRpc {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct RpcInner<R> where R: ExecutionRpc {
|
||||
struct RpcInner<R: ExecutionRpc> {
|
||||
node: Arc<RwLock<Node<R>>>,
|
||||
http_port: u16,
|
||||
ws_port: u16,
|
||||
}
|
||||
|
||||
impl<R> From<&Rpc<R>> for RpcInner<R> where R: ExecutionRpc {
|
||||
impl<R: ExecutionRpc> From<&Rpc<R>> for RpcInner<R> {
|
||||
fn from(rpc: &Rpc<R>) -> Self {
|
||||
RpcInner {
|
||||
node: Arc::clone(&rpc.node),
|
||||
|
@ -128,7 +131,7 @@ impl<R> From<&Rpc<R>> for RpcInner<R> where R: ExecutionRpc {
|
|||
}
|
||||
}
|
||||
|
||||
impl<R> RpcInner<R> where R: ExecutionRpc {
|
||||
impl<R: ExecutionRpc> RpcInner<R> {
|
||||
pub async fn start_http(&self) -> Result<(HttpServerHandle, SocketAddr)> {
|
||||
let addr = format!("127.0.0.1:{}", self.http_port);
|
||||
let server = HttpServerBuilder::default().build(addr).await?;
|
||||
|
@ -167,7 +170,7 @@ impl<R> RpcInner<R> where R: ExecutionRpc {
|
|||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<R> EthRpcServer for RpcInner<R> where R: ExecutionRpc {
|
||||
impl<R: ExecutionRpc> EthRpcServer for RpcInner<R> {
|
||||
async fn get_balance(&self, address: &str, block: BlockTag) -> Result<String, Error> {
|
||||
let address = convert_err(Address::from_str(address))?;
|
||||
let node = self.node.read().await;
|
||||
|
@ -181,7 +184,7 @@ impl<R> EthRpcServer for RpcInner<R> where R: ExecutionRpc {
|
|||
let node = self.node.read().await;
|
||||
let nonce = convert_err(node.get_nonce(&address, block).await)?;
|
||||
|
||||
Ok(format!("0x{:x}", nonce))
|
||||
Ok(format!("0x{nonce:x}"))
|
||||
}
|
||||
|
||||
async fn get_code(&self, address: &str, block: BlockTag) -> Result<String, Error> {
|
||||
|
@ -288,7 +291,7 @@ impl<R> EthRpcServer for RpcInner<R> where R: ExecutionRpc {
|
|||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<R> NetRpcServer for RpcInner<R> where R: ExecutionRpc {
|
||||
impl<R: ExecutionRpc> NetRpcServer for RpcInner<R> {
|
||||
async fn version(&self) -> Result<String, Error> {
|
||||
let node = self.node.read().await;
|
||||
Ok(node.chain_id().to_string())
|
||||
|
|
|
@ -8,5 +8,5 @@ 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 = "cb08f18ca919cc1b685b861d0fa9e2daabe89737" }
|
||||
ethers = "1.0.0"
|
||||
ethers = { version = "1.0.2", features = [ "ws", "default" ] }
|
||||
thiserror = "1.0.37"
|
||||
|
|
|
@ -20,7 +20,7 @@ impl Display for BlockTag {
|
|||
Self::Number(num) => num.to_string(),
|
||||
};
|
||||
|
||||
write!(f, "{}", formatted)
|
||||
write!(f, "{formatted}")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ pub fn format_hex(num: &U256) -> String {
|
|||
.unwrap()
|
||||
.trim_start_matches('0')
|
||||
.to_string();
|
||||
format!("0x{}", stripped)
|
||||
format!("0x{stripped}")
|
||||
}
|
||||
|
||||
pub fn hex_str_to_bytes(s: &str) -> Result<Vec<u8>> {
|
||||
|
@ -35,5 +35,5 @@ pub fn address_to_hex_string(address: &Address) -> String {
|
|||
}
|
||||
|
||||
pub fn u64_to_hex_string(val: u64) -> String {
|
||||
format!("0x{:x}", val)
|
||||
format!("0x{val:x}")
|
||||
}
|
||||
|
|
|
@ -10,7 +10,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 = "cb08f18ca919cc1b685b861d0fa9e2daabe89737" }
|
||||
ethers = "1.0.0"
|
||||
ethers = { version = "1.0.2", features = [ "ws", "default" ] }
|
||||
figment = { version = "0.10.7", features = ["toml", "env"] }
|
||||
thiserror = "1.0.37"
|
||||
log = "0.4.17"
|
||||
|
|
|
@ -101,7 +101,7 @@ impl CheckpointFallback {
|
|||
let service_list = list
|
||||
.get(network.to_string().to_lowercase())
|
||||
.ok_or_else(|| {
|
||||
eyre::eyre!(format!("missing {} fallback checkpoint services", network))
|
||||
eyre::eyre!(format!("missing {network} fallback checkpoint services"))
|
||||
})?;
|
||||
let parsed: Vec<CheckpointFallbackService> =
|
||||
serde_yaml::from_value(service_list.clone())?;
|
||||
|
@ -202,7 +202,7 @@ impl CheckpointFallback {
|
|||
/// assert_eq!("https://sync-mainnet.beaconcha.in/checkpointz/v1/beacon/slots", url);
|
||||
/// ```
|
||||
pub fn construct_url(endpoint: &str) -> String {
|
||||
format!("{}/checkpointz/v1/beacon/slots", endpoint)
|
||||
format!("{endpoint}/checkpointz/v1/beacon/slots")
|
||||
}
|
||||
|
||||
/// Returns a list of all checkpoint fallback endpoints.
|
||||
|
|
|
@ -57,20 +57,14 @@ impl Config {
|
|||
figment::error::Kind::MissingField(field) => {
|
||||
let field = field.replace('_', "-");
|
||||
|
||||
println!(
|
||||
"\x1b[91merror\x1b[0m: missing configuration field: {}",
|
||||
field
|
||||
);
|
||||
println!("\x1b[91merror\x1b[0m: missing configuration field: {field}");
|
||||
|
||||
println!(
|
||||
"\n\ttry supplying the propoper command line argument: --{}",
|
||||
field
|
||||
);
|
||||
println!("\n\ttry supplying the propoper command line argument: --{field}");
|
||||
|
||||
println!("\talternatively, you can add the field to your helios.toml file or as an environment variable");
|
||||
println!("\nfor more information, check the github README");
|
||||
}
|
||||
_ => println!("cannot parse configuration: {}", err),
|
||||
_ => println!("cannot parse configuration: {err}"),
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
|
|
@ -50,9 +50,9 @@ async fn test_get_all_fallback_endpoints() {
|
|||
.await
|
||||
.unwrap();
|
||||
let urls = cf.get_all_fallback_endpoints(&networks::Network::MAINNET);
|
||||
assert!(urls.len() > 0);
|
||||
assert!(!urls.is_empty());
|
||||
let urls = cf.get_all_fallback_endpoints(&networks::Network::GOERLI);
|
||||
assert!(urls.len() > 0);
|
||||
assert!(!urls.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
@ -62,7 +62,7 @@ async fn test_get_healthy_fallback_endpoints() {
|
|||
.await
|
||||
.unwrap();
|
||||
let urls = cf.get_healthy_fallback_endpoints(&networks::Network::MAINNET);
|
||||
assert!(urls.len() > 0);
|
||||
assert!(!urls.is_empty());
|
||||
let urls = cf.get_healthy_fallback_endpoints(&networks::Network::GOERLI);
|
||||
assert!(urls.len() > 0);
|
||||
assert!(!urls.is_empty());
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ serde_json = "1.0.85"
|
|||
hex = "0.4.3"
|
||||
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "cb08f18ca919cc1b685b861d0fa9e2daabe89737" }
|
||||
blst = "0.3.10"
|
||||
ethers = "1.0.0"
|
||||
ethers = { version = "1.0.2", features = [ "ws", "default" ] }
|
||||
bytes = "1.2.1"
|
||||
toml = "0.5.9"
|
||||
async-trait = "0.1.57"
|
||||
|
|
|
@ -661,7 +661,7 @@ mod tests {
|
|||
let mut update = updates[0].clone();
|
||||
update.finalized_header = Header::default();
|
||||
|
||||
let err = client.verify_update(&mut update).err().unwrap();
|
||||
let err = client.verify_update(&update).err().unwrap();
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
ConsensusError::InvalidFinalityProof.to_string()
|
||||
|
@ -681,7 +681,7 @@ mod tests {
|
|||
let mut update = updates[0].clone();
|
||||
update.sync_aggregate.sync_committee_signature = Vector::default();
|
||||
|
||||
let err = client.verify_update(&mut update).err().unwrap();
|
||||
let err = client.verify_update(&update).err().unwrap();
|
||||
assert_eq!(
|
||||
err.to_string(),
|
||||
ConsensusError::InvalidSignature.to_string()
|
||||
|
|
|
@ -19,24 +19,24 @@ async fn main() -> Result<()> {
|
|||
.fetch_latest_checkpoint(&networks::Network::GOERLI)
|
||||
.await
|
||||
.unwrap();
|
||||
println!("Fetched latest goerli checkpoint: {}", goerli_checkpoint);
|
||||
println!("Fetched latest goerli checkpoint: {goerli_checkpoint}");
|
||||
|
||||
// Fetch the latest mainnet checkpoint
|
||||
let mainnet_checkpoint = cf
|
||||
.fetch_latest_checkpoint(&networks::Network::MAINNET)
|
||||
.await
|
||||
.unwrap();
|
||||
println!("Fetched latest mainnet checkpoint: {}", mainnet_checkpoint);
|
||||
println!("Fetched latest mainnet checkpoint: {mainnet_checkpoint}");
|
||||
|
||||
// Let's get a list of all the fallback service endpoints for mainnet
|
||||
let endpoints = cf.get_all_fallback_endpoints(&networks::Network::MAINNET);
|
||||
println!("Fetched all mainnet fallback endpoints: {:?}", endpoints);
|
||||
println!("Fetched all mainnet fallback endpoints: {endpoints:?}");
|
||||
|
||||
// Since we built the checkpoint fallback services, we can also just get the raw checkpoint fallback services.
|
||||
// The `get_fallback_services` method returns a reference to the internal list of CheckpointFallbackService objects
|
||||
// for the given network.
|
||||
let services = cf.get_fallback_services(&networks::Network::MAINNET);
|
||||
println!("Fetched all mainnet fallback services: {:?}", services);
|
||||
println!("Fetched all mainnet fallback services: {services:?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ async fn main() -> Result<()> {
|
|||
// Load the config from the global config file
|
||||
let config_path = home::home_dir().unwrap().join(".helios/helios.toml");
|
||||
let config = Config::from_file(&config_path, "mainnet", &CliConfig::default());
|
||||
println!("Constructed config: {:#?}", config);
|
||||
println!("Constructed config: {config:#?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ serde = { version = "1.0.143", features = ["derive"] }
|
|||
serde_json = "1.0.85"
|
||||
hex = "0.4.3"
|
||||
ssz-rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "cb08f18ca919cc1b685b861d0fa9e2daabe89737" }
|
||||
ethers = "1.0.0"
|
||||
ethers = { version = "1.0.2", features = [ "ws", "default" ] }
|
||||
revm = "2.1.0"
|
||||
bytes = "1.2.1"
|
||||
futures = "0.3.23"
|
||||
|
|
|
@ -15,6 +15,7 @@ use revm::KECCAK_EMPTY;
|
|||
use triehash_ethereum::ordered_trie_root;
|
||||
|
||||
use crate::errors::ExecutionError;
|
||||
use crate::rpc::WsRpc;
|
||||
use crate::types::Transactions;
|
||||
|
||||
use super::proof::{encode_account, verify_proof};
|
||||
|
@ -30,6 +31,13 @@ pub struct ExecutionClient<R: ExecutionRpc> {
|
|||
pub rpc: R,
|
||||
}
|
||||
|
||||
impl ExecutionClient<WsRpc> {
|
||||
pub fn new_with_ws(rpc: &str) -> Result<Self> {
|
||||
let rpc = WsRpc::new(rpc)?;
|
||||
Ok(Self { rpc })
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: ExecutionRpc> ExecutionClient<R> {
|
||||
pub fn new(rpc: &str) -> Result<Self> {
|
||||
let rpc = ExecutionRpc::new(rpc)?;
|
||||
|
|
|
@ -90,7 +90,7 @@ fn get_rest_path(p: &Vec<u8>, s: usize) -> String {
|
|||
let mut ret = String::new();
|
||||
for i in s..p.len() * 2 {
|
||||
let n = get_nibble(p, i);
|
||||
ret += &format!("{:01x}", n);
|
||||
ret += &format!("{n:01x}");
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@ use eyre::Result;
|
|||
use crate::types::CallOpts;
|
||||
|
||||
pub mod http_rpc;
|
||||
pub use http_rpc::*;
|
||||
pub mod ws_rpc;
|
||||
pub use ws_rpc::*;
|
||||
pub mod mock_rpc;
|
||||
|
||||
#[async_trait]
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use common::errors::RpcError;
|
||||
use ethers::prelude::{Address, Ws};
|
||||
use ethers::providers::{HttpRateLimitRetryPolicy, Middleware, Provider, RetryClient};
|
||||
use ethers::types::transaction::eip2718::TypedTransaction;
|
||||
use ethers::types::transaction::eip2930::AccessList;
|
||||
use ethers::types::{
|
||||
BlockId, Bytes, EIP1186ProofResponse, Eip1559TransactionRequest, Filter, Log, Transaction,
|
||||
TransactionReceipt, H256, U256,
|
||||
use ethers::{
|
||||
prelude::*,
|
||||
types::transaction::{eip2718::TypedTransaction, eip2930::AccessList},
|
||||
};
|
||||
use eyre::Result;
|
||||
|
||||
|
@ -30,9 +24,9 @@ impl Clone for WsRpc {
|
|||
#[async_trait]
|
||||
impl ExecutionRpc for WsRpc {
|
||||
fn new(rpc: &str) -> Result<Self> {
|
||||
Ok(WsRpc {
|
||||
Ok(Self {
|
||||
url: rpc.to_string(),
|
||||
provider: None,
|
||||
provider: None::<Provider<Ws>>,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -51,6 +45,11 @@ impl ExecutionRpc for WsRpc {
|
|||
let block = Some(BlockId::from(block));
|
||||
let proof_response = self
|
||||
.provider
|
||||
.as_ref()
|
||||
.ok_or(RpcError::new(
|
||||
"get_proof",
|
||||
eyre::eyre!("Provider not connected!"),
|
||||
))?
|
||||
.get_proof(*address, slots.to_vec(), block)
|
||||
.await
|
||||
.map_err(|e| RpcError::new("get_proof", e))?;
|
||||
|
@ -76,6 +75,11 @@ impl ExecutionRpc for WsRpc {
|
|||
let tx = TypedTransaction::Eip1559(raw_tx);
|
||||
let list = self
|
||||
.provider
|
||||
.as_ref()
|
||||
.ok_or(RpcError::new(
|
||||
"create_access_list",
|
||||
eyre::eyre!("Provider not connected!"),
|
||||
))?
|
||||
.create_access_list(&tx, block)
|
||||
.await
|
||||
.map_err(|e| RpcError::new("create_access_list", e))?;
|
||||
|
@ -87,6 +91,11 @@ impl ExecutionRpc for WsRpc {
|
|||
let block = Some(BlockId::from(block));
|
||||
let code = self
|
||||
.provider
|
||||
.as_ref()
|
||||
.ok_or(RpcError::new(
|
||||
"get_code",
|
||||
eyre::eyre!("Provider not connected!"),
|
||||
))?
|
||||
.get_code(*address, block)
|
||||
.await
|
||||
.map_err(|e| RpcError::new("get_code", e))?;
|
||||
|
@ -98,6 +107,11 @@ impl ExecutionRpc for WsRpc {
|
|||
let bytes = Bytes::from(bytes.to_owned());
|
||||
let tx = self
|
||||
.provider
|
||||
.as_ref()
|
||||
.ok_or(RpcError::new(
|
||||
"send_raw_transaction",
|
||||
eyre::eyre!("Provider not connected!"),
|
||||
))?
|
||||
.send_raw_transaction(bytes)
|
||||
.await
|
||||
.map_err(|e| RpcError::new("send_raw_transaction", e))?;
|
||||
|
@ -108,6 +122,11 @@ impl ExecutionRpc for WsRpc {
|
|||
async fn get_transaction_receipt(&self, tx_hash: &H256) -> Result<Option<TransactionReceipt>> {
|
||||
let receipt = self
|
||||
.provider
|
||||
.as_ref()
|
||||
.ok_or(RpcError::new(
|
||||
"get_transaction_receipt",
|
||||
eyre::eyre!("Provider not connected!"),
|
||||
))?
|
||||
.get_transaction_receipt(*tx_hash)
|
||||
.await
|
||||
.map_err(|e| RpcError::new("get_transaction_receipt", e))?;
|
||||
|
@ -118,6 +137,11 @@ impl ExecutionRpc for WsRpc {
|
|||
async fn get_transaction(&self, tx_hash: &H256) -> Result<Option<Transaction>> {
|
||||
Ok(self
|
||||
.provider
|
||||
.as_ref()
|
||||
.ok_or(RpcError::new(
|
||||
"get_transaction",
|
||||
eyre::eyre!("Provider not connected!"),
|
||||
))?
|
||||
.get_transaction(*tx_hash)
|
||||
.await
|
||||
.map_err(|e| RpcError::new("get_transaction", e))?)
|
||||
|
@ -126,6 +150,11 @@ impl ExecutionRpc for WsRpc {
|
|||
async fn get_logs(&self, filter: &Filter) -> Result<Vec<Log>> {
|
||||
Ok(self
|
||||
.provider
|
||||
.as_ref()
|
||||
.ok_or(RpcError::new(
|
||||
"get_logs",
|
||||
eyre::eyre!("Provider not connected!"),
|
||||
))?
|
||||
.get_logs(filter)
|
||||
.await
|
||||
.map_err(|e| RpcError::new("get_logs", e))?)
|
||||
|
|
|
@ -18,11 +18,13 @@ async fn test_get_account() {
|
|||
let execution = get_client();
|
||||
let address = Address::from_str("14f9D4aF749609c1438528C0Cce1cC3f6D411c47").unwrap();
|
||||
|
||||
let mut payload = ExecutionPayload::default();
|
||||
payload.state_root = Vector::from_iter(
|
||||
let payload = ExecutionPayload {
|
||||
state_root: Vector::from_iter(
|
||||
hex_str_to_bytes("0xaa02f5db2ee75e3da400d10f3c30e894b6016ce8a2501680380a907b6674ce0d")
|
||||
.unwrap(),
|
||||
);
|
||||
),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let account = execution
|
||||
.get_account(&address, None, &payload)
|
||||
|
@ -102,11 +104,13 @@ async fn test_get_tx_not_included() {
|
|||
#[tokio::test]
|
||||
async fn test_get_logs() {
|
||||
let execution = get_client();
|
||||
let mut payload = ExecutionPayload::default();
|
||||
payload.receipts_root = Vector::from_iter(
|
||||
let mut payload = ExecutionPayload {
|
||||
receipts_root: Vector::from_iter(
|
||||
hex_str_to_bytes("dd82a78eccb333854f0c99e5632906e092d8a49c27a21c25cae12b82ec2a113f")
|
||||
.unwrap(),
|
||||
);
|
||||
),
|
||||
..ExecutionPayload::default()
|
||||
};
|
||||
|
||||
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
|
||||
|
||||
|
@ -130,11 +134,13 @@ async fn test_get_receipt() {
|
|||
let tx_hash =
|
||||
H256::from_str("2dac1b27ab58b493f902dda8b63979a112398d747f1761c0891777c0983e591f").unwrap();
|
||||
|
||||
let mut payload = ExecutionPayload::default();
|
||||
payload.receipts_root = Vector::from_iter(
|
||||
let mut payload = ExecutionPayload {
|
||||
receipts_root: Vector::from_iter(
|
||||
hex_str_to_bytes("dd82a78eccb333854f0c99e5632906e092d8a49c27a21c25cae12b82ec2a113f")
|
||||
.unwrap(),
|
||||
);
|
||||
),
|
||||
..ExecutionPayload::default()
|
||||
};
|
||||
|
||||
payload.transactions.push(List::from_iter(hex_str_to_bytes("0x02f8b20583623355849502f900849502f91082ea6094326c977e6efc84e512bb9c30f76e30c160ed06fb80b844a9059cbb0000000000000000000000007daccf9b3c1ae2fa5c55f1c978aeef700bc83be0000000000000000000000000000000000000000000000001158e460913d00000c080a0e1445466b058b6f883c0222f1b1f3e2ad9bee7b5f688813d86e3fa8f93aa868ca0786d6e7f3aefa8fe73857c65c32e4884d8ba38d0ecfb947fbffb82e8ee80c167").unwrap()));
|
||||
|
||||
|
@ -185,8 +191,10 @@ async fn test_get_receipt_not_included() {
|
|||
#[tokio::test]
|
||||
async fn test_get_block() {
|
||||
let execution = get_client();
|
||||
let mut payload = ExecutionPayload::default();
|
||||
payload.block_number = 12345;
|
||||
let payload = ExecutionPayload {
|
||||
block_number: 12345,
|
||||
..ExecutionPayload::default()
|
||||
};
|
||||
|
||||
let block = execution.get_block(&payload, false).await.unwrap();
|
||||
|
||||
|
|
Loading…
Reference in New Issue