2022-08-27 00:05:12 +00:00
use std ::sync ::Arc ;
2022-11-03 19:24:17 +00:00
use config ::networks ::Network ;
2022-08-21 13:13:56 +00:00
use ethers ::prelude ::{ Address , U256 } ;
2022-11-17 17:14:13 +00:00
use ethers ::types ::{ Filter , Log , Transaction , TransactionReceipt , H256 } ;
2022-08-21 13:13:56 +00:00
2022-09-29 23:35:43 +00:00
use common ::types ::BlockTag ;
2022-12-02 01:18:23 +00:00
use config ::{ CheckpointFallback , Config } ;
use consensus ::{ types ::Header , ConsensusClient } ;
2022-12-02 21:30:16 +00:00
use execution ::rpc ::ExecutionRpc ;
2022-09-01 19:58:45 +00:00
use execution ::types ::{ CallOpts , ExecutionBlock } ;
2022-09-16 19:32:15 +00:00
use log ::{ info , warn } ;
2022-09-10 04:01:23 +00:00
use tokio ::spawn ;
2022-09-22 21:30:47 +00:00
use tokio ::sync ::RwLock ;
2022-09-10 04:01:23 +00:00
use tokio ::time ::sleep ;
2022-09-16 19:32:15 +00:00
use crate ::database ::{ Database , FileDB } ;
2022-09-29 23:35:43 +00:00
use crate ::node ::Node ;
2022-09-10 04:01:23 +00:00
use crate ::rpc ::Rpc ;
2022-08-21 13:13:56 +00:00
2022-12-02 21:30:16 +00:00
pub struct Client < DB : Database , R : ExecutionRpc > {
node : Arc < RwLock < Node < R > > > ,
rpc : Option < Rpc < R > > ,
2022-12-02 01:18:23 +00:00
db : Option < DB > ,
fallback : Option < String > ,
load_external_fallback : bool ,
}
2022-12-02 21:30:16 +00:00
impl < R > Client < FileDB , R > where R : ExecutionRpc {
pub fn new ( config : Config ) -> eyre ::Result < Self > {
2022-12-02 01:18:23 +00:00
let config = Arc ::new ( config ) ;
let node = Node ::new ( config . clone ( ) ) ? ;
let node = Arc ::new ( RwLock ::new ( node ) ) ;
2022-11-30 17:01:43 +00:00
let rpc = config
. rpc_port
. map ( | port | Rpc ::new ( node . clone ( ) , config . with_http , config . with_ws , port ) ) ;
2022-12-02 01:18:23 +00:00
let data_dir = config . data_dir . clone ( ) ;
let db = data_dir . map ( FileDB ::new ) ;
Ok ( Client {
node ,
rpc ,
db ,
fallback : config . fallback . clone ( ) ,
load_external_fallback : config . load_external_fallback ,
} )
}
}
2022-12-02 21:30:16 +00:00
impl < DB : Database , R : ExecutionRpc > Client < DB , R > {
pub async fn start ( & mut self ) -> eyre ::Result < ( ) > {
2022-11-03 23:36:14 +00:00
if let Some ( rpc ) = & mut self . rpc {
2022-11-30 17:01:43 +00:00
// We can start both ws and http servers since they only run if enabled in the config.
rpc . start_ws ( ) . await ? ;
rpc . start_http ( ) . await ? ;
2022-11-03 23:36:14 +00:00
}
2022-12-02 01:18:23 +00:00
if self . node . write ( ) . await . sync ( ) . await . is_err ( ) {
warn! (
" failed to sync consensus node with checkpoint: 0x{} " ,
hex ::encode ( & self . node . read ( ) . await . config . checkpoint ) ,
) ;
let fallback = self . boot_from_fallback ( ) . await ;
if fallback . is_err ( ) & & self . load_external_fallback {
self . boot_from_external_fallbacks ( ) . await ?
} else if fallback . is_err ( ) {
return Err ( eyre ::eyre! ( " Checkpoint is too old. Please update your checkpoint. Alternatively, set an explicit checkpoint fallback service url with the `-f` flag or use the configured external fallback services with `-l` (NOT RECOMMENED). See https://github.com/a16z/helios#additional-options for more information. " ) ) ;
}
2022-11-03 23:36:14 +00:00
}
2022-09-10 04:01:23 +00:00
let node = self . node . clone ( ) ;
spawn ( async move {
loop {
2022-09-22 21:30:47 +00:00
let res = node . write ( ) . await . advance ( ) . await ;
2022-09-10 04:01:23 +00:00
if let Err ( err ) = res {
2022-09-29 23:35:43 +00:00
warn! ( " consensus error: {} " , err ) ;
2022-09-10 04:01:23 +00:00
}
2022-09-02 04:13:22 +00:00
2022-09-28 20:48:24 +00:00
let next_update = node . read ( ) . await . duration_until_next_update ( ) ;
sleep ( next_update ) . await ;
2022-09-10 04:01:23 +00:00
}
} ) ;
2022-08-31 21:40:44 +00:00
Ok ( ( ) )
2022-08-31 00:31:58 +00:00
}
2022-12-02 01:18:23 +00:00
async fn boot_from_fallback ( & self ) -> eyre ::Result < ( ) > {
if let Some ( fallback ) = & self . fallback {
info! (
" attempting to load checkpoint from fallback \" {} \" " ,
fallback
) ;
let checkpoint = CheckpointFallback ::fetch_checkpoint_from_api ( fallback )
. await
. map_err ( | _ | {
eyre ::eyre! ( " Failed to fetch checkpoint from fallback \" {} \" " , fallback )
} ) ? ;
info! (
" external fallbacks responded with checkpoint 0x{:?} " ,
checkpoint
) ;
// 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 ( ) ) ? ;
self . node . write ( ) . await . consensus = consensus ;
self . node . write ( ) . await . sync ( ) . await ? ;
Ok ( ( ) )
} else {
Err ( eyre ::eyre! ( " no explicit fallback specified " ) )
}
}
async fn boot_from_external_fallbacks ( & self ) -> eyre ::Result < ( ) > {
info! ( " attempting to fetch checkpoint from external fallbacks... " ) ;
// Build the list of external checkpoint fallback services
let list = CheckpointFallback ::new ( )
. build ( )
. await
. map_err ( | _ | eyre ::eyre! ( " Failed to construct external checkpoint sync fallbacks " ) ) ? ;
let checkpoint = if self . node . read ( ) . await . config . chain . chain_id = = 5 {
list . fetch_latest_checkpoint ( & Network ::GOERLI )
. await
. map_err ( | _ | {
eyre ::eyre! ( " Failed to fetch latest goerli checkpoint from external fallbacks " )
} ) ?
} else {
list . fetch_latest_checkpoint ( & Network ::MAINNET )
. await
. map_err ( | _ | {
eyre ::eyre! ( " Failed to fetch latest mainnet checkpoint from external fallbacks " )
} ) ?
} ;
info! (
" external fallbacks responded with checkpoint {:?} " ,
checkpoint
) ;
// 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 ( ) ) ? ;
self . node . write ( ) . await . consensus = consensus ;
self . node . write ( ) . await . sync ( ) . await ? ;
Ok ( ( ) )
}
2022-09-16 19:32:15 +00:00
pub async fn shutdown ( & self ) {
2022-09-22 21:30:47 +00:00
let node = self . node . read ( ) . await ;
2022-09-16 19:32:15 +00:00
let checkpoint = if let Some ( checkpoint ) = node . get_last_checkpoint ( ) {
checkpoint
} else {
return ;
} ;
info! ( " saving last checkpoint hash " ) ;
2022-11-03 23:36:14 +00:00
let res = self . db . as_ref ( ) . map ( | db | db . save_checkpoint ( checkpoint ) ) ;
if res . is_some ( ) & & res . unwrap ( ) . is_err ( ) {
2022-09-16 19:32:15 +00:00
warn! ( " checkpoint save failed " ) ;
}
}
2022-12-02 21:30:16 +00:00
pub async fn call ( & self , opts : & CallOpts , block : BlockTag ) -> eyre ::Result < Vec < u8 > > {
2022-11-12 00:41:37 +00:00
self . node
. read ( )
. await
. call ( opts , block )
. await
. map_err ( | err | err . into ( ) )
2022-08-24 01:33:48 +00:00
}
2022-12-02 21:30:16 +00:00
pub async fn estimate_gas ( & self , opts : & CallOpts ) -> eyre ::Result < u64 > {
2022-11-12 00:41:37 +00:00
self . node
. read ( )
. await
. estimate_gas ( opts )
. await
. map_err ( | err | err . into ( ) )
2022-08-27 20:43:27 +00:00
}
2022-12-02 21:30:16 +00:00
pub async fn get_balance ( & self , address : & Address , block : BlockTag ) -> eyre ::Result < U256 > {
2022-09-22 21:30:47 +00:00
self . node . read ( ) . await . get_balance ( address , block ) . await
2022-08-21 13:13:56 +00:00
}
2022-12-02 21:30:16 +00:00
pub async fn get_nonce ( & self , address : & Address , block : BlockTag ) -> eyre ::Result < u64 > {
2022-09-22 21:30:47 +00:00
self . node . read ( ) . await . get_nonce ( address , block ) . await
2022-08-21 13:13:56 +00:00
}
2022-12-02 21:30:16 +00:00
pub async fn get_code ( & self , address : & Address , block : BlockTag ) -> eyre ::Result < Vec < u8 > > {
2022-09-22 21:30:47 +00:00
self . node . read ( ) . await . get_code ( address , block ) . await
2022-08-21 16:59:47 +00:00
}
2022-12-02 21:30:16 +00:00
pub async fn get_storage_at ( & self , address : & Address , slot : H256 ) -> eyre ::Result < U256 > {
2022-09-22 21:30:47 +00:00
self . node . read ( ) . await . get_storage_at ( address , slot ) . await
2022-08-21 21:51:11 +00:00
}
2022-12-02 21:30:16 +00:00
pub async fn send_raw_transaction ( & self , bytes : & [ u8 ] ) -> eyre ::Result < H256 > {
2022-09-22 21:30:47 +00:00
self . node . read ( ) . await . send_raw_transaction ( bytes ) . await
2022-09-01 21:07:30 +00:00
}
2022-09-02 00:28:12 +00:00
pub async fn get_transaction_receipt (
& self ,
2022-09-06 17:57:47 +00:00
tx_hash : & H256 ,
2022-12-02 21:30:16 +00:00
) -> eyre ::Result < Option < TransactionReceipt > > {
2022-09-10 04:01:23 +00:00
self . node
2022-09-22 21:30:47 +00:00
. read ( )
2022-09-10 04:01:23 +00:00
. await
. get_transaction_receipt ( tx_hash )
2022-09-02 00:28:12 +00:00
. await
}
2022-12-02 21:30:16 +00:00
pub async fn get_transaction_by_hash ( & self , tx_hash : & H256 ) -> eyre ::Result < Option < Transaction > > {
2022-09-10 04:01:23 +00:00
self . node
2022-09-22 21:30:47 +00:00
. read ( )
2022-09-10 04:01:23 +00:00
. await
. get_transaction_by_hash ( tx_hash )
2022-09-02 03:28:37 +00:00
. await
}
2022-12-02 21:30:16 +00:00
pub async fn get_logs ( & self , filter : & Filter ) -> eyre ::Result < Vec < Log > > {
2022-11-17 17:14:13 +00:00
self . node . read ( ) . await . get_logs ( filter ) . await
}
2022-12-02 21:30:16 +00:00
pub async fn get_gas_price ( & self ) -> eyre ::Result < U256 > {
2022-09-22 21:30:47 +00:00
self . node . read ( ) . await . get_gas_price ( )
2022-08-29 15:59:02 +00:00
}
2022-12-02 21:30:16 +00:00
pub async fn get_priority_fee ( & self ) -> eyre ::Result < U256 > {
2022-09-22 21:30:47 +00:00
self . node . read ( ) . await . get_priority_fee ( )
2022-08-29 16:06:50 +00:00
}
2022-12-02 21:30:16 +00:00
pub async fn get_block_number ( & self ) -> eyre ::Result < u64 > {
2022-09-22 21:30:47 +00:00
self . node . read ( ) . await . get_block_number ( )
2022-08-31 21:40:44 +00:00
}
2022-10-27 17:46:32 +00:00
pub async fn get_block_by_number (
& self ,
2022-11-03 23:36:14 +00:00
block : BlockTag ,
2022-10-27 17:46:32 +00:00
full_tx : bool ,
2022-12-02 21:30:16 +00:00
) -> eyre ::Result < Option < ExecutionBlock > > {
2022-10-27 17:46:32 +00:00
self . node
. read ( )
. await
. get_block_by_number ( block , full_tx )
. await
2022-09-02 04:13:22 +00:00
}
2022-10-27 17:46:32 +00:00
pub async fn get_block_by_hash (
& self ,
hash : & Vec < u8 > ,
full_tx : bool ,
2022-12-02 21:30:16 +00:00
) -> eyre ::Result < Option < ExecutionBlock > > {
2022-10-27 17:46:32 +00:00
self . node
. read ( )
. await
. get_block_by_hash ( hash , full_tx )
. await
2022-08-27 00:05:12 +00:00
}
2022-09-10 04:01:23 +00:00
pub async fn chain_id ( & self ) -> u64 {
2022-09-22 21:30:47 +00:00
self . node . read ( ) . await . chain_id ( )
2022-08-21 13:13:56 +00:00
}
2022-08-31 21:40:44 +00:00
2022-12-02 21:30:16 +00:00
pub async fn get_header ( & self ) -> eyre ::Result < Header > {
2022-09-28 21:50:39 +00:00
self . node . read ( ) . await . get_header ( )
2022-08-31 21:40:44 +00:00
}
2022-08-21 13:13:56 +00:00
}