diff --git a/Cargo.toml b/Cargo.toml index 9fd161db..4c1062c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ "src/evm-log-extractor", "src/signature-verification-canister-client", ] -resolver = "2" +resolver = "3" [workspace.package] authors = ["Bitfinity Network"] @@ -43,7 +43,6 @@ alloy = { version = "0.15", default-features = false, features = [ "serde", ] } anyhow = "1.0" -async-trait = "0.1" bincode = "1.3" bytes = "1" candid = "0.10" @@ -59,7 +58,7 @@ ic-log = { git = "https://github.com/bitfinity-network/canister-sdk", package = ic-stable-structures = { git = "https://github.com/bitfinity-network/canister-sdk", package = "ic-stable-structures", tag = "v0.24.x" } itertools = "0.14" jsonrpsee = { version = "0.25", features = ["server", "macros"] } -lightspeed_scheduler = "0.63" +lightspeed_scheduler = "0.64" log = "0.4" num = "0.4" port_check = "0.2" @@ -80,7 +79,7 @@ sqlx = { version = "0.8.1", default-features = false, features = [ "runtime-tokio", ] } tempfile = "3" -testcontainers = { package = "testcontainers-modules", version = "0.11", features = [ +testcontainers = { package = "testcontainers-modules", version = "0.12", features = [ "postgres", ] } thiserror = "2.0" diff --git a/src/evm-block-extractor/Cargo.toml b/src/evm-block-extractor/Cargo.toml index c7e40cb3..8c869c54 100644 --- a/src/evm-block-extractor/Cargo.toml +++ b/src/evm-block-extractor/Cargo.toml @@ -12,7 +12,6 @@ repository.workspace = true [dependencies] alloy = { workspace = true } anyhow = { workspace = true } -async-trait = { workspace = true } chrono = { workspace = true } clap = { workspace = true } did = { workspace = true } diff --git a/src/evm-block-extractor/src/config.rs b/src/evm-block-extractor/src/config.rs index 0da6ac84..b3bb67e9 100644 --- a/src/evm-block-extractor/src/config.rs +++ b/src/evm-block-extractor/src/config.rs @@ -4,7 +4,6 @@ use clap::{Parser, Subcommand}; use sqlx::PgPool; use sqlx::postgres::{PgConnectOptions, PgSslMode}; -use crate::database::DatabaseClient; use crate::database::postgres_db_client::PostgresDbClient; const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -78,7 +77,7 @@ pub enum Database { impl Database { /// Build a database client based on the database type - pub async fn build_client(self) -> anyhow::Result> { + pub async fn build_client(self) -> anyhow::Result> { match self { Database::Postgres { username, diff --git a/src/evm-block-extractor/src/database/mod.rs b/src/evm-block-extractor/src/database/mod.rs index ab0c213f..f4175b49 100644 --- a/src/evm-block-extractor/src/database/mod.rs +++ b/src/evm-block-extractor/src/database/mod.rs @@ -35,75 +35,102 @@ const BLOCKCHAIN_BLOCK_INFO_KEY: &str = "blockchain_block_info"; pub type CertifiedBlock = CertifiedResult>; /// A trait for interacting with a blockchain database -#[async_trait::async_trait] pub trait DatabaseClient: Send + Sync { /// Initialize the database - async fn init(&self, block: Option>, reset_database: bool) -> anyhow::Result<()>; + fn init( + &self, + block: Option>, + reset_database: bool, + ) -> impl Future> + Send; /// Delete/clear the tables - async fn clear(&self) -> anyhow::Result<()>; + fn clear(&self) -> impl Future> + Send; /// Returns whether the block hash corresponds to the one in the db - async fn check_if_same_block_hash(&self, block: &Block) -> anyhow::Result { - let block_number = block.number.0.to(); - let block_in_db = self.get_block_by_number(block_number).await?; - Ok(block.hash == block_in_db.hash) + fn check_if_same_block_hash( + &self, + block: &Block, + ) -> impl Future> + Send { + async { + let block_number = block.number.0.to(); + let block_in_db = self.get_block_by_number(block_number).await?; + Ok(block.hash == block_in_db.hash) + } } /// Get a block from the database - async fn get_block_by_number(&self, block_number: u64) -> anyhow::Result>; + fn get_block_by_number( + &self, + block_number: u64, + ) -> impl Future>> + Send; /// Get a block from the database - async fn get_full_block_by_number( + fn get_full_block_by_number( &self, block_number: u64, - ) -> anyhow::Result>; + ) -> impl Future>> + Send; /// Insert block data; this includes transactions and the blocks - async fn insert_block_data( + fn insert_block_data( &self, blocks: &[Block], transactions: &[Transaction], - ) -> anyhow::Result<()>; + ) -> impl Future> + Send; /// Insert certified block data - async fn insert_certified_block_data(&self, response: CertifiedBlock) -> anyhow::Result<()>; + fn insert_certified_block_data( + &self, + response: CertifiedBlock, + ) -> impl Future> + Send; /// Returns certified response for the last block - async fn get_last_certified_block_data(&self) -> anyhow::Result; + fn get_last_certified_block_data( + &self, + ) -> impl Future> + Send; /// Get genesis balances - async fn get_genesis_balances(&self) -> anyhow::Result>>; + fn get_genesis_balances( + &self, + ) -> impl Future>>> + Send; /// Insert genesis balances - async fn insert_genesis_balances( + fn insert_genesis_balances( &self, genesis_balances: &[AccountBalance], - ) -> anyhow::Result<()>; + ) -> impl Future> + Send; /// Get chain id - async fn get_chain_id(&self) -> anyhow::Result>; + fn get_chain_id(&self) -> impl Future>> + Send; /// Insert chain_id - async fn insert_chain_id(&self, chain_id: u64) -> anyhow::Result<()>; + fn insert_chain_id(&self, chain_id: u64) -> impl Future> + Send; /// Get a transaction from the database - async fn get_transaction(&self, tx_hash: H256) -> anyhow::Result; + fn get_transaction( + &self, + tx_hash: H256, + ) -> impl Future> + Send; /// Get the latest block number - async fn get_latest_block_number(&self) -> anyhow::Result>; + fn get_latest_block_number(&self) -> impl Future>> + Send; /// Get earliest block number - async fn get_earliest_block_number(&self) -> anyhow::Result; + fn get_earliest_block_number(&self) -> impl Future> + Send; /// Delete latest blocks starting with `start_from`, and related transactions. /// Deleted blocks and transactions will be preserved in 'discarded' table with /// the given 'reason' and timestamp. - async fn discard_blocks_from(&self, start_from: u64, reason: &str) -> anyhow::Result<()>; + fn discard_blocks_from( + &self, + start_from: u64, + reason: &str, + ) -> impl Future> + Send; /// Returns a discarded block by its hash. - async fn get_discarded_block_by_hash(&self, block_hash: H256) - -> anyhow::Result; + fn get_discarded_block_by_hash( + &self, + block_hash: H256, + ) -> impl Future> + Send; /// Returns block info from storage. /// @@ -115,10 +142,15 @@ pub trait DatabaseClient: Send + Sync { /// - safe_block_number: u64, /// - finalized_block_number: u64, /// - pending_block_number: u64, - async fn get_block_info(&self) -> anyhow::Result>; + fn get_block_info( + &self, + ) -> impl Future>> + Send; /// Stores blockchain block info. - async fn set_block_info(&self, info: BlockchainBlockInfo) -> anyhow::Result<()>; + fn set_block_info( + &self, + info: BlockchainBlockInfo, + ) -> impl Future> + Send; } /// Discarded block with metadata. diff --git a/src/evm-block-extractor/src/database/postgres_db_client.rs b/src/evm-block-extractor/src/database/postgres_db_client.rs index e048c610..783578db 100644 --- a/src/evm-block-extractor/src/database/postgres_db_client.rs +++ b/src/evm-block-extractor/src/database/postgres_db_client.rs @@ -71,7 +71,6 @@ impl PostgresDbClient { } } -#[async_trait::async_trait] impl DatabaseClient for PostgresDbClient { async fn init(&self, block: Option>, reset_database: bool) -> anyhow::Result<()> { MIGRATOR.run(&self.pool).await?; diff --git a/src/evm-block-extractor/src/rpc.rs b/src/evm-block-extractor/src/rpc.rs index 26804802..e3503a56 100644 --- a/src/evm-block-extractor/src/rpc.rs +++ b/src/evm-block-extractor/src/rpc.rs @@ -11,23 +11,34 @@ use jsonrpsee::types::{ErrorCode, ErrorObject}; use crate::database::{CertifiedBlock, DatabaseClient}; -#[derive(Clone)] -pub struct EthImpl +pub struct EthImpl where + DB: DatabaseClient, C: Client + Send + Sync + 'static, { - pub blockchain: Arc, + pub blockchain: Arc, pub evm_client: Arc>, } -impl EthImpl +impl Clone for EthImpl where + DB: DatabaseClient, C: Client + Send + Sync + 'static, { - pub fn new( - db: Arc, - evm_client: Arc>, - ) -> Self { + fn clone(&self) -> Self { + Self { + blockchain: self.blockchain.clone(), + evm_client: self.evm_client.clone(), + } + } +} + +impl EthImpl +where + DB: DatabaseClient, + C: Client + Send + Sync + 'static, +{ + pub fn new(db: Arc, evm_client: Arc>) -> Self { Self { blockchain: db, evm_client, @@ -74,9 +85,10 @@ pub trait IC { ) -> RpcResult; } -#[async_trait::async_trait] -impl ICServer for EthImpl +#[jsonrpsee::core::async_trait] +impl ICServer for EthImpl where + DB: DatabaseClient + Send + Sync + 'static, C: Client + Send + Sync + 'static, { async fn get_genesis_balances(&self) -> RpcResult> { @@ -143,9 +155,10 @@ where } } -#[async_trait::async_trait] -impl EthServer for EthImpl +#[jsonrpsee::core::async_trait] +impl EthServer for EthImpl where + DB: DatabaseClient + Send + Sync + 'static, C: Client + Send + Sync + 'static, { async fn get_block_by_number( diff --git a/src/evm-block-extractor/src/server.rs b/src/evm-block-extractor/src/server.rs index dc70f510..1a4277d2 100644 --- a/src/evm-block-extractor/src/server.rs +++ b/src/evm-block-extractor/src/server.rs @@ -9,9 +9,9 @@ use crate::database::DatabaseClient; use crate::rpc::{EthImpl, EthServer, ICServer}; /// Start the RPC server -pub async fn server_start( +pub async fn server_start( server_address: &str, - db_client: Arc, + db_client: Arc, evm_client: Arc>, ) -> anyhow::Result { info!("Start server"); diff --git a/src/evm-block-extractor/src/task/block_extractor.rs b/src/evm-block-extractor/src/task/block_extractor.rs index 02012759..95d6ad93 100644 --- a/src/evm-block-extractor/src/task/block_extractor.rs +++ b/src/evm-block-extractor/src/task/block_extractor.rs @@ -10,9 +10,9 @@ use crate::config::ExtractorArgs; use crate::database::{AccountBalance, CertifiedBlock, DatabaseClient}; /// Starts the block extractor process -pub async fn start_extractor( +pub async fn start_extractor( config: ExtractorArgs, - db_client: Arc, + db_client: Arc, evm_client: Arc>, ) -> anyhow::Result<()> { let earliest_block = evm_client @@ -44,11 +44,11 @@ pub async fn start_extractor( } /// Extracts blocks from an EVMC and stores them in a database -pub struct BlockExtractor { +pub struct BlockExtractor { client: Arc>, request_time_out_secs: u64, rpc_batch_size: usize, - blockchain: Arc, + blockchain: Arc, } /// Outcome of the block extraction process @@ -60,12 +60,12 @@ pub enum BlockExtractCollectOutcome { BlocksExtracted { from_block: u64, to_block: u64 }, } -impl BlockExtractor { +impl BlockExtractor { pub fn new( client: Arc>, request_time_out_secs: u64, rpc_batch_size: usize, - blockchain: Arc, + blockchain: Arc, ) -> Self { Self { client, @@ -367,13 +367,17 @@ mod tests { use ethereum_json_rpc_client::reqwest::ReqwestClient; use super::*; + use crate::database::postgres_db_client::PostgresDbClient; #[test] fn test_validate_chain_without_blocks_in_storage() { let latest_block_in_storage = Option::>::None; let sequence = generate_valid_blocks_sequence(10, did::H256::default()); - BlockExtractor::::validate_chain(latest_block_in_storage, &sequence) - .unwrap(); + BlockExtractor::::validate_chain( + latest_block_in_storage, + &sequence, + ) + .unwrap(); } #[test] @@ -384,8 +388,11 @@ mod tests { let hash = block.hash.clone(); let latest_block_in_storage = Some(block); let sequence = generate_valid_blocks_sequence(10, hash); - BlockExtractor::::validate_chain(latest_block_in_storage, &sequence) - .unwrap(); + BlockExtractor::::validate_chain( + latest_block_in_storage, + &sequence, + ) + .unwrap(); } #[test] @@ -396,9 +403,11 @@ mod tests { let latest_block_in_storage = Some(block); let invalid_parent_hash = keccak::keccak_hash(&[1, 2, 3]); let sequence = generate_valid_blocks_sequence(10, invalid_parent_hash); - let err = - BlockExtractor::::validate_chain(latest_block_in_storage, &sequence) - .unwrap_err(); + let err = BlockExtractor::::validate_chain( + latest_block_in_storage, + &sequence, + ) + .unwrap_err(); assert!(matches!(err, ChainError::InconsistentStorage)) } @@ -414,9 +423,11 @@ mod tests { // break the sequnce sequence[5].parent_hash = keccak::keccak_hash(&[1, 2, 3, 4]); - let err = - BlockExtractor::::validate_chain(latest_block_in_storage, &sequence) - .unwrap_err(); + let err = BlockExtractor::::validate_chain( + latest_block_in_storage, + &sequence, + ) + .unwrap_err(); assert!(matches!(err, ChainError::InconsistentSequence)) } diff --git a/src/evm-block-extractor/tests/evm_block_extractor_it.rs b/src/evm-block-extractor/tests/evm_block_extractor_it.rs index ab8cac8e..91d19091 100644 --- a/src/evm-block-extractor/tests/evm_block_extractor_it.rs +++ b/src/evm-block-extractor/tests/evm_block_extractor_it.rs @@ -1,13 +1,13 @@ use std::sync::Arc; use evm_block_extractor::config::Database; -use evm_block_extractor::database::DatabaseClient; +use evm_block_extractor::database::postgres_db_client::PostgresDbClient; use testcontainers::testcontainers::ContainerAsync; use testcontainers::testcontainers::runners::AsyncRunner; mod tests; -async fn test_with_clients) -> ()>(test: T) { +async fn test_with_clients) -> ()>(test: T) { let _ = env_logger::Builder::new().parse_filters("info").try_init(); println!("----------------------------------"); println!("Running test with PostgresDbClient"); @@ -17,7 +17,7 @@ async fn test_with_clients) -> ()>(test: T) { } async fn new_postgres_db_client() -> ( - Arc, + Arc, ContainerAsync, ) { let node = testcontainers::postgres::Postgres::default() diff --git a/src/evm-block-extractor/tests/tests/block_extractor_it.rs b/src/evm-block-extractor/tests/tests/block_extractor_it.rs index 1092c43e..30d4e6d8 100644 --- a/src/evm-block-extractor/tests/tests/block_extractor_it.rs +++ b/src/evm-block-extractor/tests/tests/block_extractor_it.rs @@ -15,7 +15,7 @@ use did::{ }; use ethereum_json_rpc_client::reqwest::ReqwestClient; use ethereum_json_rpc_client::{CertifiedResult, Client, EthJsonRpcClient, JsonRpcResult}; -use evm_block_extractor::database::AccountBalance; +use evm_block_extractor::database::{AccountBalance, DatabaseClient}; use evm_block_extractor::server; use evm_block_extractor::task::block_extractor::{BlockExtractCollectOutcome, BlockExtractor}; use serde::de::DeserializeOwned; diff --git a/src/evm-block-extractor/tests/tests/database_client_it.rs b/src/evm-block-extractor/tests/tests/database_client_it.rs index ad1e6d93..30a5fab2 100644 --- a/src/evm-block-extractor/tests/tests/database_client_it.rs +++ b/src/evm-block-extractor/tests/tests/database_client_it.rs @@ -1,4 +1,5 @@ use did::{Block, H160, H256, Transaction, U64, U256}; +use evm_block_extractor::database::postgres_db_client::PostgresDbClient; use evm_block_extractor::database::{AccountBalance, CertifiedBlock, DatabaseClient}; use rand::random; @@ -675,7 +676,7 @@ async fn test_blockchain_tail_discard_and_get_discarded_entries() { assert!( check_blocks_with_txs_storage_state( - &*db_client, + &db_client, &blocks[..FIRST_REMOVED_BLOCK as usize - 1], StorageState::Present, ) @@ -684,7 +685,7 @@ async fn test_blockchain_tail_discard_and_get_discarded_entries() { assert!( check_blocks_with_txs_storage_state( - &*db_client, + &db_client, &blocks[FIRST_REMOVED_BLOCK as usize - 1..], StorageState::NotPresent, ) @@ -715,7 +716,7 @@ async fn test_blockchain_tail_discard_and_get_discarded_entries() { } async fn check_blocks_with_txs_storage_state( - db_client: &dyn DatabaseClient, + db_client: &PostgresDbClient, blocks: &[Block], storage_state: StorageState, ) -> bool { diff --git a/src/evm-block-extractor/tests/tests/server_it.rs b/src/evm-block-extractor/tests/tests/server_it.rs index 60a5ac5e..352d996f 100644 --- a/src/evm-block-extractor/tests/tests/server_it.rs +++ b/src/evm-block-extractor/tests/tests/server_it.rs @@ -10,6 +10,7 @@ use did::rpc::version::Version; use did::{Block, BlockNumber, H160, H256, U64, U256}; use ethereum_json_rpc_client::reqwest::ReqwestClient; use ethereum_json_rpc_client::{Client, EthJsonRpcClient}; +use evm_block_extractor::database::postgres_db_client::PostgresDbClient; use evm_block_extractor::database::{AccountBalance, CertifiedBlock, DatabaseClient}; use evm_block_extractor::rpc::{EthImpl, EthServer, ICServer}; use jsonrpsee::RpcModule; @@ -22,7 +23,7 @@ use crate::tests::block_extractor_it::MockClient; const BLOCK_COUNT: u64 = 10; -async fn with_filled_db) -> ()>(func: Func) { +async fn with_filled_db) -> ()>(func: Func) { test_with_clients(async |db_client| { db_client.init(None, false).await.unwrap(); @@ -346,7 +347,7 @@ async fn test_get_evm_global_state() { } async fn new_server( - db_client: Arc, + db_client: Arc, evm_client: Option>>, ) -> (EthJsonRpcClient, u16, ServerHandle) { let evm_client = evm_client.unwrap_or_else(|| { @@ -355,7 +356,7 @@ async fn new_server( ))) }); - let eth = EthImpl::::new(db_client, evm_client); + let eth = EthImpl::::new(db_client, evm_client); let mut module = RpcModule::new(()); module.merge(EthServer::into_rpc(eth.clone())).unwrap(); module.merge(ICServer::into_rpc(eth)).unwrap(); diff --git a/src/evm-log-extractor/Cargo.toml b/src/evm-log-extractor/Cargo.toml index 21dcf994..111a1c85 100644 --- a/src/evm-log-extractor/Cargo.toml +++ b/src/evm-log-extractor/Cargo.toml @@ -21,7 +21,6 @@ log = { workspace = true } tokio = { workspace = true } [dev-dependencies] -async-trait = { workspace = true } candid = { workspace = true } rand = { workspace = true } serde = { workspace = true } diff --git a/src/evm-log-extractor/tests/evm_log_extractor_it.rs b/src/evm-log-extractor/tests/evm_log_extractor_it.rs index 4ce2e63f..ee225385 100644 --- a/src/evm-log-extractor/tests/evm_log_extractor_it.rs +++ b/src/evm-log-extractor/tests/evm_log_extractor_it.rs @@ -14,7 +14,6 @@ struct MockCanisterClient { max_logs: usize, } -#[async_trait::async_trait] impl CanisterClient for MockCanisterClient { async fn update(&self, _method: &str, _args: T) -> CanisterClientResult where