From 81a362270c900b28220b29edc3b13d42188389a6 Mon Sep 17 00:00:00 2001 From: jouzo Date: Thu, 27 Apr 2023 09:21:01 +0100 Subject: [PATCH 1/4] Consolidate persistent state to BlockchainDataHandler struct --- lib/ain-evm/src/block.rs | 101 ++-------- lib/ain-evm/src/evm.rs | 2 +- lib/ain-evm/src/handler.rs | 29 ++- lib/ain-evm/src/lib.rs | 1 - lib/ain-evm/src/receipt.rs | 117 ++++-------- lib/ain-evm/src/runtime.rs | 6 +- lib/ain-evm/src/storage.rs | 74 -------- lib/ain-evm/src/{ => storage}/cache.rs | 96 +++++----- lib/ain-evm/src/storage/data_handler.rs | 179 ++++++++++++++++++ lib/ain-evm/src/storage/mod.rs | 125 +++++++++++++ lib/ain-evm/src/storage/traits.rs | 235 ++++++++++++++++++++++++ lib/ain-evm/src/traits.rs | 198 -------------------- lib/ain-grpc/src/rpc.rs | 19 +- 13 files changed, 659 insertions(+), 523 deletions(-) delete mode 100644 lib/ain-evm/src/storage.rs rename lib/ain-evm/src/{ => storage}/cache.rs (81%) create mode 100644 lib/ain-evm/src/storage/data_handler.rs create mode 100644 lib/ain-evm/src/storage/mod.rs create mode 100644 lib/ain-evm/src/storage/traits.rs diff --git a/lib/ain-evm/src/block.rs b/lib/ain-evm/src/block.rs index d8d3496fec..ed95487c9f 100644 --- a/lib/ain-evm/src/block.rs +++ b/lib/ain-evm/src/block.rs @@ -1,98 +1,27 @@ -use crate::traits::{PersistentState, PersistentStateError}; use ethereum::BlockAny; -use primitive_types::H256; -use std::collections::HashMap; -use std::error::Error; -use std::sync::{Arc, RwLock}; +use keccak_hash::H256; +use primitive_types::U256; +use std::sync::Arc; -pub static BLOCK_MAP_PATH: &str = "block_map.bin"; -pub static BLOCK_DATA_PATH: &str = "block_data.bin"; - -type BlockHashtoBlock = HashMap; -type Blocks = Vec; +use crate::storage::{traits::BlockStorage, Storage}; pub struct BlockHandler { - pub block_map: Arc>, - pub blocks: Arc>, -} - -impl PersistentState for BlockHashtoBlock {} - -impl PersistentState for Blocks {} - -impl Default for BlockHandler { - fn default() -> Self { - Self::new() - } + storage: Arc, } impl BlockHandler { - pub fn new() -> Self { - Self { - block_map: Arc::new(RwLock::new( - BlockHashtoBlock::load_from_disk(BLOCK_MAP_PATH).unwrap(), - )), - blocks: Arc::new(RwLock::new( - Blocks::load_from_disk(BLOCK_DATA_PATH).unwrap(), - )), - } - } - - pub fn connect_block(&self, block: BlockAny) { - let mut blocks = self.blocks.write().unwrap(); - blocks.push(block.clone()); - - let mut blockhash = self.block_map.write().unwrap(); - blockhash.insert(block.header.hash(), blocks.len() - 1); - } - - pub fn flush(&self) -> Result<(), PersistentStateError> { - self.block_map - .write() - .unwrap() - .save_to_disk(BLOCK_MAP_PATH)?; - self.blocks.write().unwrap().save_to_disk(BLOCK_DATA_PATH) + pub fn new(storage: Arc) -> Self { + Self { storage } } - - pub fn get_block_by_hash(&self, hash: H256) -> Result { - let block_map = self.block_map.read().unwrap(); - let block_number = *block_map - .get(&hash) - .ok_or(BlockHandlerError::BlockNotFound)?; - - let blocks = self.blocks.read().unwrap(); - let block = blocks - .get(block_number) - .ok_or(BlockHandlerError::BlockNotFound)? - .clone(); - - Ok(block) - } - - pub fn get_block_by_number(&self, count: usize) -> Result { - let blocks = self.blocks.read().unwrap(); - let block = blocks - .get(count) - .ok_or(BlockHandlerError::BlockNotFound)? - .clone(); - - Ok(block) + pub fn get_latest_block_and_number(&self) -> (H256, U256) { + self.storage + .get_latest_block() + .map(|latest_block| (latest_block.header.hash(), latest_block.header.number + 1)) + .unwrap_or((H256::default(), U256::zero())) } -} - -use std::fmt; - -#[derive(Debug)] -pub enum BlockHandlerError { - BlockNotFound, -} -impl fmt::Display for BlockHandlerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - BlockHandlerError::BlockNotFound => write!(f, "Block not found"), - } + pub fn connect_block(&self, block: BlockAny) { + self.storage.put_latest_block(block.clone()); + self.storage.put_block(block) } } - -impl Error for BlockHandlerError {} diff --git a/lib/ain-evm/src/evm.rs b/lib/ain-evm/src/evm.rs index b4f399f53d..1484c7cab9 100644 --- a/lib/ain-evm/src/evm.rs +++ b/lib/ain-evm/src/evm.rs @@ -1,4 +1,4 @@ -use crate::traits::{PersistentState, PersistentStateError}; +use crate::storage::traits::{PersistentState, PersistentStateError}; use crate::tx_queue::TransactionQueueMap; use crate::{executor::AinExecutor, traits::Executor, transaction::SignedTx}; use anyhow::anyhow; diff --git a/lib/ain-evm/src/handler.rs b/lib/ain-evm/src/handler.rs index 48ffd6438a..f80d71ada3 100644 --- a/lib/ain-evm/src/handler.rs +++ b/lib/ain-evm/src/handler.rs @@ -7,14 +7,15 @@ use crate::traits::Executor; use ethereum::{Block, BlockAny, PartialHeader, TransactionV2}; use evm::backend::MemoryBackend; -use primitive_types::{H160, H256, U256}; +use primitive_types::{H160, U256}; use std::error::Error; +use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; pub struct Handlers { pub evm: EVMHandler, pub block: BlockHandler, - pub storage: Storage, + pub storage: Arc, pub receipt: ReceiptHandler, } @@ -26,11 +27,12 @@ impl Default for Handlers { impl Handlers { pub fn new() -> Self { + let storage = Arc::new(Storage::new()); Self { evm: EVMHandler::new(), - block: BlockHandler::new(), - storage: Storage::new(), - receipt: ReceiptHandler::new(), + block: BlockHandler::new(Arc::clone(&storage)), + receipt: ReceiptHandler::new(Arc::clone(&storage)), + storage, } } @@ -72,12 +74,7 @@ impl Handlers { self.evm.tx_queues.remove(context); - let (parent_hash, number) = { - self.storage - .get_latest_block() - .map(|latest_block| (latest_block.header.hash(), latest_block.header.number + 1)) - .unwrap_or((H256::default(), U256::zero())) - }; + let (parent_hash, number) = self.block.get_latest_block_and_number(); let mut block = Block::new( PartialHeader { @@ -102,22 +99,20 @@ impl Handlers { Vec::new(), ); - let receipts_root = self.receipt.generate_receipts( + let receipts = self.receipt.generate_receipts( successful_transactions, failed_transactions.clone(), block.header.hash(), block.header.number, ); - block.header.receipts_root = receipts_root; - - self.block.connect_block(block.clone()); + block.header.receipts_root = self.receipt.get_receipt_root(&receipts); if update_state { let mut state = self.evm.state.write().unwrap(); *state = executor.backend().state().clone(); - self.storage.put_latest_block(block.clone()); - self.storage.put_block(block.clone()); + self.block.connect_block(block.clone()); + self.receipt.put_receipts(receipts); } Ok(( diff --git a/lib/ain-evm/src/lib.rs b/lib/ain-evm/src/lib.rs index 905117c414..ea5488ff75 100644 --- a/lib/ain-evm/src/lib.rs +++ b/lib/ain-evm/src/lib.rs @@ -1,5 +1,4 @@ mod block; -mod cache; mod ecrecover; pub mod evm; pub mod executor; diff --git a/lib/ain-evm/src/receipt.rs b/lib/ain-evm/src/receipt.rs index 399a301715..149f3c0e2a 100644 --- a/lib/ain-evm/src/receipt.rs +++ b/lib/ain-evm/src/receipt.rs @@ -1,4 +1,4 @@ -use crate::traits::{PersistentState, PersistentStateError}; +use crate::storage::{traits::ReceiptStorage, Storage}; use crate::transaction::SignedTx; use ethereum::{EIP658ReceiptData, EnvelopedEncodable, ReceiptV3}; use primitive_types::{H160, H256, U256}; @@ -7,13 +7,9 @@ use ethereum::util::ordered_trie_root; use keccak_hash::keccak; use rlp::RlpStream; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::error::Error; -use std::sync::{Arc, RwLock}; +use std::sync::Arc; -pub static RECEIPT_MAP_PATH: &str = "receipt_map.bin"; - -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, Debug)] pub struct Receipt { pub tx_hash: H256, pub receipt: ReceiptV3, @@ -26,18 +22,8 @@ pub struct Receipt { pub contract_address: Option, } -type TransactionHashToReceipt = HashMap; - pub struct ReceiptHandler { - pub transaction_map: Arc>, -} - -impl PersistentState for TransactionHashToReceipt {} - -impl Default for ReceiptHandler { - fn default() -> Self { - Self::new() - } + storage: Arc, } fn get_contract_address(to: &Option, sender: &H160, nonce: &U256) -> Option { @@ -53,29 +39,16 @@ fn get_contract_address(to: &Option, sender: &H160, nonce: &U256) -> Optio } impl ReceiptHandler { - pub fn new() -> Self { - Self { - transaction_map: Arc::new(RwLock::new( - TransactionHashToReceipt::load_from_disk(RECEIPT_MAP_PATH).unwrap(), - )), - } + pub fn new(storage: Arc) -> Self { + Self { storage } } - pub fn flush(&self) -> Result<(), PersistentStateError> { - self.transaction_map - .write() - .unwrap() - .save_to_disk(RECEIPT_MAP_PATH) - } - - pub fn get_receipt(&self, tx: H256) -> Result { - let map = self.transaction_map.read().unwrap(); - - let receipt = map - .get(&tx) - .ok_or(ReceiptHandlerError::ReceiptNotFound)? - .clone(); - Ok(receipt) + pub fn get_receipt_root(&self, receipts: &[Receipt]) -> H256 { + ordered_trie_root( + receipts + .iter() + .map(|r| EnvelopedEncodable::encode(&r.receipt).freeze()), + ) } pub fn generate_receipts( @@ -84,14 +57,12 @@ impl ReceiptHandler { failed: Vec, block_hash: H256, block_number: U256, - ) -> H256 { - let mut map = self.transaction_map.write().unwrap(); + ) -> Vec { let mut receipts = Vec::new(); let mut index = 0; - for transaction in successful { - let tv2 = transaction.clone().transaction; + for signed_tx in successful { let receipt = Receipt { receipt: ReceiptV3::EIP1559(EIP658ReceiptData { status_code: 1, @@ -101,25 +72,22 @@ impl ReceiptHandler { }), block_hash, block_number, - tx_hash: tv2.hash(), - from: transaction.sender, - to: transaction.to(), + tx_hash: signed_tx.transaction.hash(), + from: signed_tx.sender, + to: signed_tx.to(), tx_index: index, - tx_type: EnvelopedEncodable::type_id(&tv2).unwrap_or_default(), + tx_type: EnvelopedEncodable::type_id(&signed_tx.transaction).unwrap_or_default(), contract_address: get_contract_address( - &transaction.to(), - &transaction.sender, - &transaction.nonce(), + &signed_tx.to(), + &signed_tx.sender, + &signed_tx.nonce(), ), }; - - map.insert(tv2.hash(), receipt.clone()); receipts.push(receipt); index += 1; } - for transaction in failed { - let tv2 = transaction.clone().transaction; + for signed_tx in failed { let receipt = Receipt { receipt: ReceiptV3::EIP1559(EIP658ReceiptData { status_code: 0, @@ -129,50 +97,29 @@ impl ReceiptHandler { }), block_hash, block_number, - tx_hash: tv2.hash(), - from: transaction.sender, - to: transaction.to(), + tx_hash: signed_tx.transaction.hash(), + from: signed_tx.sender, + to: signed_tx.to(), tx_index: index, contract_address: get_contract_address( - &transaction.to(), - &transaction.sender, - &transaction.nonce(), + &signed_tx.to(), + &signed_tx.sender, + &signed_tx.nonce(), ), - tx_type: EnvelopedEncodable::type_id(&tv2).unwrap(), + tx_type: EnvelopedEncodable::type_id(&signed_tx.transaction).unwrap(), }; - map.insert(tv2.hash(), receipt.clone()); receipts.push(receipt); index += 1; } - - let root = ordered_trie_root( - receipts - .iter() - .map(|r| EnvelopedEncodable::encode(&r.receipt).freeze()), - ); - - return root; + receipts } -} - -use std::fmt; - -#[derive(Debug)] -pub enum ReceiptHandlerError { - ReceiptNotFound, -} -impl fmt::Display for ReceiptHandlerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - ReceiptHandlerError::ReceiptNotFound => write!(f, "Receipt not found"), - } + pub fn put_receipts(&self, receipts: Vec) { + self.storage.put_receipts(receipts) } } -impl Error for ReceiptHandlerError {} - #[cfg(test)] mod test { use crate::receipt::get_contract_address; diff --git a/lib/ain-evm/src/runtime.rs b/lib/ain-evm/src/runtime.rs index da41e04ba0..f7091236e0 100644 --- a/lib/ain-evm/src/runtime.rs +++ b/lib/ain-evm/src/runtime.rs @@ -1,4 +1,5 @@ use crate::handler::Handlers; +use crate::storage::traits::FlushableStorage; use jsonrpsee_http_server::HttpServerHandle; use std::sync::{Arc, Mutex}; @@ -59,10 +60,9 @@ impl Runtime { .evm .flush() .expect("Could not flush evm state"); - self.handlers.block.flush().expect("Could not flush blocks"); self.handlers - .receipt + .storage .flush() - .expect("Could not flush TX receipts"); + .expect("Could not flush storage"); } } diff --git a/lib/ain-evm/src/storage.rs b/lib/ain-evm/src/storage.rs deleted file mode 100644 index 53731b6466..0000000000 --- a/lib/ain-evm/src/storage.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::cache::Cache; -use ethereum::{BlockAny, TransactionV2}; -use primitive_types::{H256, U256}; - -#[derive(Debug)] -pub struct Storage { - cache: Cache, -} - -// TODO : Add DB and pull from DB when cache miss -impl Default for Storage { - fn default() -> Self { - Self::new() - } -} - -impl Storage { - pub fn new() -> Self { - Self { - cache: Cache::new(None), - } - } -} - -// Block storage -impl Storage { - pub fn get_block_by_number(&self, number: &U256) -> Option { - self.cache.get_block_by_number(number) - } - - pub fn get_block_by_hash(&self, block_hash: &H256) -> Option { - self.cache.get_block_by_hash(block_hash) - } - - pub fn put_block(&self, block: BlockAny) { - self.cache.put_block(block) - } -} - -// Transaction storage -impl Storage { - pub fn get_transaction_by_hash(&self, hash: H256) -> Option { - self.cache.get_transaction_by_hash(&hash) - } - - pub fn get_transaction_by_block_hash_and_index( - &self, - hash: H256, - index: usize, - ) -> Option { - self.cache - .get_transaction_by_block_hash_and_index(&hash, index) - } - - pub fn get_transaction_by_block_number_and_index( - &self, - hash: U256, - index: usize, - ) -> Option { - self.cache - .get_transaction_by_block_number_and_index(&hash, index) - } -} - -// Latest block storage -impl Storage { - pub fn get_latest_block(&self) -> Option { - self.cache.get_latest_block() - } - - pub fn put_latest_block(&self, block: BlockAny) { - self.cache.put_latest_block(block) - } -} diff --git a/lib/ain-evm/src/cache.rs b/lib/ain-evm/src/storage/cache.rs similarity index 81% rename from lib/ain-evm/src/cache.rs rename to lib/ain-evm/src/storage/cache.rs index bdef665a1d..79578d63f6 100644 --- a/lib/ain-evm/src/cache.rs +++ b/lib/ain-evm/src/storage/cache.rs @@ -5,6 +5,8 @@ use lru::LruCache; use primitive_types::{H256, U256}; use std::borrow::ToOwned; +use super::traits::{BlockStorage, TransactionStorage}; + #[derive(Debug)] pub struct Cache { transactions: RwLock>, @@ -32,8 +34,48 @@ impl Cache { } } -impl Cache { - pub fn extend_transactions_from_block(&self, block: &BlockAny) { +impl BlockStorage for Cache { + fn get_block_by_number(&self, number: &U256) -> Option { + self.blocks + .write() + .unwrap() + .get(number) + .map(ToOwned::to_owned) + } + + fn get_block_by_hash(&self, block_hash: &H256) -> Option { + self.block_hashes + .write() + .unwrap() + .get(block_hash) + .and_then(|block_number| self.get_block_by_number(block_number)) + } + + fn put_block(&self, block: BlockAny) { + self.extend_transactions_from_block(&block); + + let block_number = block.header.number; + let hash = block.header.hash(); + self.blocks.write().unwrap().put(block_number, block); + self.block_hashes.write().unwrap().put(hash, block_number); + } + + fn get_latest_block(&self) -> Option { + self.latest_block + .read() + .unwrap() + .as_ref() + .map(ToOwned::to_owned) + } + + fn put_latest_block(&self, block: BlockAny) { + let mut cache = self.latest_block.write().unwrap(); + *cache = Some(block); + } +} + +impl TransactionStorage for Cache { + fn extend_transactions_from_block(&self, block: &BlockAny) { let mut cache = self.transactions.write().unwrap(); for transaction in &block.transactions { @@ -42,7 +84,7 @@ impl Cache { } } - pub fn get_transaction_by_hash(&self, hash: &H256) -> Option { + fn get_transaction_by_hash(&self, hash: &H256) -> Option { self.transactions .write() .unwrap() @@ -50,7 +92,7 @@ impl Cache { .map(ToOwned::to_owned) } - pub fn get_transaction_by_block_hash_and_index( + fn get_transaction_by_block_hash_and_index( &self, block_hash: &H256, index: usize, @@ -64,7 +106,7 @@ impl Cache { }) } - pub fn get_transaction_by_block_number_and_index( + fn get_transaction_by_block_number_and_index( &self, block_number: &U256, index: usize, @@ -78,47 +120,3 @@ impl Cache { .map(ToOwned::to_owned) } } - -// Block impl -impl Cache { - pub fn get_block_by_number(&self, number: &U256) -> Option { - self.blocks - .write() - .unwrap() - .get(number) - .map(ToOwned::to_owned) - } - - pub fn get_block_by_hash(&self, block_hash: &H256) -> Option { - self.block_hashes - .write() - .unwrap() - .get(block_hash) - .and_then(|block_number| self.get_block_by_number(block_number)) - } - - pub fn put_block(&self, block: BlockAny) { - self.extend_transactions_from_block(&block); - - let block_number = block.header.number; - let hash = block.header.hash(); - self.blocks.write().unwrap().put(block_number, block); - self.block_hashes.write().unwrap().put(hash, block_number); - } -} - -// Latest block impl -impl Cache { - pub fn get_latest_block(&self) -> Option { - self.latest_block - .read() - .unwrap() - .as_ref() - .map(ToOwned::to_owned) - } - - pub fn put_latest_block(&self, block: BlockAny) { - let mut cache = self.latest_block.write().unwrap(); - *cache = Some(block); - } -} diff --git a/lib/ain-evm/src/storage/data_handler.rs b/lib/ain-evm/src/storage/data_handler.rs new file mode 100644 index 0000000000..ea299f0458 --- /dev/null +++ b/lib/ain-evm/src/storage/data_handler.rs @@ -0,0 +1,179 @@ +use std::{collections::HashMap, sync::RwLock}; + +use ethereum::{BlockAny, TransactionV2}; +use primitive_types::{H256, U256}; +use std::borrow::ToOwned; + +use crate::receipt::Receipt; + +use super::traits::{ + BlockStorage, FlushableStorage, PersistentState, PersistentStateError, ReceiptStorage, + TransactionStorage, +}; + +pub static BLOCK_MAP_PATH: &str = "block_map.bin"; +pub static BLOCK_DATA_PATH: &str = "block_data.bin"; +pub static LATEST_BLOCK_DATA_PATH: &str = "latest_block_data.bin"; +pub static RECEIPT_MAP_PATH: &str = "receipt_map.bin"; +// pub static TRANSACTION_DATA_PATH: &str = "transaction_data.bin"; + +type BlockHashtoBlock = HashMap; +type Blocks = HashMap; +type TxHashToTx = HashMap; +type LatestBlockNumber = U256; +type TransactionHashToReceipt = HashMap; + +impl PersistentState for BlockHashtoBlock {} +impl PersistentState for Blocks {} +impl PersistentState for LatestBlockNumber {} +impl PersistentState for TransactionHashToReceipt {} + +#[derive(Debug)] +pub struct BlockchainDataHandler { + // Improvements: Add transaction_map behind feature flag -txindex or equivalent + transactions: RwLock, + + receipts: RwLock, + + block_map: RwLock, + blocks: RwLock, + latest_block_number: RwLock>, +} + +impl BlockchainDataHandler { + pub fn new() -> Self { + let blocks = Blocks::load_from_disk(BLOCK_DATA_PATH).expect("Error loading blocks data"); + BlockchainDataHandler { + transactions: RwLock::new(HashMap::new()), + block_map: RwLock::new( + BlockHashtoBlock::load_from_disk(BLOCK_MAP_PATH) + .expect("Error loading block_map data"), + ), + latest_block_number: RwLock::new( + LatestBlockNumber::load_from_disk(LATEST_BLOCK_DATA_PATH).ok(), + ), + blocks: RwLock::new(blocks), + receipts: RwLock::new( + TransactionHashToReceipt::load_from_disk(RECEIPT_MAP_PATH) + .expect("Error loading receipts data"), + ), + } + } +} + +impl TransactionStorage for BlockchainDataHandler { + // TODO: Feature flag + fn extend_transactions_from_block(&self, block: &BlockAny) { + let mut transactions = self.transactions.write().unwrap(); + + for transaction in &block.transactions { + let hash = transaction.hash(); + transactions.insert(hash, transaction.clone()); + } + } + + fn get_transaction_by_hash(&self, _hash: &H256) -> Option { + // TODO: Feature flag + None // Unimplement without tx index + } + + fn get_transaction_by_block_hash_and_index( + &self, + block_hash: &H256, + index: usize, + ) -> Option { + self.block_map + .write() + .unwrap() + .get(block_hash) + .and_then(|block_number| { + self.get_transaction_by_block_number_and_index(block_number, index) + }) + } + + fn get_transaction_by_block_number_and_index( + &self, + block_number: &U256, + index: usize, + ) -> Option { + self.blocks + .write() + .unwrap() + .get(block_number)? + .transactions + .get(index) + .map(ToOwned::to_owned) + } +} + +impl BlockStorage for BlockchainDataHandler { + fn get_block_by_number(&self, number: &U256) -> Option { + self.blocks + .write() + .unwrap() + .get(number) + .map(ToOwned::to_owned) + } + + fn get_block_by_hash(&self, block_hash: &H256) -> Option { + self.block_map + .write() + .unwrap() + .get(block_hash) + .and_then(|block_number| self.get_block_by_number(block_number)) + } + + fn put_block(&self, block: BlockAny) { + self.extend_transactions_from_block(&block); + + let block_number = block.header.number; + let hash = block.header.hash(); + self.blocks.write().unwrap().insert(block_number, block); + self.block_map.write().unwrap().insert(hash, block_number); + } + + fn get_latest_block(&self) -> Option { + self.latest_block_number + .read() + .unwrap() + .as_ref() + .and_then(|number| self.get_block_by_number(number)) + } + + fn put_latest_block(&self, block: BlockAny) { + let mut latest_block_number = self.latest_block_number.write().unwrap(); + *latest_block_number = Some(block.header.number); + } +} + +impl ReceiptStorage for BlockchainDataHandler { + fn get_receipt(&self, tx: &H256) -> Option { + self.receipts.read().unwrap().get(tx).map(ToOwned::to_owned) + } + + fn put_receipts(&self, receipts: Vec) { + let mut receipt_map = self.receipts.write().unwrap(); + for receipt in receipts { + receipt_map.insert(receipt.tx_hash, receipt); + } + } +} + +impl FlushableStorage for BlockchainDataHandler { + fn flush(&self) -> Result<(), PersistentStateError> { + self.block_map + .write() + .unwrap() + .save_to_disk(BLOCK_MAP_PATH)?; + self.blocks.write().unwrap().save_to_disk(BLOCK_DATA_PATH)?; + self.latest_block_number + .write() + .unwrap() + .unwrap_or_default() + .save_to_disk(LATEST_BLOCK_DATA_PATH)?; + self.receipts + .write() + .unwrap() + .save_to_disk(RECEIPT_MAP_PATH) + } +} diff --git a/lib/ain-evm/src/storage/mod.rs b/lib/ain-evm/src/storage/mod.rs new file mode 100644 index 0000000000..360f615456 --- /dev/null +++ b/lib/ain-evm/src/storage/mod.rs @@ -0,0 +1,125 @@ +mod cache; +mod data_handler; +pub mod traits; + +use ethereum::{BlockAny, TransactionV2}; +use primitive_types::{H256, U256}; + +use crate::receipt::Receipt; + +use self::{ + cache::Cache, + data_handler::BlockchainDataHandler, + traits::{ + BlockStorage, FlushableStorage, PersistentStateError, ReceiptStorage, TransactionStorage, + }, +}; + +#[derive(Debug)] +pub struct Storage { + cache: Cache, + blockchain_data_handler: BlockchainDataHandler, +} + +impl Default for Storage { + fn default() -> Self { + Self::new() + } +} + +impl Storage { + pub fn new() -> Self { + Self { + cache: Cache::new(None), + blockchain_data_handler: BlockchainDataHandler::new(), + } + } +} + +impl BlockStorage for Storage { + fn get_block_by_number(&self, number: &U256) -> Option { + self.cache + .get_block_by_number(number) + .or_else(|| self.blockchain_data_handler.get_block_by_number(number)) + } + + fn get_block_by_hash(&self, block_hash: &H256) -> Option { + self.cache + .get_block_by_hash(block_hash) + .or_else(|| self.blockchain_data_handler.get_block_by_hash(block_hash)) + } + + fn put_block(&self, block: BlockAny) { + self.cache.put_block(block.clone()); + self.blockchain_data_handler.put_block(block) + } + + fn get_latest_block(&self) -> Option { + self.cache + .get_latest_block() + .or_else(|| self.blockchain_data_handler.get_latest_block()) + } + + fn put_latest_block(&self, block: BlockAny) { + self.cache.put_latest_block(block.clone()); + self.blockchain_data_handler.put_latest_block(block) + } +} + +impl TransactionStorage for Storage { + fn extend_transactions_from_block(&self, block: &BlockAny) { + // Feature flag + self.cache.extend_transactions_from_block(block); + + self.blockchain_data_handler + .extend_transactions_from_block(block); + } + + fn get_transaction_by_hash(&self, hash: &H256) -> Option { + self.cache + .get_transaction_by_hash(hash) + .or_else(|| self.blockchain_data_handler.get_transaction_by_hash(hash)) + } + + fn get_transaction_by_block_hash_and_index( + &self, + hash: &H256, + index: usize, + ) -> Option { + self.cache + .get_transaction_by_block_hash_and_index(hash, index) + .or_else(|| { + self.blockchain_data_handler + .get_transaction_by_block_hash_and_index(hash, index) + }) + } + + fn get_transaction_by_block_number_and_index( + &self, + number: &U256, + index: usize, + ) -> Option { + self.cache + .get_transaction_by_block_number_and_index(number, index) + .or_else(|| { + self.blockchain_data_handler + .get_transaction_by_block_number_and_index(number, index) + }) + } +} + +impl ReceiptStorage for Storage { + fn get_receipt(&self, tx: &H256) -> Option { + self.blockchain_data_handler.get_receipt(tx) + } + + fn put_receipts(&self, receipts: Vec) { + self.blockchain_data_handler.put_receipts(receipts) + } +} + +impl FlushableStorage for Storage { + fn flush(&self) -> Result<(), PersistentStateError> { + self.blockchain_data_handler.flush() + } +} diff --git a/lib/ain-evm/src/storage/traits.rs b/lib/ain-evm/src/storage/traits.rs new file mode 100644 index 0000000000..0d5163ebab --- /dev/null +++ b/lib/ain-evm/src/storage/traits.rs @@ -0,0 +1,235 @@ +use crate::receipt::Receipt; +use ethereum::BlockAny; +use ethereum::TransactionV2; +use keccak_hash::H256; +use primitive_types::U256; +use std::fs::File; + +use std::fmt; +use std::io; +use std::io::Write; +use std::path::Path; +use std::path::PathBuf; + +pub trait BlockStorage { + fn get_block_by_number(&self, number: &U256) -> Option; + fn get_block_by_hash(&self, block_hash: &H256) -> Option; + fn put_block(&self, block: BlockAny); + fn get_latest_block(&self) -> Option; + fn put_latest_block(&self, block: BlockAny); +} + +pub trait TransactionStorage { + fn extend_transactions_from_block(&self, block: &BlockAny); + fn get_transaction_by_hash(&self, hash: &H256) -> Option; + fn get_transaction_by_block_hash_and_index( + &self, + hash: &H256, + index: usize, + ) -> Option; + fn get_transaction_by_block_number_and_index( + &self, + number: &U256, + index: usize, + ) -> Option; +} + +pub trait ReceiptStorage { + fn get_receipt(&self, tx: &H256) -> Option; + fn put_receipts(&self, receipts: Vec); +} + +pub trait FlushableStorage { + fn flush(&self) -> Result<(), PersistentStateError>; +} + +pub trait PersistentState { + fn save_to_disk(&self, file_path: &str) -> Result<(), PersistentStateError> + where + Self: serde::ser::Serialize, + { + // Automatically resolves from datadir for now + let path = match ain_cpp_imports::get_datadir() { + Ok(path) => { + let path = PathBuf::from(path).join("evm"); + if !path.exists() { + std::fs::create_dir(&path).expect("Error creating `evm` dir") + } + path.join(file_path) + } + _ => PathBuf::from(file_path), + }; + + let serialized_state = bincode::serialize(self)?; + let mut file = File::create(path)?; + file.write_all(&serialized_state)?; + Ok(()) + } + + fn load_from_disk(file_path: &str) -> Result + where + Self: Sized + serde::de::DeserializeOwned + Default, + { + // Automatically resolves from datadir for now + let path = match ain_cpp_imports::get_datadir() { + Ok(path) => PathBuf::from(path).join("evm").join(file_path), + _ => PathBuf::from(file_path), + }; + + if Path::new(&path).exists() { + let file = File::open(path)?; + let new_state: Self = bincode::deserialize_from(file)?; + Ok(new_state) + } else { + Ok(Self::default()) + } + } +} + +#[derive(Debug)] +pub enum PersistentStateError { + IoError(io::Error), + BincodeError(bincode::Error), +} + +impl fmt::Display for PersistentStateError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + PersistentStateError::IoError(err) => write!(f, "IO error: {}", err), + PersistentStateError::BincodeError(err) => write!(f, "Bincode error: {}", err), + } + } +} + +impl std::error::Error for PersistentStateError {} + +impl From for PersistentStateError { + fn from(error: io::Error) -> Self { + PersistentStateError::IoError(error) + } +} + +impl From for PersistentStateError { + fn from(error: bincode::Error) -> Self { + PersistentStateError::BincodeError(error) + } +} + +#[cfg(test_off)] +mod tests { + use crate::evm::EVMState; + use crate::traits::PersistentState; + use evm::backend::MemoryAccount; + use primitive_types::{H160, H256, U256}; + use std::collections::BTreeMap; + use std::fs::File; + use std::io::Write; + + fn create_account( + nonce: U256, + balance: U256, + code: Vec, + storage: BTreeMap, + ) -> MemoryAccount { + MemoryAccount { + nonce, + balance, + code, + storage, + } + } + + #[test] + fn test_load_non_existent_file() { + let state = EVMState::load_from_disk("non_existent_file.bin").unwrap(); + assert_eq!(state, EVMState::default()); + } + + #[test] + fn test_empty_file() { + let state = BTreeMap::new(); + let path = "empty_test.bin"; + + state.save_to_disk(path).unwrap(); + + let new_state = EVMState::load_from_disk(path).unwrap(); + + assert_eq!(state, new_state); + } + + #[test] + fn test_invalid_file_format() { + let invalid_data = b"invalid_data"; + let path = "invalid_file_format.bin"; + + let mut file = File::create(path).unwrap(); + file.write_all(invalid_data).unwrap(); + + let state = EVMState::load_from_disk(path); + + assert!(state.is_err()); + } + + #[test] + fn test_save_and_load_empty_backend() { + let path = "test_empty_backend.bin"; + let state = BTreeMap::new(); + + state.save_to_disk(path).unwrap(); + + let loaded_backend = EVMState::load_from_disk(path).unwrap(); + + assert_eq!(state, loaded_backend); + } + + #[test] + fn test_save_and_load_single_account() { + let path = "test_single_account.bin"; + let mut state = BTreeMap::new(); + + let account = create_account( + U256::from(1), + U256::from(1000), + vec![1, 2, 3], + BTreeMap::new(), + ); + let address = H160::from_low_u64_be(1); + state.insert(address, account); + + state.save_to_disk(path).unwrap(); + + let loaded_backend = EVMState::load_from_disk(path).unwrap(); + + assert_eq!(state, loaded_backend); + } + + #[test] + fn test_save_and_load_multiple_accounts() { + let path = "test_multiple_accounts.bin"; + let mut state = BTreeMap::new(); + + let account1 = create_account( + U256::from(1), + U256::from(1000), + vec![1, 2, 3], + BTreeMap::new(), + ); + let address1 = H160::from_low_u64_be(1); + state.insert(address1, account1); + + let account2 = create_account( + U256::from(2), + U256::from(2000), + vec![4, 5, 6], + BTreeMap::new(), + ); + let address2 = H160::from_low_u64_be(2); + state.insert(address2, account2); + + state.save_to_disk(path).unwrap(); + + let loaded_backend = EVMState::load_from_disk(path).unwrap(); + + assert_eq!(state, loaded_backend); + } +} diff --git a/lib/ain-evm/src/traits.rs b/lib/ain-evm/src/traits.rs index 264ae5c664..a9042b81ec 100644 --- a/lib/ain-evm/src/traits.rs +++ b/lib/ain-evm/src/traits.rs @@ -2,7 +2,6 @@ use crate::{executor::TxResponse, transaction::SignedTx}; use ethereum::AccessList; use evm::Config; use primitive_types::{H160, U256}; -use std::fs::File; pub trait Executor { const CONFIG: Config = Config::london(); @@ -20,200 +19,3 @@ pub trait Executor { fn exec(&mut self, tx: &SignedTx) -> TxResponse; } - -pub trait PersistentState { - fn save_to_disk(&self, file_path: &str) -> Result<(), PersistentStateError> - where - Self: serde::ser::Serialize, - { - // Automatically resolves from datadir for now - let path = match ain_cpp_imports::get_datadir() { - Ok(path) => { - let path = PathBuf::from(path).join("evm"); - if !path.exists() { - std::fs::create_dir(&path).expect("Error creating `evm` dir") - } - path.join(file_path) - } - _ => PathBuf::from(file_path), - }; - - let serialized_state = bincode::serialize(self)?; - let mut file = File::create(path)?; - file.write_all(&serialized_state)?; - Ok(()) - } - - fn load_from_disk(file_path: &str) -> Result - where - Self: Sized + serde::de::DeserializeOwned + Default, - { - // Automatically resolves from datadir for now - let path = match ain_cpp_imports::get_datadir() { - Ok(path) => PathBuf::from(path).join("evm").join(file_path), - _ => PathBuf::from(file_path), - }; - - if Path::new(&path).exists() { - let file = File::open(path)?; - let new_state: Self = bincode::deserialize_from(file)?; - Ok(new_state) - } else { - Ok(Self::default()) - } - } -} - -use std::fmt; -use std::io; -use std::io::Write; -use std::path::Path; -use std::path::PathBuf; - -#[derive(Debug)] -pub enum PersistentStateError { - IoError(io::Error), - BincodeError(bincode::Error), -} - -impl fmt::Display for PersistentStateError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - PersistentStateError::IoError(err) => write!(f, "IO error: {}", err), - PersistentStateError::BincodeError(err) => write!(f, "Bincode error: {}", err), - } - } -} - -impl std::error::Error for PersistentStateError {} - -impl From for PersistentStateError { - fn from(error: io::Error) -> Self { - PersistentStateError::IoError(error) - } -} - -impl From for PersistentStateError { - fn from(error: bincode::Error) -> Self { - PersistentStateError::BincodeError(error) - } -} - -#[cfg(test_off)] -mod tests { - use crate::evm::EVMState; - use crate::traits::PersistentState; - use evm::backend::MemoryAccount; - use primitive_types::{H160, H256, U256}; - use std::collections::BTreeMap; - use std::fs::File; - use std::io::Write; - - fn create_account( - nonce: U256, - balance: U256, - code: Vec, - storage: BTreeMap, - ) -> MemoryAccount { - MemoryAccount { - nonce, - balance, - code, - storage, - } - } - - #[test] - fn test_load_non_existent_file() { - let state = EVMState::load_from_disk("non_existent_file.bin").unwrap(); - assert_eq!(state, EVMState::default()); - } - - #[test] - fn test_empty_file() { - let state = BTreeMap::new(); - let path = "empty_test.bin"; - - state.save_to_disk(path).unwrap(); - - let new_state = EVMState::load_from_disk(path).unwrap(); - - assert_eq!(state, new_state); - } - - #[test] - fn test_invalid_file_format() { - let invalid_data = b"invalid_data"; - let path = "invalid_file_format.bin"; - - let mut file = File::create(path).unwrap(); - file.write_all(invalid_data).unwrap(); - - let state = EVMState::load_from_disk(path); - - assert!(state.is_err()); - } - - #[test] - fn test_save_and_load_empty_backend() { - let path = "test_empty_backend.bin"; - let state = BTreeMap::new(); - - state.save_to_disk(path).unwrap(); - - let loaded_backend = EVMState::load_from_disk(path).unwrap(); - - assert_eq!(state, loaded_backend); - } - - #[test] - fn test_save_and_load_single_account() { - let path = "test_single_account.bin"; - let mut state = BTreeMap::new(); - - let account = create_account( - U256::from(1), - U256::from(1000), - vec![1, 2, 3], - BTreeMap::new(), - ); - let address = H160::from_low_u64_be(1); - state.insert(address, account); - - state.save_to_disk(path).unwrap(); - - let loaded_backend = EVMState::load_from_disk(path).unwrap(); - - assert_eq!(state, loaded_backend); - } - - #[test] - fn test_save_and_load_multiple_accounts() { - let path = "test_multiple_accounts.bin"; - let mut state = BTreeMap::new(); - - let account1 = create_account( - U256::from(1), - U256::from(1000), - vec![1, 2, 3], - BTreeMap::new(), - ); - let address1 = H160::from_low_u64_be(1); - state.insert(address1, account1); - - let account2 = create_account( - U256::from(2), - U256::from(2000), - vec![4, 5, 6], - BTreeMap::new(), - ); - let address2 = H160::from_low_u64_be(2); - state.insert(address2, account2); - - state.save_to_disk(path).unwrap(); - - let loaded_backend = EVMState::load_from_disk(path).unwrap(); - - assert_eq!(state, loaded_backend); - } -} diff --git a/lib/ain-grpc/src/rpc.rs b/lib/ain-grpc/src/rpc.rs index a14f329cba..0aaa5b9d16 100644 --- a/lib/ain-grpc/src/rpc.rs +++ b/lib/ain-grpc/src/rpc.rs @@ -6,6 +6,7 @@ use crate::receipt::ReceiptResult; use ain_evm::evm::EVMState; use ain_evm::handler::Handlers; +use ain_evm::storage::traits::{BlockStorage, ReceiptStorage, TransactionStorage}; use ain_evm::transaction::{SignedTx, TransactionError}; use jsonrpsee::core::{Error, RpcResult}; use jsonrpsee::proc_macros::rpc; @@ -212,7 +213,7 @@ impl MetachainRPCServer for MetachainRPCModule { fn get_transaction_by_hash(&self, hash: H256) -> RpcResult> { self.handler .storage - .get_transaction_by_hash(hash) + .get_transaction_by_hash(&hash) .map_or(Ok(None), |tx| { let transaction_info = tx .try_into() @@ -228,7 +229,7 @@ impl MetachainRPCServer for MetachainRPCModule { ) -> RpcResult> { self.handler .storage - .get_transaction_by_block_hash_and_index(hash, index) + .get_transaction_by_block_hash_and_index(&hash, index) .map_or(Ok(None), |tx| { let transaction_info = tx .try_into() @@ -244,7 +245,7 @@ impl MetachainRPCServer for MetachainRPCModule { ) -> RpcResult> { self.handler .storage - .get_transaction_by_block_number_and_index(number, index) + .get_transaction_by_block_number_and_index(&number, index) .map_or(Ok(None), |tx| { let transaction_info = tx .try_into() @@ -255,8 +256,8 @@ impl MetachainRPCServer for MetachainRPCModule { fn get_block_transaction_count_by_hash(&self, hash: H256) -> RpcResult { self.handler - .block - .get_block_by_hash(hash) + .storage + .get_block_by_hash(&hash) .map_or(Ok(0), |b| Ok(b.transactions.len())) } @@ -265,8 +266,8 @@ impl MetachainRPCServer for MetachainRPCModule { BlockNumber::Pending => Ok(0), // TODO get from mempool ? BlockNumber::Num(number) if number > 0 => self .handler - .block - .get_block_by_number(number as usize) + .storage + .get_block_by_number(&U256::from(number)) .map_or(Ok(0), |b| Ok(b.transactions.len())), BlockNumber::Num(_) => Err(Error::Custom(String::from("Block number should be >= 0."))), BlockNumber::Latest => self @@ -348,8 +349,8 @@ impl MetachainRPCServer for MetachainRPCModule { fn get_receipt(&self, hash: H256) -> RpcResult> { self.handler - .receipt - .get_receipt(hash) + .storage + .get_receipt(&hash) .map_or(Ok(None), |receipt| Ok(Some(ReceiptResult::from(receipt)))) } } From 60c12afe9223efa764d335f2ed038029b37dfb30 Mon Sep 17 00:00:00 2001 From: jouzo Date: Thu, 27 Apr 2023 10:23:34 +0100 Subject: [PATCH 2/4] More descriptive naming --- lib/ain-evm/src/block.rs | 3 ++- lib/ain-evm/src/handler.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/ain-evm/src/block.rs b/lib/ain-evm/src/block.rs index ed95487c9f..0fe8cfaec8 100644 --- a/lib/ain-evm/src/block.rs +++ b/lib/ain-evm/src/block.rs @@ -13,7 +13,8 @@ impl BlockHandler { pub fn new(storage: Arc) -> Self { Self { storage } } - pub fn get_latest_block_and_number(&self) -> (H256, U256) { + + pub fn get_latest_block_hash_and_number(&self) -> (H256, U256) { self.storage .get_latest_block() .map(|latest_block| (latest_block.header.hash(), latest_block.header.number + 1)) diff --git a/lib/ain-evm/src/handler.rs b/lib/ain-evm/src/handler.rs index f80d71ada3..fc7bbc2b98 100644 --- a/lib/ain-evm/src/handler.rs +++ b/lib/ain-evm/src/handler.rs @@ -74,7 +74,7 @@ impl Handlers { self.evm.tx_queues.remove(context); - let (parent_hash, number) = self.block.get_latest_block_and_number(); + let (parent_hash, number) = self.block.get_latest_block_hash_and_number(); let mut block = Block::new( PartialHeader { From f456807fbd6a77574f112f9cc5ae0720b8c5b8e1 Mon Sep 17 00:00:00 2001 From: jouzo Date: Thu, 27 Apr 2023 10:52:48 +0100 Subject: [PATCH 3/4] Update cache after cache miss --- lib/ain-evm/src/block.rs | 4 +- lib/ain-evm/src/handler.rs | 2 +- lib/ain-evm/src/storage/cache.rs | 20 +++++-- lib/ain-evm/src/storage/data_handler.rs | 18 +++++-- lib/ain-evm/src/storage/mod.rs | 71 ++++++++++++++++++------- lib/ain-evm/src/storage/traits.rs | 5 +- 6 files changed, 86 insertions(+), 34 deletions(-) diff --git a/lib/ain-evm/src/block.rs b/lib/ain-evm/src/block.rs index 0fe8cfaec8..4a93c67e87 100644 --- a/lib/ain-evm/src/block.rs +++ b/lib/ain-evm/src/block.rs @@ -21,8 +21,8 @@ impl BlockHandler { .unwrap_or((H256::default(), U256::zero())) } - pub fn connect_block(&self, block: BlockAny) { - self.storage.put_latest_block(block.clone()); + pub fn connect_block(&self, block: &BlockAny) { + self.storage.put_latest_block(block); self.storage.put_block(block) } } diff --git a/lib/ain-evm/src/handler.rs b/lib/ain-evm/src/handler.rs index 01164164a6..d9ba1a084d 100644 --- a/lib/ain-evm/src/handler.rs +++ b/lib/ain-evm/src/handler.rs @@ -111,7 +111,7 @@ impl Handlers { let mut state = self.evm.state.write().unwrap(); *state = executor.backend().state().clone(); - self.block.connect_block(block.clone()); + self.block.connect_block(&block); self.receipt.put_receipts(receipts); } diff --git a/lib/ain-evm/src/storage/cache.rs b/lib/ain-evm/src/storage/cache.rs index 79578d63f6..33fc1935dd 100644 --- a/lib/ain-evm/src/storage/cache.rs +++ b/lib/ain-evm/src/storage/cache.rs @@ -51,12 +51,15 @@ impl BlockStorage for Cache { .and_then(|block_number| self.get_block_by_number(block_number)) } - fn put_block(&self, block: BlockAny) { - self.extend_transactions_from_block(&block); + fn put_block(&self, block: &BlockAny) { + self.extend_transactions_from_block(block); let block_number = block.header.number; let hash = block.header.hash(); - self.blocks.write().unwrap().put(block_number, block); + self.blocks + .write() + .unwrap() + .put(block_number, block.clone()); self.block_hashes.write().unwrap().put(hash, block_number); } @@ -68,9 +71,9 @@ impl BlockStorage for Cache { .map(ToOwned::to_owned) } - fn put_latest_block(&self, block: BlockAny) { + fn put_latest_block(&self, block: &BlockAny) { let mut cache = self.latest_block.write().unwrap(); - *cache = Some(block); + *cache = Some(block.clone()); } } @@ -119,4 +122,11 @@ impl TransactionStorage for Cache { .get(index) .map(ToOwned::to_owned) } + + fn put_transaction(&self, transaction: &TransactionV2) { + self.transactions + .write() + .unwrap() + .put(transaction.hash(), transaction.clone()); + } } diff --git a/lib/ain-evm/src/storage/data_handler.rs b/lib/ain-evm/src/storage/data_handler.rs index ea299f0458..b1ff501cb3 100644 --- a/lib/ain-evm/src/storage/data_handler.rs +++ b/lib/ain-evm/src/storage/data_handler.rs @@ -104,6 +104,13 @@ impl TransactionStorage for BlockchainDataHandler { .get(index) .map(ToOwned::to_owned) } + + fn put_transaction(&self, transaction: &TransactionV2) { + self.transactions + .write() + .unwrap() + .insert(transaction.hash(), transaction.clone()); + } } impl BlockStorage for BlockchainDataHandler { @@ -123,12 +130,15 @@ impl BlockStorage for BlockchainDataHandler { .and_then(|block_number| self.get_block_by_number(block_number)) } - fn put_block(&self, block: BlockAny) { - self.extend_transactions_from_block(&block); + fn put_block(&self, block: &BlockAny) { + self.extend_transactions_from_block(block); let block_number = block.header.number; let hash = block.header.hash(); - self.blocks.write().unwrap().insert(block_number, block); + self.blocks + .write() + .unwrap() + .insert(block_number, block.clone()); self.block_map.write().unwrap().insert(hash, block_number); } @@ -140,7 +150,7 @@ impl BlockStorage for BlockchainDataHandler { .and_then(|number| self.get_block_by_number(number)) } - fn put_latest_block(&self, block: BlockAny) { + fn put_latest_block(&self, block: &BlockAny) { let mut latest_block_number = self.latest_block_number.write().unwrap(); *latest_block_number = Some(block.header.number); } diff --git a/lib/ain-evm/src/storage/mod.rs b/lib/ain-evm/src/storage/mod.rs index 360f615456..51a2ae78f7 100644 --- a/lib/ain-evm/src/storage/mod.rs +++ b/lib/ain-evm/src/storage/mod.rs @@ -38,30 +38,42 @@ impl Storage { impl BlockStorage for Storage { fn get_block_by_number(&self, number: &U256) -> Option { - self.cache - .get_block_by_number(number) - .or_else(|| self.blockchain_data_handler.get_block_by_number(number)) + self.cache.get_block_by_number(number).or_else(|| { + let block = self.blockchain_data_handler.get_block_by_number(number); + if let Some(ref block) = block { + self.cache.put_block(block) + } + block + }) } fn get_block_by_hash(&self, block_hash: &H256) -> Option { - self.cache - .get_block_by_hash(block_hash) - .or_else(|| self.blockchain_data_handler.get_block_by_hash(block_hash)) + self.cache.get_block_by_hash(block_hash).or_else(|| { + let block = self.blockchain_data_handler.get_block_by_hash(block_hash); + if let Some(ref block) = block { + self.cache.put_block(block) + } + block + }) } - fn put_block(&self, block: BlockAny) { - self.cache.put_block(block.clone()); + fn put_block(&self, block: &BlockAny) { + self.cache.put_block(block); self.blockchain_data_handler.put_block(block) } fn get_latest_block(&self) -> Option { - self.cache - .get_latest_block() - .or_else(|| self.blockchain_data_handler.get_latest_block()) + self.cache.get_latest_block().or_else(|| { + let latest_block = self.blockchain_data_handler.get_latest_block(); + if let Some(ref block) = latest_block { + self.cache.put_latest_block(block) + } + latest_block + }) } - fn put_latest_block(&self, block: BlockAny) { - self.cache.put_latest_block(block.clone()); + fn put_latest_block(&self, block: &BlockAny) { + self.cache.put_latest_block(block); self.blockchain_data_handler.put_latest_block(block) } } @@ -76,9 +88,13 @@ impl TransactionStorage for Storage { } fn get_transaction_by_hash(&self, hash: &H256) -> Option { - self.cache - .get_transaction_by_hash(hash) - .or_else(|| self.blockchain_data_handler.get_transaction_by_hash(hash)) + self.cache.get_transaction_by_hash(hash).or_else(|| { + let transaction = self.blockchain_data_handler.get_transaction_by_hash(hash); + if let Some(ref transaction) = transaction { + self.cache.put_transaction(transaction) + } + transaction + }) } fn get_transaction_by_block_hash_and_index( @@ -89,8 +105,13 @@ impl TransactionStorage for Storage { self.cache .get_transaction_by_block_hash_and_index(hash, index) .or_else(|| { - self.blockchain_data_handler - .get_transaction_by_block_hash_and_index(hash, index) + let transaction = self + .blockchain_data_handler + .get_transaction_by_block_hash_and_index(hash, index); + if let Some(ref transaction) = transaction { + self.cache.put_transaction(transaction) + } + transaction }) } @@ -102,10 +123,20 @@ impl TransactionStorage for Storage { self.cache .get_transaction_by_block_number_and_index(number, index) .or_else(|| { - self.blockchain_data_handler - .get_transaction_by_block_number_and_index(number, index) + let transaction = self + .blockchain_data_handler + .get_transaction_by_block_number_and_index(number, index); + if let Some(ref transaction) = transaction { + self.cache.put_transaction(transaction) + } + transaction }) } + + fn put_transaction(&self, transaction: &TransactionV2) { + self.cache.put_transaction(transaction); + self.blockchain_data_handler.put_transaction(transaction); + } } impl ReceiptStorage for Storage { diff --git a/lib/ain-evm/src/storage/traits.rs b/lib/ain-evm/src/storage/traits.rs index 0d5163ebab..ad9649c092 100644 --- a/lib/ain-evm/src/storage/traits.rs +++ b/lib/ain-evm/src/storage/traits.rs @@ -14,9 +14,9 @@ use std::path::PathBuf; pub trait BlockStorage { fn get_block_by_number(&self, number: &U256) -> Option; fn get_block_by_hash(&self, block_hash: &H256) -> Option; - fn put_block(&self, block: BlockAny); + fn put_block(&self, block: &BlockAny); fn get_latest_block(&self) -> Option; - fn put_latest_block(&self, block: BlockAny); + fn put_latest_block(&self, block: &BlockAny); } pub trait TransactionStorage { @@ -32,6 +32,7 @@ pub trait TransactionStorage { number: &U256, index: usize, ) -> Option; + fn put_transaction(&self, transaction: &TransactionV2); } pub trait ReceiptStorage { From 2ccffa5d37185f92e89596f57753a5ff2e529e70 Mon Sep 17 00:00:00 2001 From: jouzo Date: Fri, 28 Apr 2023 11:09:33 +0100 Subject: [PATCH 4/4] Get receipts root before creating block --- lib/ain-evm/src/block.rs | 6 ++-- lib/ain-evm/src/evm.rs | 12 ++++++- lib/ain-evm/src/executor.rs | 18 +++++++++-- lib/ain-evm/src/handler.rs | 64 ++++++++++++------------------------- lib/ain-evm/src/receipt.rs | 60 ++++++++-------------------------- 5 files changed, 64 insertions(+), 96 deletions(-) diff --git a/lib/ain-evm/src/block.rs b/lib/ain-evm/src/block.rs index 4a93c67e87..8596cabf04 100644 --- a/lib/ain-evm/src/block.rs +++ b/lib/ain-evm/src/block.rs @@ -21,8 +21,8 @@ impl BlockHandler { .unwrap_or((H256::default(), U256::zero())) } - pub fn connect_block(&self, block: &BlockAny) { - self.storage.put_latest_block(block); - self.storage.put_block(block) + pub fn connect_block(&self, block: BlockAny) { + self.storage.put_latest_block(&block); + self.storage.put_block(&block) } } diff --git a/lib/ain-evm/src/evm.rs b/lib/ain-evm/src/evm.rs index 8fc0df7aad..c5187919d3 100644 --- a/lib/ain-evm/src/evm.rs +++ b/lib/ain-evm/src/evm.rs @@ -6,7 +6,8 @@ use crate::{ transaction::SignedTx, }; use anyhow::anyhow; -use ethereum::{AccessList, TransactionV2}; +use ethereum::{AccessList, Log, TransactionV2}; +use ethereum_types::{Bloom, BloomInput}; use evm::backend::MemoryAccount; use evm::{ backend::{MemoryBackend, MemoryVicinity}, @@ -138,6 +139,15 @@ impl EVMHandler { self.tx_queues.add_signed_tx(context, signed_tx); Ok(()) } + + pub fn logs_bloom(logs: Vec, bloom: &mut Bloom) { + for log in logs { + bloom.accrue(BloomInput::Raw(&log.address[..])); + for topic in log.topics { + bloom.accrue(BloomInput::Raw(&topic[..])); + } + } + } } impl EVMHandler { diff --git a/lib/ain-evm/src/executor.rs b/lib/ain-evm/src/executor.rs index 801d2ed0b5..d0a67d53d7 100644 --- a/lib/ain-evm/src/executor.rs +++ b/lib/ain-evm/src/executor.rs @@ -1,17 +1,18 @@ use std::collections::BTreeMap; use crate::{ + evm::EVMHandler, traits::{Executor, ExecutorContext}, transaction::SignedTx, }; +use ethereum::{EIP658ReceiptData, Log, ReceiptV3}; +use ethereum_types::{Bloom, U256}; use evm::{ backend::{ApplyBackend, Backend}, executor::stack::{MemoryStackState, StackExecutor, StackSubstateMetadata}, Config, ExitReason, }; -use ethereum::Log; - #[derive(Debug)] pub struct AinExecutor { backend: B, @@ -71,11 +72,23 @@ where self.backend.apply(values, logs.clone(), true); } + let receipt = ReceiptV3::EIP1559(EIP658ReceiptData { + logs_bloom: { + let mut bloom: Bloom = Bloom::default(); + EVMHandler::logs_bloom(logs.clone(), &mut bloom); + bloom + }, + status_code: exit_reason.is_succeed() as u8, + logs: logs.clone(), + used_gas: U256::from(used_gas), + }); + TxResponse { exit_reason, data, logs, used_gas, + receipt, } } @@ -101,4 +114,5 @@ pub struct TxResponse { pub data: Vec, pub logs: Vec, pub used_gas: u64, + pub receipt: ReceiptV3, } diff --git a/lib/ain-evm/src/handler.rs b/lib/ain-evm/src/handler.rs index 787e459850..5dca21977b 100644 --- a/lib/ain-evm/src/handler.rs +++ b/lib/ain-evm/src/handler.rs @@ -5,10 +5,9 @@ use crate::receipt::ReceiptHandler; use crate::storage::Storage; use crate::traits::Executor; -use ethereum::{Block, BlockAny, Log, PartialHeader, TransactionV2}; -use ethereum_types::{Bloom, BloomInput}; +use ethereum::{Block, BlockAny, PartialHeader, ReceiptV3, TransactionV2}; +use ethereum_types::{Bloom, H160, U256}; use evm::backend::MemoryBackend; -use primitive_types::{H160, U256}; use std::error::Error; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; @@ -44,8 +43,9 @@ impl Handlers { difficulty: u32, miner_address: Option, ) -> Result<(BlockAny, Vec), Box> { - let mut successful_transactions = Vec::with_capacity(self.evm.tx_queues.len(context)); + let mut all_transactions = Vec::with_capacity(self.evm.tx_queues.len(context)); let mut failed_transactions = Vec::with_capacity(self.evm.tx_queues.len(context)); + let mut receipts_v3: Vec = Vec::with_capacity(self.evm.tx_queues.len(context)); let mut gas_used = 0u64; let mut logs_bloom: Bloom = Default::default(); @@ -59,40 +59,31 @@ impl Handlers { exit_reason, logs, used_gas, + receipt, .. } = executor.exec(&signed_tx); if exit_reason.is_succeed() { - successful_transactions.push(signed_tx); + all_transactions.push(signed_tx); } else { - failed_transactions.push(signed_tx) + failed_transactions.push(signed_tx.transaction.clone()); + all_transactions.push(signed_tx); } + gas_used += used_gas; - Self::logs_bloom(logs, &mut logs_bloom); + EVMHandler::logs_bloom(logs, &mut logs_bloom); + receipts_v3.push(receipt); } - let mut all_transactions = successful_transactions - .clone() - .into_iter() - .map(|tx| tx.transaction) - .collect::>(); - all_transactions.extend( - failed_transactions - .clone() - .into_iter() - .map(|tx| tx.transaction) - .collect::>(), - ); - self.evm.tx_queues.remove(context); let (parent_hash, number) = self.block.get_latest_block_hash_and_number(); - let mut block = Block::new( + let block = Block::new( PartialHeader { parent_hash, beneficiary: miner_address.unwrap_or_default(), state_root: Default::default(), - receipts_root: Default::default(), + receipts_root: ReceiptHandler::get_receipts_root(&receipts_v3), logs_bloom, difficulty: U256::from(difficulty), number, @@ -106,41 +97,28 @@ impl Handlers { mix_hash: Default::default(), nonce: Default::default(), }, - all_transactions, + all_transactions + .iter() + .map(|signed_tx| signed_tx.transaction.clone()) + .collect(), Vec::new(), ); let receipts = self.receipt.generate_receipts( - successful_transactions, - failed_transactions.clone(), + &all_transactions, + receipts_v3, block.header.hash(), block.header.number, ); - block.header.receipts_root = self.receipt.get_receipt_root(&receipts); if update_state { let mut state = self.evm.state.write().unwrap(); *state = executor.backend().state().clone(); - self.block.connect_block(&block); + self.block.connect_block(block.clone()); self.receipt.put_receipts(receipts); } - Ok(( - block, - failed_transactions - .into_iter() - .map(|tx| tx.transaction) - .collect(), - )) - } - - fn logs_bloom(logs: Vec, bloom: &mut Bloom) { - for log in logs { - bloom.accrue(BloomInput::Raw(&log.address[..])); - for topic in log.topics { - bloom.accrue(BloomInput::Raw(&topic[..])); - } - } + Ok((block, failed_transactions)) } } diff --git a/lib/ain-evm/src/receipt.rs b/lib/ain-evm/src/receipt.rs index 84163e693d..26da765a43 100644 --- a/lib/ain-evm/src/receipt.rs +++ b/lib/ain-evm/src/receipt.rs @@ -1,6 +1,6 @@ use crate::storage::{traits::ReceiptStorage, Storage}; use crate::transaction::SignedTx; -use ethereum::{EIP658ReceiptData, EnvelopedEncodable, ReceiptV3}; +use ethereum::{EnvelopedEncodable, ReceiptV3}; use primitive_types::{H160, H256, U256}; use ethereum::util::ordered_trie_root; @@ -39,33 +39,27 @@ impl ReceiptHandler { Self { storage } } - pub fn get_receipt_root(&self, receipts: &[Receipt]) -> H256 { + pub fn get_receipts_root(receipts: &[ReceiptV3]) -> H256 { ordered_trie_root( receipts .iter() - .map(|r| EnvelopedEncodable::encode(&r.receipt).freeze()), + .map(|r| EnvelopedEncodable::encode(r).freeze()), ) } pub fn generate_receipts( &self, - successful: Vec, - failed: Vec, + transactions: &[SignedTx], + receipts: Vec, block_hash: H256, block_number: U256, ) -> Vec { - let mut receipts = Vec::new(); - - let mut index = 0; - - for signed_tx in successful { - let receipt = Receipt { - receipt: ReceiptV3::EIP1559(EIP658ReceiptData { - status_code: 1, - used_gas: Default::default(), - logs_bloom: Default::default(), - logs: vec![], - }), + transactions + .iter() + .enumerate() + .zip(receipts.into_iter()) + .map(|((index, signed_tx), receipt)| Receipt { + receipt, block_hash, block_number, tx_hash: signed_tx.transaction.hash(), @@ -77,36 +71,8 @@ impl ReceiptHandler { .to() .is_none() .then(|| get_contract_address(&signed_tx.sender, &signed_tx.nonce())), - }; - receipts.push(receipt); - index += 1; - } - - for signed_tx in failed { - let receipt = Receipt { - receipt: ReceiptV3::EIP1559(EIP658ReceiptData { - status_code: 0, - used_gas: Default::default(), - logs_bloom: Default::default(), - logs: vec![], - }), - block_hash, - block_number, - tx_hash: signed_tx.transaction.hash(), - from: signed_tx.sender, - to: signed_tx.to(), - tx_index: index, - contract_address: signed_tx - .to() - .is_none() - .then(|| get_contract_address(&signed_tx.sender, &signed_tx.nonce())), - tx_type: EnvelopedEncodable::type_id(&signed_tx.transaction).unwrap(), - }; - - receipts.push(receipt); - index += 1; - } - receipts + }) + .collect() } pub fn put_receipts(&self, receipts: Vec) {