use std::path::PathBuf; use std::sync::Arc; use config::networks::Network; use ethers::prelude::{Address, U256}; use ethers::types::{Transaction, TransactionReceipt, H256}; use eyre::{eyre, Result}; use common::types::BlockTag; use config::Config; use consensus::types::Header; use execution::types::{CallOpts, ExecutionBlock}; use log::{info, warn}; use tokio::spawn; use tokio::sync::RwLock; use tokio::time::sleep; use crate::database::{Database, FileDB}; use crate::node::Node; use crate::rpc::Rpc; pub struct Client { node: Arc>, rpc: Option, db: DB, } impl Client { fn new(config: Config) -> Result { let config = Arc::new(config); let node = Node::new(config.clone())?; let node = Arc::new(RwLock::new(node)); let rpc = if let Some(port) = config.rpc_port { Some(Rpc::new(node.clone(), port)) } else { None }; let data_dir = config.data_dir.clone(); let db = FileDB::new(data_dir.ok_or(eyre!("data dir not found"))?); Ok(Client { node, rpc, db }) } } pub struct ClientBuilder { network: Option, consensus_rpc: Option, execution_rpc: Option, checkpoint: Option>, rpc_port: Option, data_dir: Option, config: Option, } impl ClientBuilder { pub fn new() -> Self { Self { network: None, consensus_rpc: None, execution_rpc: None, checkpoint: None, rpc_port: None, data_dir: None, config: None, } } pub fn network(mut self, network: Network) -> Self { self.network = Some(network); 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).expect("cannot parse checkpoint"); self.checkpoint = Some(checkpoint); self } pub fn rpc_port(mut self, port: u16) -> Self { self.rpc_port = Some(port); self } pub fn data_dir(mut self, data_dir: PathBuf) -> Self { self.data_dir = Some(data_dir); self } pub fn config(mut self, config: Config) -> Self { self.config = Some(config); self } pub fn build(self) -> Result> { let base_config = if let Some(network) = self.network { network.to_base_config() } else { let config = self .config .as_ref() .ok_or(eyre!("missing network config"))?; config.to_base_config() }; let consensus_rpc = self.consensus_rpc.unwrap_or( self.config .as_ref() .ok_or(eyre!("missing consensus rpc"))? .consensus_rpc .clone(), ); let execution_rpc = self.execution_rpc.unwrap_or( self.config .as_ref() .ok_or(eyre!("missing execution rpc"))? .execution_rpc .clone(), ); let checkpoint = self.checkpoint.unwrap_or( self.config .as_ref() .ok_or(eyre!("missing checkpoint"))? .checkpoint .clone(), ); let rpc_port = if self.rpc_port.is_some() { self.rpc_port } else if let Some(config) = &self.config { config.rpc_port } else { None }; let data_dir = if self.data_dir.is_some() { self.data_dir } else if let Some(config) = &self.config { config.data_dir.clone() } else { None }; let config = Config { consensus_rpc, execution_rpc, checkpoint, rpc_port, data_dir, chain: base_config.chain, forks: base_config.forks, }; Client::new(config) } } impl Client { pub async fn start(&mut self) -> Result<()> { self.rpc.as_mut().unwrap().start().await?; let node = self.node.clone(); spawn(async move { let res = node.write().await.sync().await; if let Err(err) = res { warn!("consensus error: {}", err); } loop { let res = node.write().await.advance().await; if let Err(err) = res { warn!("consensus error: {}", err); } let next_update = node.read().await.duration_until_next_update(); sleep(next_update).await; } }); Ok(()) } pub async fn shutdown(&self) { let node = self.node.read().await; let checkpoint = if let Some(checkpoint) = node.get_last_checkpoint() { checkpoint } else { return; }; info!("saving last checkpoint hash"); let res = self.db.save_checkpoint(checkpoint); if res.is_err() { warn!("checkpoint save failed"); } } pub async fn call(&self, opts: &CallOpts, block: &BlockTag) -> Result> { self.node.read().await.call(opts, block).await } pub async fn estimate_gas(&self, opts: &CallOpts) -> Result { self.node.read().await.estimate_gas(opts).await } pub async fn get_balance(&self, address: &Address, block: &BlockTag) -> Result { self.node.read().await.get_balance(address, block).await } pub async fn get_nonce(&self, address: &Address, block: &BlockTag) -> Result { self.node.read().await.get_nonce(address, block).await } pub async fn get_code(&self, address: &Address, block: &BlockTag) -> Result> { self.node.read().await.get_code(address, block).await } pub async fn get_storage_at(&self, address: &Address, slot: H256) -> Result { self.node.read().await.get_storage_at(address, slot).await } pub async fn send_raw_transaction(&self, bytes: &Vec) -> Result { self.node.read().await.send_raw_transaction(bytes).await } pub async fn get_transaction_receipt( &self, tx_hash: &H256, ) -> Result> { self.node .read() .await .get_transaction_receipt(tx_hash) .await } pub async fn get_transaction_by_hash(&self, tx_hash: &H256) -> Result> { self.node .read() .await .get_transaction_by_hash(tx_hash) .await } pub async fn get_gas_price(&self) -> Result { self.node.read().await.get_gas_price() } pub async fn get_priority_fee(&self) -> Result { self.node.read().await.get_priority_fee() } pub async fn get_block_number(&self) -> Result { self.node.read().await.get_block_number() } pub async fn get_block_by_number( &self, block: &BlockTag, full_tx: bool, ) -> Result> { self.node .read() .await .get_block_by_number(block, full_tx) .await } pub async fn get_block_by_hash( &self, hash: &Vec, full_tx: bool, ) -> Result> { self.node .read() .await .get_block_by_hash(hash, full_tx) .await } pub async fn chain_id(&self) -> u64 { self.node.read().await.chain_id() } pub async fn get_header(&self) -> Result
{ self.node.read().await.get_header() } }