From f8106f27f79e72a8d97452fe27131f74535ae794 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Thu, 26 Oct 2023 12:20:39 +0800 Subject: [PATCH] Revert "EVM backend overlay (#2611)" This reverts commit 07ba855f73c7fdfb0b2f10fc3b31fe73c17b1630. --- lib/ain-evm/src/backend.rs | 293 ++++++------------ lib/ain-evm/src/blocktemplate.rs | 41 ++- lib/ain-evm/src/contract.rs | 7 +- lib/ain-evm/src/core.rs | 71 ++++- lib/ain-evm/src/evm.rs | 122 ++++++-- lib/ain-evm/src/executor.rs | 19 +- lib/ain-evm/src/trie.rs | 3 +- lib/ain-grpc/src/rpc/eth.rs | 1 - lib/ain-rs-exports/src/evm.rs | 53 ++-- lib/ain-rs-exports/src/lib.rs | 29 +- src/dfi/validation.cpp | 2 +- src/miner.cpp | 3 +- test/functional/contracts/SelfDestruct.sol | 20 -- test/functional/feature_evm.py | 184 ++--------- test/functional/feature_evm_gas.py | 2 +- test/functional/feature_evm_rollback.py | 2 +- test/functional/feature_evm_transferdomain.py | 2 +- 17 files changed, 355 insertions(+), 499 deletions(-) delete mode 100644 test/functional/contracts/SelfDestruct.sol diff --git a/lib/ain-evm/src/backend.rs b/lib/ain-evm/src/backend.rs index 689964087e..e46b3bd6af 100644 --- a/lib/ain-evm/src/backend.rs +++ b/lib/ain-evm/src/backend.rs @@ -1,8 +1,4 @@ -use std::{ - collections::{HashMap, HashSet}, - error::Error, - sync::Arc, -}; +use std::{error::Error, sync::Arc}; use anyhow::format_err; use ethereum::{Account, Log}; @@ -18,7 +14,7 @@ use crate::{ fee::calculate_gas_fee, storage::{traits::BlockStorage, Storage}, transaction::SignedTx, - trie::{TrieDBStore, GENESIS_STATE_ROOT}, + trie::TrieDBStore, Result, }; @@ -42,86 +38,11 @@ pub struct Vicinity { pub block_randomness: Option, } -#[derive(Debug, Clone)] -struct OverlayData { - account: Account, - code: Option>, - storage: HashMap, -} - -#[derive(Debug, Clone)] -struct Overlay { - state: HashMap, - changeset: Vec>, - deletes: HashSet, -} - -impl Overlay { - pub fn new() -> Self { - Self { - state: HashMap::new(), - changeset: Vec::new(), - deletes: HashSet::new(), - } - } - - pub fn apply( - &mut self, - address: H160, - account: Account, - code: Option>, - mut storage: HashMap, - reset_storage: bool, - ) { - if !reset_storage { - if let Some(existing_storage) = self.storage(&address) { - for (k, v) in existing_storage { - storage.entry(*k).or_insert_with(|| *v); - } - } - } - - let data = OverlayData { - account, - code, - storage, - }; - self.state.insert(address, data.clone()); - } - - fn mark_delete(&mut self, address: H160) { - self.deletes.insert(address); - } - - // Keeps track of the number of TXs in the changeset. - // Should be called after a TX has been fully processed. - fn inc(&mut self) { - self.changeset.push(self.state.clone()); - } - - pub fn storage(&self, address: &H160) -> Option<&HashMap> { - self.state.get(address).map(|d| &d.storage) - } - - pub fn storage_val(&self, address: &H160, index: &H256) -> Option { - self.storage(address).and_then(|d| d.get(index).cloned()) - } - - pub fn get_account(&self, address: &H160) -> Option { - self.state.get(address).map(|d| d.account.to_owned()) - } - - pub fn get_code(&self, address: &H160) -> Option> { - self.state.get(address).and_then(|d| d.code.clone()) - } -} - pub struct EVMBackend { state: MptOnce, trie_store: Arc, storage: Arc, pub vicinity: Vicinity, - overlay: Overlay, } impl EVMBackend { @@ -141,14 +62,9 @@ impl EVMBackend { trie_store, storage, vicinity, - overlay: Overlay::new(), }) } - pub fn increase_tx_count(&mut self) { - self.overlay.inc() - } - pub fn apply>( &mut self, address: H160, @@ -157,107 +73,70 @@ impl EVMBackend { storage: I, reset_storage: bool, ) -> Result { - let mut account = self.get_account(&address).unwrap_or(Account { + let account = self.get_account(&address).unwrap_or(Account { nonce: U256::zero(), balance: U256::zero(), storage_root: H256::zero(), code_hash: H256::zero(), }); - if reset_storage || is_empty_account(&account) { + let mut storage_trie = if reset_storage || is_empty_account(&account) { self.trie_store .trie_db .trie_create(address.as_bytes(), None, true) - .map_err(|e| BackendError::TrieCreationFailed(e.to_string()))?; - account.storage_root = GENESIS_STATE_ROOT; - } - - if let Some(code) = &code { - account.code_hash = Hasher::hash(code); - } - - if let Some(basic) = basic { - account.balance = basic.balance; - account.nonce = basic.nonce; - } + .map_err(|e| BackendError::TrieCreationFailed(e.to_string()))? + } else { + self.trie_store + .trie_db + .trie_restore(address.as_bytes(), None, account.storage_root.into()) + .map_err(|e| BackendError::TrieRestoreFailed(e.to_string()))? + }; - self.overlay.apply( - address, - account.clone(), - code, - storage.into_iter().collect(), - reset_storage, - ); - Ok(account) - } - - pub fn clear_overlay(&mut self) { - self.overlay.state.clear() - } - - pub fn reset_from_tx(&mut self, index: usize) { - self.overlay.state = self - .overlay - .changeset - .get(index) - .cloned() - .unwrap_or_default(); - self.overlay.changeset.truncate(index + 1); - } - - fn apply_overlay(&mut self, is_miner: bool) -> Result<()> { - for ( - address, - OverlayData { - ref mut account, - code, - storage, - }, - ) in self.overlay.state.drain() - { - if self.overlay.deletes.contains(&address) { - self.state - .remove(address.as_bytes()) - .expect("Error removing address in state"); - - if !is_miner { - self.trie_store.trie_db.trie_remove(address.as_bytes()); - } + storage.into_iter().for_each(|(k, v)| { + debug!("Apply::Modify storage, key: {:x} value: {:x}", k, v); + let _ = storage_trie.insert(k.as_bytes(), v.as_bytes()); + storage_trie.commit(); + }); - continue; + let code_hash = match code { + None => account.code_hash, + Some(code) => { + let code_hash = Hasher::hash(&code); + self.storage.put_code(code_hash, code)?; + code_hash } + }; - if !storage.is_empty() { - let mut storage_trie = self - .trie_store - .trie_db - .trie_restore(address.as_bytes(), None, account.storage_root.into()) - .map_err(|e| BackendError::TrieRestoreFailed(e.to_string()))?; + let new_account = match basic { + Some(basic) => Account { + nonce: basic.nonce, + balance: basic.balance, + code_hash, + storage_root: storage_trie.commit().into(), + }, + None => Account { + nonce: account.nonce, + balance: account.balance, + code_hash, + storage_root: storage_trie.commit().into(), + }, + }; - storage.into_iter().for_each(|(k, v)| { - debug!( - "Apply::Modify storage {address:?}, key: {:x} value: {:x}", - k, v - ); - let _ = storage_trie.insert(k.as_bytes(), v.as_bytes()); - }); - account.storage_root = storage_trie.commit().into(); - } + self.state + .insert(address.as_bytes(), new_account.rlp_bytes().as_ref()) + .map_err(|e| BackendError::TrieError(format!("{e}")))?; + self.state.commit(); - if let Some(code) = code { - self.storage.put_code(account.code_hash, code)?; - } + Ok(new_account) + } - self.state - .insert(address.as_bytes(), account.rlp_bytes().as_ref()) - .map_err(|e| BackendError::TrieError(format!("{e}")))?; - } - Ok(()) + pub fn commit(&mut self) -> H256 { + self.state.commit().into() } - pub fn commit(&mut self, is_miner: bool) -> Result { - self.apply_overlay(is_miner)?; - Ok(self.state.commit().into()) + // Read-only state root. Does not commit changes to database + pub fn root(&self) -> H256 { + self.state.root().into() } pub fn update_vicinity_from_tx(&mut self, tx: &SignedTx) { @@ -293,6 +172,7 @@ impl EVMBackend { self.apply(sender, Some(new_basic), None, Vec::new(), false) .map_err(|e| BackendError::DeductPrepayGasFailed(e.to_string()))?; + self.commit(); Ok(()) } @@ -323,6 +203,7 @@ impl EVMBackend { self.apply(signed_tx.sender, Some(new_basic), None, Vec::new(), false) .map_err(|e| BackendError::RefundUnusedGasFailed(e.to_string()))?; + self.commit(); Ok(()) } @@ -330,12 +211,10 @@ impl EVMBackend { impl EVMBackend { pub fn get_account(&self, address: &H160) -> Option { - self.overlay.get_account(address).or_else(|| { - self.state - .get(address.as_bytes()) - .unwrap_or(None) - .and_then(|addr| Account::decode(&Rlp::new(addr.as_bytes_ref())).ok()) - }) + self.state + .get(address.as_bytes()) + .unwrap_or(None) + .and_then(|addr| Account::decode(&Rlp::new(addr.as_bytes_ref())).ok()) } pub fn get_nonce(&self, address: &H160) -> U256 { @@ -350,8 +229,24 @@ impl EVMBackend { .unwrap_or_default() } - pub fn get_contract_storage(&self, contract: H160, index: H256) -> Result { - Ok(U256::from(self.storage(contract, index).as_bytes())) + pub fn get_contract_storage(&self, contract: H160, storage_index: &[u8]) -> Result { + let Some(account) = self.get_account(&contract) else { + return Ok(U256::zero()); + }; + + let state = self + .trie_store + .trie_db + .trie_restore(contract.as_ref(), None, account.storage_root.into()) + .map_err(|e| BackendError::TrieRestoreFailed(e.to_string()))?; + + Ok(U256::from( + state + .get(storage_index) + .unwrap_or_default() + .unwrap_or_default() + .as_slice(), + )) } pub fn deploy_contract( @@ -426,7 +321,7 @@ impl Backend for EVMBackend { } fn exists(&self, address: H160) -> bool { - self.get_account(&address).is_some() + self.state.contains(address.as_bytes()).unwrap_or(false) } fn basic(&self, address: H160) -> Basic { @@ -441,35 +336,23 @@ impl Backend for EVMBackend { fn code(&self, address: H160) -> Vec { trace!(target: "backend", "[EVMBackend] code for address {:x?}", address); - self.overlay.get_code(&address).unwrap_or_else(|| { - self.get_account(&address) - .and_then(|account| { - self.storage - .get_code_by_hash(account.code_hash) - .ok() - .flatten() - }) - .unwrap_or_default() - }) + self.get_account(&address) + .and_then(|account| self.storage.get_code_by_hash(account.code_hash).unwrap()) + .unwrap_or_default() } fn storage(&self, address: H160, index: H256) -> H256 { trace!(target: "backend", "[EVMBackend] Getting storage for address {:x?} at index {:x?}", address, index); - let Some(account) = self.get_account(&address) else { - return H256::zero(); - }; - - self.overlay - .storage_val(&address, &index) - .unwrap_or_else(|| { + self.get_account(&address) + .and_then(|account| { self.trie_store .trie_db - .trie_restore(address.as_ref(), None, account.storage_root.into()) + .trie_restore(address.as_bytes(), None, account.storage_root.into()) .ok() - .and_then(|trie| trie.get(index.as_bytes()).ok().flatten()) - .map(|res| H256::from_slice(res.as_ref())) - .unwrap_or_default() }) + .and_then(|trie| trie.get(index.as_bytes()).ok().flatten()) + .map(|res| H256::from_slice(res.as_ref())) + .unwrap_or_default() } fn original_storage(&self, address: H160, index: H256) -> Option { @@ -505,14 +388,18 @@ impl ApplyBackend for EVMBackend { if is_empty_account(&new_account) && delete_empty { debug!("Deleting empty address {:x?}", address); - self.overlay.mark_delete(address); + self.trie_store.trie_db.trie_remove(address.as_bytes()); + self.state + .remove(address.as_bytes()) + .expect("Error removing address in state"); } } Apply::Delete { address } => { debug!("Deleting address {:x?}", address); - self.apply(address, None, None, vec![], false) - .expect("Error applying state"); - self.overlay.mark_delete(address); + self.trie_store.trie_db.trie_remove(address.as_bytes()); + self.state + .remove(address.as_bytes()) + .expect("Error removing address in state"); } } } diff --git a/lib/ain-evm/src/blocktemplate.rs b/lib/ain-evm/src/blocktemplate.rs index 139583e61c..83074d4cc3 100644 --- a/lib/ain-evm/src/blocktemplate.rs +++ b/lib/ain-evm/src/blocktemplate.rs @@ -2,11 +2,7 @@ use ethereum::{Block, ReceiptV3, TransactionV2}; use ethereum_types::{Bloom, H160, H256, U256}; use crate::{ - backend::{EVMBackend, Vicinity}, - core::XHash, - evm::ExecTxState, - receipt::Receipt, - transaction::SignedTx, + backend::Vicinity, core::XHash, evm::ExecTxState, receipt::Receipt, transaction::SignedTx, }; type Result = std::result::Result; @@ -17,8 +13,9 @@ pub type ReceiptAndOptionalContractAddress = (ReceiptV3, Option); pub struct TemplateTxItem { pub tx: Box, pub tx_hash: XHash, - pub gas_fees: U256, pub gas_used: U256, + pub gas_fees: U256, + pub state_root: H256, pub logs_bloom: Bloom, pub receipt_v3: ReceiptAndOptionalContractAddress, } @@ -27,13 +24,15 @@ impl TemplateTxItem { pub fn new_system_tx( tx: Box, receipt_v3: ReceiptAndOptionalContractAddress, + state_root: H256, logs_bloom: Bloom, ) -> Self { TemplateTxItem { tx, tx_hash: Default::default(), - gas_fees: U256::zero(), gas_used: U256::zero(), + gas_fees: U256::zero(), + state_root, logs_bloom, receipt_v3, } @@ -53,10 +52,11 @@ pub struct BlockData { /// 4. Backend vicinity /// 5. Block template timestamp /// 6. DVM block number -/// 7. EVM backend +/// 7. Initial state root /// /// The template is used to construct a valid EVM block. /// +#[derive(Clone, Debug, Default)] pub struct BlockTemplate { pub transactions: Vec, pub block_data: Option, @@ -65,7 +65,7 @@ pub struct BlockTemplate { pub parent_hash: H256, pub dvm_block: u64, pub timestamp: u64, - pub backend: EVMBackend, + pub initial_state_root: H256, } impl BlockTemplate { @@ -74,7 +74,7 @@ impl BlockTemplate { parent_hash: H256, dvm_block: u64, timestamp: u64, - backend: EVMBackend, + initial_state_root: H256, ) -> Self { Self { transactions: Vec::new(), @@ -84,7 +84,7 @@ impl BlockTemplate { parent_hash, dvm_block, timestamp, - backend, + initial_state_root, } } @@ -97,8 +97,9 @@ impl BlockTemplate { self.transactions.push(TemplateTxItem { tx: tx_update.tx, tx_hash, - gas_fees: tx_update.gas_fees, gas_used: tx_update.gas_used, + gas_fees: tx_update.gas_fees, + state_root: tx_update.state_root, logs_bloom: tx_update.logs_bloom, receipt_v3: tx_update.receipt, }); @@ -122,13 +123,25 @@ impl BlockTemplate { self.total_gas_used = self.transactions.iter().try_fold(U256::zero(), |acc, tx| { acc.checked_add(tx.gas_used) .ok_or(BlockTemplateError::ValueOverflow) - })?; - self.backend.reset_from_tx(index); + })? } Ok(removed_txs) } + pub fn get_state_root_from_native_hash(&self, hash: XHash) -> Option { + self.transactions + .iter() + .find(|tx_item| tx_item.tx_hash == hash) + .map(|tx_item| tx_item.state_root) + } + + pub fn get_latest_state_root(&self) -> H256 { + self.transactions + .last() + .map_or(self.initial_state_root, |tx_item| tx_item.state_root) + } + pub fn get_latest_logs_bloom(&self) -> Bloom { self.transactions .last() diff --git a/lib/ain-evm/src/contract.rs b/lib/ain-evm/src/contract.rs index ec53a178a4..7ae95c32a7 100644 --- a/lib/ain-evm/src/contract.rs +++ b/lib/ain-evm/src/contract.rs @@ -230,7 +230,7 @@ pub fn bridge_dst20_in( let contract_balance_storage_index = get_address_storage_index(H256::zero(), fixed_address); let total_supply_index = H256::from_low_u64_be(2); - let total_supply = backend.get_contract_storage(contract, total_supply_index)?; + let total_supply = backend.get_contract_storage(contract, total_supply_index.as_bytes())?; let new_total_supply = total_supply .checked_add(amount) @@ -265,7 +265,7 @@ pub fn bridge_dst20_out( let contract_balance_storage_index = get_address_storage_index(H256::zero(), fixed_address); let total_supply_index = H256::from_low_u64_be(2); - let total_supply = backend.get_contract_storage(contract, total_supply_index)?; + let total_supply = backend.get_contract_storage(contract, total_supply_index.as_bytes())?; let new_total_supply = total_supply .checked_sub(amount) @@ -310,7 +310,8 @@ pub fn bridge_dfi( let FixedContract { fixed_address, .. } = get_transfer_domain_contract(); let total_supply_index = H256::from_low_u64_be(1); - let total_supply = backend.get_contract_storage(fixed_address, total_supply_index)?; + let total_supply = + backend.get_contract_storage(fixed_address, total_supply_index.as_bytes())?; let new_total_supply = if direction == TransferDirection::EvmOut { total_supply.checked_sub(amount) diff --git a/lib/ain-evm/src/core.rs b/lib/ain-evm/src/core.rs index 3b8601f4a3..56f7a90c90 100644 --- a/lib/ain-evm/src/core.rs +++ b/lib/ain-evm/src/core.rs @@ -81,6 +81,7 @@ impl SignedTxCache { } struct TxValidationCache { + validated: spin::Mutex>, stateless: spin::Mutex>, } @@ -93,19 +94,37 @@ impl Default for TxValidationCache { impl TxValidationCache { pub fn new(capacity: usize) -> Self { Self { + validated: spin::Mutex::new(LruCache::new(NonZeroUsize::new(capacity).unwrap())), stateless: spin::Mutex::new(LruCache::new(NonZeroUsize::new(capacity).unwrap())), } } + pub fn get(&self, key: &(H256, String)) -> Option { + self.validated.lock().get(key).cloned() + } + pub fn get_stateless(&self, key: &str) -> Option { self.stateless.lock().get(key).cloned() } + pub fn set(&self, key: (H256, String), value: ValidateTxInfo) -> ValidateTxInfo { + let mut cache = self.validated.lock(); + cache.put(key, value.clone()); + value + } + pub fn set_stateless(&self, key: String, value: ValidateTxInfo) -> ValidateTxInfo { let mut cache = self.stateless.lock(); cache.put(key, value.clone()); value } + + // To be used on new block or any known state changes. Only clears fully validated TX cache. + // Stateless cache can be kept across blocks and is handled by LRU itself + pub fn clear(&self) { + let mut cache = self.validated.lock(); + cache.clear() + } } pub struct EVMCoreService { @@ -322,6 +341,16 @@ impl EVMCoreService { tx: &str, template: &BlockTemplate, ) -> Result { + let state_root = template.get_latest_state_root(); + debug!("[validate_raw_tx] state_root : {:#?}", state_root); + + if let Some(tx_info) = self + .tx_validation_cache + .get(&(state_root, String::from(tx))) + { + return Ok(tx_info); + } + debug!("[validate_raw_tx] raw transaction : {:#?}", tx); let ValidateTxInfo { @@ -381,7 +410,7 @@ impl EVMCoreService { // Start of stateful checks // Validate tx prepay fees with account balance - let backend = &template.backend; + let backend = self.get_backend(state_root)?; let balance = backend.get_balance(&signed_tx.sender); debug!("[validate_raw_tx] Account balance : {:x?}", balance); if balance < max_prepay_fee { @@ -405,10 +434,13 @@ impl EVMCoreService { .into()); } - Ok(ValidateTxInfo { - signed_tx, - max_prepay_fee, - }) + Ok(self.tx_validation_cache.set( + (state_root, String::from(tx)), + ValidateTxInfo { + signed_tx, + max_prepay_fee, + }, + )) } /// Validates a raw transfer domain tx. @@ -463,6 +495,12 @@ impl EVMCoreService { tx ); + let state_root = template.get_latest_state_root(); + debug!( + "[validate_raw_transferdomain_tx] state_root : {:#?}", + state_root + ); + let ValidateTxInfo { signed_tx, max_prepay_fee, @@ -657,7 +695,7 @@ impl EVMCoreService { } }; - let backend = &template.backend; + let backend = self.get_backend(state_root)?; let nonce = backend.get_nonce(&signed_tx.sender); debug!( @@ -733,7 +771,9 @@ impl EVMCoreService { template: &BlockTemplate, address: H160, ) -> Result { - let nonce = template.backend.get_nonce(&address); + let state_root = template.get_latest_state_root(); + let backend = self.get_backend(state_root)?; + let nonce = backend.get_nonce(&address); trace!("[get_next_valid_nonce_in_block_template] Account {address:x?} nonce {nonce:x?}"); Ok(nonce) } @@ -767,6 +807,18 @@ impl EVMCoreService { Ok(backend.get_account(&address)) } + pub fn get_latest_contract_storage(&self, contract: H160, storage_index: H256) -> Result { + let state_root = self.get_state_root()?; + let backend = EVMBackend::from_root( + state_root, + Arc::clone(&self.trie_store), + Arc::clone(&self.storage), + Vicinity::default(), + )?; + + backend.get_contract_storage(contract, storage_index.as_bytes()) + } + pub fn get_code(&self, address: H160, block_number: U256) -> Result>> { self.get_account(address, block_number)? .map_or(Ok(None), |account| { @@ -832,7 +884,6 @@ impl EVMCoreService { block_number, state_root ); - EVMBackend::from_root( state_root, Arc::clone(&self.trie_store), @@ -911,4 +962,8 @@ impl EVMCoreService { let mut nonce_store = self.nonce_store.lock(); nonce_store.clear() } + + pub fn clear_transaction_cache(&self) { + self.tx_validation_cache.clear() + } } diff --git a/lib/ain-evm/src/evm.rs b/lib/ain-evm/src/evm.rs index dad4822c6e..1105bd789f 100644 --- a/lib/ain-evm/src/evm.rs +++ b/lib/ain-evm/src/evm.rs @@ -52,6 +52,7 @@ pub struct EVMServices { pub struct ExecTxState { pub tx: Box, pub receipt: ReceiptAndOptionalContractAddress, + pub state_root: H256, pub logs_bloom: Bloom, pub gas_used: U256, pub gas_fees: U256, @@ -138,8 +139,8 @@ impl EVMServices { pub unsafe fn construct_block_in_template( &self, template: &mut BlockTemplate, - is_miner: bool, ) -> Result { + let state_root = template.get_latest_state_root(); let logs_bloom = template.get_latest_logs_bloom(); let timestamp = template.timestamp; @@ -157,7 +158,14 @@ impl EVMServices { debug!("[construct_block] vicinity: {:?}", template.vicinity); - let mut executor = AinExecutor::new(&mut template.backend); + let mut backend = EVMBackend::from_root( + state_root, + Arc::clone(&self.core.trie_store), + Arc::clone(&self.storage), + template.vicinity.clone(), + )?; + + let mut executor = AinExecutor::new(&mut backend); for template_tx in template.transactions.clone() { all_transactions.push(template_tx.tx); receipts_v3.push(template_tx.receipt_v3); @@ -186,15 +194,14 @@ impl EVMServices { executor .backend .add_balance(beneficiary, total_priority_fees)?; - - let state_root = executor.commit(is_miner)?; + executor.commit(); let extra_data = format!("DFI: {}", template.dvm_block).into_bytes(); let block = Block::new( PartialHeader { parent_hash, beneficiary, - state_root, + state_root: backend.commit(), receipts_root: ReceiptService::get_receipts_root(&receipts_v3), logs_bloom, difficulty, @@ -259,29 +266,51 @@ impl EVMServices { .send(Notification::Block(block.header.hash())) .map_err(|e| format_err!(e.to_string()))?; } + // self.core.block_templates.remove(template); self.core.clear_account_nonce(); + self.core.clear_transaction_cache(); Ok(()) } unsafe fn update_block_template_state_from_tx( &self, - template: &mut BlockTemplate, + template: &BlockTemplate, tx: ExecuteTx, ) -> Result { - let base_fee = template.get_block_base_fee_per_gas(); + let state_root = template.get_latest_state_root(); let mut logs_bloom = template.get_latest_logs_bloom(); + debug!( + "[update_block_template_state_from_tx] state_root : {:#?}", + state_root + ); - let mut executor = AinExecutor::new(&mut template.backend); + let mut backend = EVMBackend::from_root( + state_root, + Arc::clone(&self.core.trie_store), + Arc::clone(&self.storage), + template.vicinity.clone(), + )?; + let mut executor = AinExecutor::new(&mut backend); + + let (parent_hash, _) = self + .block + .get_latest_block_hash_and_number()? + .unwrap_or_default(); // Safe since calculate_base_fee will default to INITIAL_BASE_FEE + let base_fee = self.block.calculate_base_fee(parent_hash)?; + debug!( + "[update_block_template_state_from_tx] Block base fee: {}", + base_fee + ); executor.update_total_gas_used(template.total_gas_used); let apply_tx = executor.execute_tx(tx, base_fee)?; EVMCoreService::logs_bloom(apply_tx.logs, &mut logs_bloom); - template.backend.increase_tx_count(); Ok(ExecTxState { tx: apply_tx.tx, receipt: apply_tx.receipt, + state_root: backend.commit(), logs_bloom, gas_used: apply_tx.used_gas, gas_fees: apply_tx.gas_fee, @@ -301,12 +330,19 @@ impl EVMServices { ) -> Result<()> { // reserve DST20 namespace; let is_evm_genesis_block = template.get_block_number() == U256::zero(); + let state_root = template.get_latest_state_root(); let mut logs_bloom = template.get_latest_logs_bloom(); - let mut executor = AinExecutor::new(&mut template.backend); + let mut backend = EVMBackend::from_root( + state_root, + Arc::clone(&self.core.trie_store), + Arc::clone(&self.storage), + template.vicinity.clone(), + )?; + let mut executor = AinExecutor::new(&mut backend); let base_fee = template.vicinity.block_base_fee_per_gas; debug!( - "[update_state_in_block_template] Block base fee: {}", + "[update_block_template_state_from_tx] Block base fee: {}", base_fee ); @@ -323,6 +359,7 @@ impl EVMServices { trace!("deploying {:x?} bytecode {:?}", address, bytecode); executor.deploy_contract(address, bytecode, storage)?; + executor.commit(); // DFIIntrinsicsRegistry contract deployment TX let (tx, receipt) = deploy_contract_tx( @@ -334,6 +371,7 @@ impl EVMServices { template.transactions.push(TemplateTxItem::new_system_tx( Box::new(tx), (receipt, Some(address)), + executor.commit(), logs_bloom, )); @@ -346,6 +384,7 @@ impl EVMServices { trace!("deploying {:x?} bytecode {:?}", address, bytecode); executor.deploy_contract(address, bytecode, storage)?; + executor.commit(); // DFIIntrinsics contract deployment TX let (tx, receipt) = deploy_contract_tx( @@ -355,6 +394,7 @@ impl EVMServices { template.transactions.push(TemplateTxItem::new_system_tx( Box::new(tx), (receipt, Some(address)), + executor.commit(), logs_bloom, )); @@ -367,6 +407,7 @@ impl EVMServices { trace!("deploying {:x?} bytecode {:?}", address, bytecode); executor.deploy_contract(address, bytecode, storage)?; + executor.commit(); // Transferdomain_v1 contract deployment TX let (tx, receipt) = deploy_contract_tx( @@ -376,6 +417,7 @@ impl EVMServices { template.transactions.push(TemplateTxItem::new_system_tx( Box::new(tx), (receipt, Some(address)), + executor.commit(), logs_bloom, )); @@ -388,6 +430,7 @@ impl EVMServices { trace!("deploying {:x?} bytecode {:?}", address, bytecode); executor.deploy_contract(address, bytecode, storage)?; + executor.commit(); // Transferdomain contract deployment TX let (tx, receipt) = deploy_contract_tx( @@ -397,6 +440,7 @@ impl EVMServices { template.transactions.push(TemplateTxItem::new_system_tx( Box::new(tx), (receipt, Some(address)), + executor.commit(), logs_bloom, )); @@ -409,6 +453,7 @@ impl EVMServices { trace!("deploying {:x?} bytecode {:?}", address, bytecode); executor.deploy_contract(address, bytecode, storage)?; + executor.commit(); // DST20 implementation contract deployment TX let (tx, receipt) = @@ -416,6 +461,7 @@ impl EVMServices { template.transactions.push(TemplateTxItem::new_system_tx( Box::new(tx), (receipt, Some(address)), + executor.commit(), logs_bloom, )); @@ -427,6 +473,7 @@ impl EVMServices { template.transactions.push(TemplateTxItem::new_system_tx( apply_result.tx, apply_result.receipt, + executor.commit(), logs_bloom, )); } @@ -436,8 +483,9 @@ impl EVMServices { } = dfi_intrinsics_v1_deploy_info(template.dvm_block, template.vicinity.block_number)?; executor.update_storage(address, storage)?; + executor.commit(); + template.initial_state_root = backend.commit(); } - template.backend.increase_tx_count(); Ok(()) } } @@ -477,30 +525,34 @@ impl EVMServices { let block_base_fee_per_gas = self.block.calculate_base_fee(parent_hash)?; let block_gas_limit = U256::from(self.storage.get_attributes_or_default()?.block_gas_limit); - let vicinity = Vicinity { - beneficiary, - block_number: target_block, - timestamp: U256::from(timestamp), - block_difficulty, - block_gas_limit, - block_base_fee_per_gas, - block_randomness: None, - ..Vicinity::default() - }; - - let backend = EVMBackend::from_root( + let template = BlockTemplate::new( + Vicinity { + beneficiary, + block_number: target_block, + timestamp: U256::from(timestamp), + total_gas_used: U256::zero(), + block_difficulty, + block_gas_limit, + block_base_fee_per_gas, + block_randomness: None, + ..Vicinity::default() + }, + parent_hash, + dvm_block, + timestamp, initial_state_root, - Arc::clone(&self.core.trie_store), - Arc::clone(&self.storage), - vicinity.clone(), - )?; - - let template = BlockTemplate::new(vicinity, parent_hash, dvm_block, timestamp, backend); + ); Ok(template) } - unsafe fn verify_tx_fees(&self, base_fee_per_gas: U256, tx: &ExecuteTx) -> Result<()> { + unsafe fn verify_tx_fees_in_block_template( + &self, + template: &BlockTemplate, + tx: &ExecuteTx, + ) -> Result<()> { if let ExecuteTx::SignedTx(signed_tx) = tx { + let base_fee_per_gas = template.get_block_base_fee_per_gas(); + let tx_gas_price = signed_tx.gas_price(); if tx_gas_price < base_fee_per_gas { return Err(format_err!( @@ -524,10 +576,14 @@ impl EVMServices { tx: ExecuteTx, hash: XHash, ) -> Result<()> { - self.verify_tx_fees(template.get_block_base_fee_per_gas(), &tx)?; + self.verify_tx_fees_in_block_template(template, &tx)?; let tx_update = self.update_block_template_state_from_tx(template, tx.clone())?; let tx_hash = tx_update.tx.hash(); + debug!( + "[push_tx_in_block_template] Pushing new state_root {:x?}", + tx_update.state_root + ); template.add_tx(tx_update, hash)?; self.filters.add_tx_to_filters(tx_hash); @@ -545,7 +601,7 @@ impl EVMServices { address: H160, template: &BlockTemplate, ) -> Result { - let backend = &template.backend; + let backend = self.core.get_backend(template.get_latest_state_root())?; Ok(match backend.get_account(&address) { None => false, diff --git a/lib/ain-evm/src/executor.rs b/lib/ain-evm/src/executor.rs index dcf4c25172..98ededef99 100644 --- a/lib/ain-evm/src/executor.rs +++ b/lib/ain-evm/src/executor.rs @@ -10,7 +10,7 @@ use evm::{ use log::{debug, trace}; use crate::{ - backend::{BackendError, EVMBackend}, + backend::EVMBackend, blocktemplate::ReceiptAndOptionalContractAddress, bytes::Bytes, contract::{ @@ -83,8 +83,8 @@ impl<'backend> AinExecutor<'backend> { self.backend.update_vicinity_with_gas_used(gas_used) } - pub fn commit(&mut self, is_miner: bool) -> Result { - self.backend.commit(is_miner) + pub fn commit(&mut self) -> H256 { + self.backend.commit() } pub fn get_nonce(&self, address: &H160) -> U256 { @@ -166,9 +166,6 @@ impl<'backend> AinExecutor<'backend> { } else { calculate_current_prepay_gas_fee(signed_tx, base_fee)? }; - - let old_basic = self.backend.basic(signed_tx.sender); // Keep old basic values to restore state if block size limit exceeded - if !system_tx { self.backend .deduct_prepay_gas_fee(signed_tx.sender, prepay_fee)?; @@ -211,9 +208,6 @@ impl<'backend> AinExecutor<'backend> { .ok_or_else(|| format_err!("total_gas_used overflow"))? > block_gas_limit { - self.backend - .apply(signed_tx.sender, Some(old_basic), None, Vec::new(), false) - .map_err(|e| BackendError::DeductPrepayGasFailed(e.to_string()))?; return Err(format_err!( "[exec] block size limit exceeded, tx cannot make it into the block" ) @@ -223,6 +217,7 @@ impl<'backend> AinExecutor<'backend> { let logs = logs.into_iter().collect::>(); ApplyBackend::apply(self.backend, values, logs.clone(), true); + self.backend.commit(); if !system_tx { self.backend @@ -335,6 +330,7 @@ impl<'backend> AinExecutor<'backend> { let storage = bridge_dfi(self.backend, amount, direction)?; self.update_storage(fixed_address, storage)?; self.add_balance(fixed_address, amount)?; + self.commit(); } let (tx_response, receipt) = @@ -346,6 +342,7 @@ impl<'backend> AinExecutor<'backend> { ) .into()); } + self.commit(); debug!( "[execute_tx] receipt : {:?}, exit_reason {:#?} for signed_tx : {:#x}, logs: {:x?}", @@ -397,10 +394,12 @@ impl<'backend> AinExecutor<'backend> { let DST20BridgeInfo { address, storage } = bridge_dst20_in(self.backend, contract_address, amount)?; self.update_storage(address, storage)?; + self.commit(); } let allowance = dst20_allowance(direction, signed_tx.sender, amount); self.update_storage(contract_address, allowance)?; + self.commit(); let (tx_response, receipt) = self.exec(&signed_tx, U256::MAX, U256::zero(), true)?; @@ -418,6 +417,8 @@ impl<'backend> AinExecutor<'backend> { .into()); } + self.commit(); + debug!( "[execute_tx] receipt : {:?}, exit_reason {:#?} for signed_tx : {:#x}, logs: {:x?}", receipt, diff --git a/lib/ain-evm/src/trie.rs b/lib/ain-evm/src/trie.rs index 7b828ac80a..5be5d7cdff 100644 --- a/lib/ain-evm/src/trie.rs +++ b/lib/ain-evm/src/trie.rs @@ -91,10 +91,11 @@ impl TrieDBStore { false, ) .expect("Could not set account data"); + backend.commit(); } } - let state_root = backend.commit(false)?; + let state_root = backend.commit(); debug!("Loaded genesis state_root : {:#x}", state_root); Ok((state_root, genesis)) } diff --git a/lib/ain-grpc/src/rpc/eth.rs b/lib/ain-grpc/src/rpc/eth.rs index 53ee8c9412..38ba37d860 100644 --- a/lib/ain-grpc/src/rpc/eth.rs +++ b/lib/ain-grpc/src/rpc/eth.rs @@ -405,7 +405,6 @@ impl MetachainRPCServer for MetachainRPCModule { .get_code(address, block_number) .map_err(to_jsonrpsee_custom_error)?; - debug!("code : {:?} for address {address:?}", code); match code { Some(code) => Ok(format!("0x{}", hex::encode(code))), None => Ok(String::from("0x")), diff --git a/lib/ain-rs-exports/src/evm.rs b/lib/ain-rs-exports/src/evm.rs index 236be834d5..f32bf922ea 100644 --- a/lib/ain-rs-exports/src/evm.rs +++ b/lib/ain-rs-exports/src/evm.rs @@ -3,6 +3,7 @@ use ain_contracts::{ get_transferdomain_native_transfer_function, FixedContract, }; use ain_evm::{ + blocktemplate::BlockTemplate, core::{TransferDomainTxInfo, XHash}, evm::FinalizedBlockInfo, executor::ExecuteTx, @@ -226,7 +227,7 @@ fn evm_try_unsafe_update_state_in_template( unsafe { SERVICES .evm - .update_state_in_block_template(template.get_inner_mut()?, mnview_ptr) + .update_state_in_block_template(&mut template.0, mnview_ptr) } } @@ -242,7 +243,7 @@ fn evm_try_unsafe_update_state_in_template( /// Returns the next valid nonce of the account in a specific template #[ffi_fallible] fn evm_try_unsafe_get_next_valid_nonce_in_template( - template: &BlockTemplateWrapper, + template: &mut BlockTemplateWrapper, address: &str, ) -> Result { let address = address.parse::().map_err(|_| "Invalid address")?; @@ -251,7 +252,7 @@ fn evm_try_unsafe_get_next_valid_nonce_in_template( let next_nonce = SERVICES .evm .core - .get_next_valid_nonce_in_block_template(template.get_inner()?, address)?; + .get_next_valid_nonce_in_block_template(&template.0, address)?; let nonce = u64::try_from(next_nonce)?; Ok(nonce) @@ -273,7 +274,7 @@ fn evm_try_unsafe_remove_txs_above_hash_in_template( SERVICES .evm .core - .remove_txs_above_hash_in_block_template(template.get_inner_mut()?, target_hash) + .remove_txs_above_hash_in_block_template(&mut template.0, target_hash) } } @@ -304,7 +305,7 @@ fn evm_try_unsafe_add_balance_in_template( unsafe { SERVICES .evm - .push_tx_in_block_template(template.get_inner_mut()?, exec_tx, native_hash) + .push_tx_in_block_template(&mut template.0, exec_tx, native_hash) } } @@ -336,7 +337,7 @@ fn evm_try_unsafe_sub_balance_in_template( unsafe { SERVICES .evm - .push_tx_in_block_template(template.get_inner_mut()?, exec_tx, native_hash)?; + .push_tx_in_block_template(&mut template.0, exec_tx, native_hash)?; Ok(true) } } @@ -366,15 +367,12 @@ fn evm_try_unsafe_sub_balance_in_template( /// Returns the validation result. #[ffi_fallible] fn evm_try_unsafe_validate_raw_tx_in_template( - template: &BlockTemplateWrapper, + template: &mut BlockTemplateWrapper, raw_tx: &str, ) -> Result<()> { debug!("[unsafe_validate_raw_tx_in_template]"); unsafe { - let _ = SERVICES - .evm - .core - .validate_raw_tx(raw_tx, template.get_inner()?)?; + let _ = SERVICES.evm.core.validate_raw_tx(raw_tx, &template.0)?; Ok(()) } } @@ -403,7 +401,7 @@ fn evm_try_unsafe_validate_raw_tx_in_template( /// Returns the validation result. #[ffi_fallible] fn evm_try_unsafe_validate_transferdomain_tx_in_template( - template: &BlockTemplateWrapper, + template: &mut BlockTemplateWrapper, raw_tx: &str, context: ffi::TransferDomainInfo, ) -> Result<()> { @@ -411,7 +409,7 @@ fn evm_try_unsafe_validate_transferdomain_tx_in_template( unsafe { let _ = SERVICES.evm.core.validate_raw_transferdomain_tx( raw_tx, - template.get_inner()?, + &template.0, TransferDomainTxInfo { from: context.from, to: context.to, @@ -431,7 +429,7 @@ fn block_template_err_wrapper() -> &'static mut BlockTemplateWrapper { // to never be used static mut CELL: std::cell::OnceCell = std::cell::OnceCell::new(); unsafe { - let v = CELL.get_or_init(|| BlockTemplateWrapper(None)); + let v = CELL.get_or_init(|| BlockTemplateWrapper(BlockTemplate::default())); #[allow(mutable_transmutes)] std::mem::transmute(v) } @@ -468,7 +466,7 @@ pub fn evm_try_unsafe_create_template( { Ok(template) => cross_boundary_success_return( result, - Box::leak(Box::new(BlockTemplateWrapper(Some(template)))), + Box::leak(Box::new(BlockTemplateWrapper(template))), ), Err(e) => { cross_boundary_error(result, e.to_string()); @@ -523,11 +521,9 @@ fn evm_try_unsafe_push_tx_in_template( .try_get_or_create(raw_tx)?; let tx_hash = signed_tx.hash(); - SERVICES.evm.push_tx_in_block_template( - template.get_inner_mut()?, - signed_tx.into(), - native_hash, - )?; + SERVICES + .evm + .push_tx_in_block_template(&mut template.0, signed_tx.into(), native_hash)?; Ok(ffi::ValidateTxCompletion { tx_hash: format!("{:?}", tx_hash), @@ -550,7 +546,6 @@ fn evm_try_unsafe_push_tx_in_template( #[ffi_fallible] fn evm_try_unsafe_construct_block_in_template( template: &mut BlockTemplateWrapper, - is_miner: bool, ) -> Result { unsafe { let FinalizedBlockInfo { @@ -558,9 +553,7 @@ fn evm_try_unsafe_construct_block_in_template( total_burnt_fees, total_priority_fees, block_number, - } = SERVICES - .evm - .construct_block_in_template(template.get_inner_mut()?, is_miner)?; + } = SERVICES.evm.construct_block_in_template(&mut template.0)?; let total_burnt_fees = u64::try_from(WeiAmount(total_burnt_fees).to_satoshi()?)?; let total_priority_fees = u64::try_from(WeiAmount(total_priority_fees).to_satoshi()?)?; @@ -574,8 +567,8 @@ fn evm_try_unsafe_construct_block_in_template( } #[ffi_fallible] -fn evm_try_unsafe_commit_block(template: &BlockTemplateWrapper) -> Result<()> { - unsafe { SERVICES.evm.commit_block(template.get_inner()?) } +fn evm_try_unsafe_commit_block(template: &mut BlockTemplateWrapper) -> Result<()> { + unsafe { SERVICES.evm.commit_block(&template.0) } } #[ffi_fallible] @@ -749,7 +742,7 @@ fn evm_try_unsafe_create_dst20( unsafe { SERVICES .evm - .push_tx_in_block_template(template.get_inner_mut()?, system_tx, native_hash) + .push_tx_in_block_template(&mut template.0, system_tx, native_hash) } } @@ -777,7 +770,7 @@ fn evm_try_unsafe_bridge_dst20( unsafe { SERVICES .evm - .push_tx_in_block_template(template.get_inner_mut()?, system_tx, native_hash) + .push_tx_in_block_template(&mut template.0, system_tx, native_hash) } } @@ -811,14 +804,14 @@ fn evm_try_get_tx_hash(raw_tx: &str) -> Result { #[ffi_fallible] fn evm_try_unsafe_is_smart_contract_in_template( address: &str, - template: &BlockTemplateWrapper, + template: &mut BlockTemplateWrapper, ) -> Result { let address = address.parse::().map_err(|_| "Invalid address")?; unsafe { SERVICES .evm - .is_smart_contract_in_block_template(address, template.get_inner()?) + .is_smart_contract_in_block_template(address, &template.0) } } diff --git a/lib/ain-rs-exports/src/lib.rs b/lib/ain-rs-exports/src/lib.rs index 9b1e807f17..973506ba1b 100644 --- a/lib/ain-rs-exports/src/lib.rs +++ b/lib/ain-rs-exports/src/lib.rs @@ -2,23 +2,11 @@ mod core; mod evm; mod prelude; -use ain_evm::blocktemplate::BlockTemplate; - use crate::{core::*, evm::*}; +use ain_evm::blocktemplate::BlockTemplate; -pub struct BlockTemplateWrapper(Option); - -impl BlockTemplateWrapper { - const ERROR: &'static str = "Inner block template is None"; - - fn get_inner(&self) -> Result<&BlockTemplate, &'static str> { - self.0.as_ref().ok_or(Self::ERROR) - } - - fn get_inner_mut(&mut self) -> Result<&mut BlockTemplate, &'static str> { - self.0.as_mut().ok_or(Self::ERROR) - } -} +#[derive(Debug)] +pub struct BlockTemplateWrapper(BlockTemplate); #[cxx::bridge] pub mod ffi { @@ -183,7 +171,7 @@ pub mod ffi { fn evm_try_unsafe_get_next_valid_nonce_in_template( result: &mut CrossBoundaryResult, - block_template: &BlockTemplateWrapper, + block_template: &mut BlockTemplateWrapper, address: &str, ) -> u64; @@ -209,13 +197,13 @@ pub mod ffi { fn evm_try_unsafe_validate_raw_tx_in_template( result: &mut CrossBoundaryResult, - block_template: &BlockTemplateWrapper, + block_template: &mut BlockTemplateWrapper, raw_tx: &str, ); fn evm_try_unsafe_validate_transferdomain_tx_in_template( result: &mut CrossBoundaryResult, - block_template: &BlockTemplateWrapper, + block_template: &mut BlockTemplateWrapper, raw_tx: &str, context: TransferDomainInfo, ); @@ -230,12 +218,11 @@ pub mod ffi { fn evm_try_unsafe_construct_block_in_template( result: &mut CrossBoundaryResult, block_template: &mut BlockTemplateWrapper, - is_miner: bool, ) -> FinalizeBlockCompletion; fn evm_try_unsafe_commit_block( result: &mut CrossBoundaryResult, - block_template: &BlockTemplateWrapper, + block_template: &mut BlockTemplateWrapper, ); fn evm_try_unsafe_handle_attribute_apply( @@ -301,7 +288,7 @@ pub mod ffi { fn evm_try_unsafe_is_smart_contract_in_template( result: &mut CrossBoundaryResult, address: &str, - block_template: &BlockTemplateWrapper, + block_template: &mut BlockTemplateWrapper, ) -> bool; fn evm_try_get_tx_miner_info_from_raw_tx( diff --git a/src/dfi/validation.cpp b/src/dfi/validation.cpp index 602eae8c35..cad4b1672d 100644 --- a/src/dfi/validation.cpp +++ b/src/dfi/validation.cpp @@ -2681,7 +2681,7 @@ static Res ProcessEVMQueue(const CBlock &block, } CrossBoundaryResult result; - const auto blockResult = evm_try_unsafe_construct_block_in_template(result, evmTemplate->GetTemplate(), false); + const auto blockResult = evm_try_unsafe_construct_block_in_template(result, evmTemplate->GetTemplate()); if (!result.ok) { return Res::Err(result.reason.c_str()); } diff --git a/src/miner.cpp b/src/miner.cpp index e16d9fc886..96c5294cee 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -280,8 +280,7 @@ ResVal> BlockAssembler::CreateNewBlock(const CSc XVM xvm{}; if (isEvmEnabledForBlock) { - auto res = - XResultValueLogged(evm_try_unsafe_construct_block_in_template(result, evmTemplate->GetTemplate(), true)); + auto res = XResultValueLogged(evm_try_unsafe_construct_block_in_template(result, evmTemplate->GetTemplate())); if (!res) { return Res::Err("Failed to construct block"); } diff --git a/test/functional/contracts/SelfDestruct.sol b/test/functional/contracts/SelfDestruct.sol deleted file mode 100644 index 996a7cf8eb..0000000000 --- a/test/functional/contracts/SelfDestruct.sol +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity >=0.7.0 <0.9.0; - -contract SelfDestructContract { - address public owner; - - constructor() { - owner = msg.sender; - } - - modifier onlyOwner() { - require(msg.sender == owner, "Only the owner can call this function"); - _; - } - - function killContract(address payable recipient) public onlyOwner { - selfdestruct(recipient); - } -} diff --git a/test/functional/feature_evm.py b/test/functional/feature_evm.py index 2704cb3211..e21760789e 100755 --- a/test/functional/feature_evm.py +++ b/test/functional/feature_evm.py @@ -5,7 +5,6 @@ # file LICENSE or http://www.opensource.org/licenses/mit-license.php. """Test EVM behaviour""" from test_framework.evm_key_pair import EvmKeyPair -from test_framework.evm_contract import EVMContract from test_framework.test_framework import DefiTestFramework from test_framework.util import ( assert_equal, @@ -15,7 +14,6 @@ ) from decimal import Decimal import math -from web3 import Web3 class EVMTest(DefiTestFramework): @@ -65,6 +63,40 @@ def set_test_params(self): ], ] + def run_test(self): + # Check ERC55 wallet support + self.erc55_wallet_support() + + # Test TransferDomain, OP_RETURN and EVM Gov vars + self.evm_gov_vars() + + # Fund accounts + self.setup_accounts() + + # Test block ordering by nonce and Eth RBF + self.nonce_order_and_rbf() + + # Check XVM in coinbase + self.validate_xvm_coinbase() + + # EVM rollback + self.evm_rollback() + + # Multiple mempool fee replacement + self.multiple_eth_rbf() + + # Test that node should not crash without chainId param + self.test_tx_without_chainid() + + # Test evmtx auto nonce + self.sendtransaction_auto_nonce() + + # Toggle EVM + self.toggle_evm_enablement() + + # Test Eth on encrypted wallet + self.encrypt_wallet() + def test_tx_without_chainid(self): node = self.nodes[0] @@ -709,8 +741,6 @@ def verify_transferdomain_not_enabled_post_evm_on(): assert_equal(attrs["v0/evm/block/finality_count"], "100") def setup_accounts(self): - self.evm_key_pair = EvmKeyPair.from_node(self.nodes[0]) - # Fund DFI address self.nodes[0].utxostoaccount({self.address: "300@DFI"}) self.nodes[0].generate(1) @@ -1369,152 +1399,6 @@ def encrypt_wallet(self): ) self.nodes[0].generate(1) - def delete_account_from_trie(self): - addressA = self.nodes[0].getnewaddress("", "erc55") - addressB = self.nodes[0].getnewaddress("", "erc55") - - # Fund EVM address - self.nodes[0].transferdomain( - [ - { - "src": {"address": self.address, "amount": "2@DFI", "domain": 2}, - "dst": { - "address": addressA, - "amount": "2@DFI", - "domain": 3, - }, - } - ] - ) - self.nodes[0].generate(1) - - self.nodes[0].evmtx( - addressA, 0, 21, 21001, addressB, 0 - ) # Touch addressB and trigger deletion as empty account - self.nodes[0].generate(1) - - # Deploy SelfDestruct contract to trigger deletion via SELFDESTRUCT opcode - self.nodes[0].transferdomain( - [ - { - "src": {"address": self.address, "amount": "20@DFI", "domain": 2}, - "dst": { - "address": self.evm_key_pair.address, - "amount": "20@DFI", - "domain": 3, - }, - } - ] - ) - self.nodes[0].generate(1) - - self.contract = EVMContract.from_file( - "SelfDestruct.sol", "SelfDestructContract" - ) - abi, bytecode, deployedBytecode = self.contract.compile() - compiled = self.nodes[0].w3.eth.contract(abi=abi, bytecode=bytecode) - - tx = compiled.constructor().build_transaction( - { - "chainId": self.nodes[0].w3.eth.chain_id, - "nonce": self.nodes[0].w3.eth.get_transaction_count( - self.evm_key_pair.address - ), - "maxFeePerGas": 10_000_000_000, - "maxPriorityFeePerGas": 500_000, - "gas": 1_000_000, - } - ) - signed = self.nodes[0].w3.eth.account.sign_transaction( - tx, self.evm_key_pair.privkey - ) - hash = self.nodes[0].w3.eth.send_raw_transaction(signed.rawTransaction) - - self.nodes[0].generate(1) - - receipt = self.nodes[0].w3.eth.wait_for_transaction_receipt(hash) - self.contract_address = receipt["contractAddress"] - self.contract = self.nodes[0].w3.eth.contract( - address=receipt["contractAddress"], abi=abi - ) - - codeBefore = self.nodes[0].eth_getCode(self.contract_address) - assert_equal( - codeBefore[2:], - deployedBytecode, - ) - storageBefore = self.nodes[0].eth_getStorageAt(self.contract_address, "0x0") - assert_equal( - Web3.to_checksum_address(storageBefore[26:]), - self.evm_key_pair.address, - ) - - tx = self.contract.functions.killContract(addressA).build_transaction( - { - "chainId": self.nodes[0].w3.eth.chain_id, - "nonce": self.nodes[0].w3.eth.get_transaction_count( - self.evm_key_pair.address - ), - "gasPrice": 10_000_000_000, - "gas": 10_000_000, - } - ) - signed = self.nodes[0].w3.eth.account.sign_transaction( - tx, self.evm_key_pair.privkey - ) - hash = self.nodes[0].w3.eth.send_raw_transaction(signed.rawTransaction) - self.nodes[0].generate(1) - - codeAfterSelfDestruct = self.nodes[0].eth_getCode(self.contract_address) - assert_equal( - codeAfterSelfDestruct, - "0x", - ) - storageAfterSelfDestruct = self.nodes[0].eth_getStorageAt( - self.contract_address, "0x0" - ) - assert_equal( - storageAfterSelfDestruct, - "0x0000000000000000000000000000000000000000000000000000000000000000", - ) - - def run_test(self): - # Check ERC55 wallet support - self.erc55_wallet_support() - - # Test TransferDomain, OP_RETURN and EVM Gov vars - self.evm_gov_vars() - - # Fund accounts - self.setup_accounts() - - # Test block ordering by nonce and Eth RBF - self.nonce_order_and_rbf() - - # Check XVM in coinbase - self.validate_xvm_coinbase() - - # EVM rollback - self.evm_rollback() - - # Multiple mempool fee replacement - self.multiple_eth_rbf() - - # Test that node should not crash without chainId param - self.test_tx_without_chainid() - - # Test evmtx auto nonce - self.sendtransaction_auto_nonce() - - # Toggle EVM - self.toggle_evm_enablement() - - # Test Eth on encrypted wallet - self.encrypt_wallet() - - # Delete state account - self.delete_account_from_trie() - if __name__ == "__main__": EVMTest().main() diff --git a/test/functional/feature_evm_gas.py b/test/functional/feature_evm_gas.py index 13444e3389..d7f675ecc3 100644 --- a/test/functional/feature_evm_gas.py +++ b/test/functional/feature_evm_gas.py @@ -83,7 +83,7 @@ def setup(self): } } ) - self.nodes[0].generate(2) + self.nodes[0].generate(1) self.nodes[0] = self.nodes[0] self.evm_key_pair = EvmKeyPair.from_node(self.nodes[0]) diff --git a/test/functional/feature_evm_rollback.py b/test/functional/feature_evm_rollback.py index 4631351c24..84c7f01758 100755 --- a/test/functional/feature_evm_rollback.py +++ b/test/functional/feature_evm_rollback.py @@ -74,7 +74,7 @@ def setup(self): } } ) - self.nodes[0].generate(2) + self.nodes[0].generate(1) self.nodes[0].transferdomain( [ { diff --git a/test/functional/feature_evm_transferdomain.py b/test/functional/feature_evm_transferdomain.py index c74a426582..40a481b9df 100755 --- a/test/functional/feature_evm_transferdomain.py +++ b/test/functional/feature_evm_transferdomain.py @@ -210,7 +210,7 @@ def invalid_before_fork_and_disabled(self): } } ) - self.nodes[0].generate(2) + self.nodes[0].generate(1) assert_raises_rpc_error( -32600,