diff --git a/evm_loader/lib/src/commands/emulate.rs b/evm_loader/lib/src/commands/emulate.rs index e37ea8b46..796ed225f 100644 --- a/evm_loader/lib/src/commands/emulate.rs +++ b/evm_loader/lib/src/commands/emulate.rs @@ -438,6 +438,7 @@ async fn emulate_trx_multiple_steps<'rpc, T: Tracer>( } if execution_step.is_reset { + tracer_result = evm.take_tracer(); drop(evm); drop(backend); drop(storage); diff --git a/evm_loader/lib/src/rpc/mod.rs b/evm_loader/lib/src/rpc/mod.rs index 5bac8d306..bd147b3d6 100644 --- a/evm_loader/lib/src/rpc/mod.rs +++ b/evm_loader/lib/src/rpc/mod.rs @@ -4,19 +4,24 @@ mod validator_client; use crate::commands::get_config::GetConfigResponse; pub use db_call_client::CallDbClient; +use tracing::trace; pub use validator_client::CloneRpcClient; use crate::commands::get_config::{BuildConfigSimulator, ConfigSimulator}; use crate::{NeonError, NeonResult}; use async_trait::async_trait; + +use bincode::deserialize; use enum_dispatch::enum_dispatch; use evm_loader::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState; pub use solana_account_decoder::UiDataSliceConfig as SliceConfig; use solana_cli::cli::CliError; use solana_client::client_error::{ClientErrorKind, Result as ClientResult}; use solana_sdk::{ - account::Account, message::Message, native_token::lamports_to_sol, pubkey::Pubkey, + account::Account, bpf_loader, bpf_loader_upgradeable, message::Message, + native_token::lamports_to_sol, pubkey::Pubkey, }; +use std::cmp::max; #[async_trait(?Send)] #[enum_dispatch] @@ -28,14 +33,43 @@ pub trait Rpc { ) -> ClientResult>; async fn get_last_deployed_slot(&self, program_id: &Pubkey) -> ClientResult> { + let slice_len = max( + std::mem::size_of::(), + UpgradeableLoaderState::size_of_programdata_metadata(), + ); let slice = SliceConfig { offset: 0, - length: UpgradeableLoaderState::size_of_programdata_metadata(), + length: slice_len, }; let result = self.get_account_slice(program_id, Some(slice)).await; + if let Ok(Some(acc)) = result { - let slot = get_programdata_slot_from_account(&acc)?; - return Ok(slot); + // check if not upgradeable + + if bpf_loader::check_id(&acc.owner) { + trace!("Account {program_id} is program not upgradeable"); + return Ok(Some(0)); + } else if bpf_loader_upgradeable::check_id(&acc.owner) { + return match deserialize::(&acc.data) { + Ok(UpgradeableLoaderState::Program { + programdata_address, + .. + }) => self.get_last_deployed_slot(&programdata_address).await, + Ok(UpgradeableLoaderState::ProgramData { slot, .. }) => { + trace!("Account {program_id} is programdata with slot {slot} "); + Ok(Some(slot)) + } + Ok(_) => Err(ClientErrorKind::Custom( + "Not program nither programdata ".to_string(), + ) + .into()), + Err(_) => { + Err(ClientErrorKind::Custom("Data corruption error? ".to_string()).into()) + } + }; + } + trace!("Account {program_id} some troulbes and return None "); + return Ok(None); } Err(ClientErrorKind::Custom("Not account on slot ".to_string()).into()) } @@ -71,7 +105,6 @@ macro_rules! e { }; } -use crate::types::programs_cache::get_programdata_slot_from_account; pub(crate) use e; pub(crate) async fn check_account_for_fee( diff --git a/evm_loader/lib/src/tracing/tracers/call_tracer.rs b/evm_loader/lib/src/tracing/tracers/call_tracer.rs index eac19027b..26eecb2a6 100644 --- a/evm_loader/lib/src/tracing/tracers/call_tracer.rs +++ b/evm_loader/lib/src/tracing/tracers/call_tracer.rs @@ -294,9 +294,10 @@ impl CallTracer { impl Tracer for CallTracer { fn into_traces(mut self, emulator_gas_used: u64) -> Value { + let call_stack_len = self.call_stack.len(); assert!( - self.call_stack.len() == 1, - "incorrect number of top-level calls" + call_stack_len == 1, + "incorrect number of top-level calls, call_stack_len== {call_stack_len}, and it must be 1 . (emulator_gas_used: {emulator_gas_used})" ); let call_frame = &mut self.call_stack[0]; diff --git a/evm_loader/lib/src/tracing/tracers/struct_logger.rs b/evm_loader/lib/src/tracing/tracers/struct_logger.rs index c512a67b7..69fa9e050 100644 --- a/evm_loader/lib/src/tracing/tracers/struct_logger.rs +++ b/evm_loader/lib/src/tracing/tracers/struct_logger.rs @@ -196,6 +196,11 @@ impl EventListener for StructLogger { impl Tracer for StructLogger { fn into_traces(self, emulator_gas_used: u64) -> Value { + let call_stack_len = self.depth; + assert!( + call_stack_len == 0, + "incorrect number of top-level calls {call_stack_len} - StructLogger depth should be 0 " + ); let exit_status = self.exit_status.expect("Exit status should be set"); let result = StructLoggerResult { gas: self.actual_gas_used.map_or(emulator_gas_used, U256::as_u64), diff --git a/evm_loader/lib/src/types/programs_cache.rs b/evm_loader/lib/src/types/programs_cache.rs index 8b9d623ba..9773ba8b5 100644 --- a/evm_loader/lib/src/types/programs_cache.rs +++ b/evm_loader/lib/src/types/programs_cache.rs @@ -23,6 +23,13 @@ pub struct KeyAccountCache { pub addr: Pubkey, pub slot: u64, } +impl KeyAccountCache { + #[must_use] + + pub const fn new(addr: &Pubkey, slot: u64) -> Self { + Self { addr: *addr, slot } + } +} type ProgramDataCache = HashMap; @@ -78,25 +85,48 @@ async fn programdata_account_cache_get_instance() -> &'static ThreadSaveProgramD .await } -async fn programdata_account_cache_get(addr: Pubkey, slot: u64) -> Option { - let key = KeyAccountCache { addr, slot }; - programdata_account_cache_get_instance().await.get(&key) +async fn programdata_account_cache_get(key: &KeyAccountCache) -> Option { + programdata_account_cache_get_instance().await.get(key) } -async fn programdata_account_cache_add(addr: Pubkey, slot: u64, acc: Account) { - let key = KeyAccountCache { addr, slot }; +async fn programdata_account_cache_add(key: KeyAccountCache, acc: Account) { programdata_account_cache_get_instance().await.add(key, acc); } -/// in case of Not upgradeable account - return option None -pub fn get_programdata_slot_from_account(acc: &Account) -> ClientResult> { - if !bpf_loader_upgradeable::check_id(&acc.owner) { - return Ok(None); +/// in case of Not upgradeable account - return option None +pub fn get_program_programdata_address(acc: &Account) -> ClientResult> { + assert!(!bpf_loader_upgradeable::check_id(&acc.owner), "NOT AN ACC"); + + match deserialize::(&acc.data) { + Ok(UpgradeableLoaderState::Program { + programdata_address, + .. + }) => Ok(Some(programdata_address)), + Ok(_) => { + panic!("Unexpected account type! Only Program type is acceptable "); + } + Err(e) => { + eprintln!("Error occurred: {e:?}"); + panic!("Failed to deserialize account data."); + } } +} +pub fn get_programdata_slot_from_account(acc: &Account) -> ClientResult> { + assert!(bpf_loader_upgradeable::check_id(&acc.owner), "NOT AN ACC"); match deserialize::(&acc.data) { Ok(UpgradeableLoaderState::ProgramData { slot, .. }) => Ok(Some(slot)), - Ok(_) => Ok(None), + Ok(UpgradeableLoaderState::Program { + programdata_address, + .. + }) => { + info!(" programdata_address:{programdata_address}"); + Ok(Some(0)) + } + + Ok(_) => { + panic!("Unexpected account type! Only ProgramData type is acceptable "); + } Err(e) => { eprintln!("Error occurred: {e:?}"); panic!("Failed to deserialize account data."); @@ -127,16 +157,18 @@ pub async fn programdata_cache_get_values_by_keys( "programdata_keys.size()!=future_requests.size()" ); let results = join_all(future_requests).await; - for (result, key) in results.iter().zip(programdata_keys) { + for (result, addr) in results.iter().zip(programdata_keys) { match result { Ok(Some(account)) => { if let Some(slot_val) = get_programdata_slot_from_account(account)? { - if let Some(acc) = programdata_account_cache_get(*key, slot_val).await { + let key = KeyAccountCache::new(addr, slot_val); + if let Some(acc) = programdata_account_cache_get(&key).await { answer.push(Some(acc)); - } else if let Ok(Some(tmp_acc)) = rpc.get_account(key).await { + } else if let Ok(Some(tmp_acc)) = rpc.get_account(&key.addr).await { let current_slot = get_programdata_slot_from_account(&tmp_acc)?.expect("No current slot "); - programdata_account_cache_add(*key, current_slot, tmp_acc.clone()).await; + let key = KeyAccountCache::new(addr, current_slot); + programdata_account_cache_add(key, tmp_acc.clone()).await; answer.push(Some(tmp_acc)); } else { @@ -147,11 +179,11 @@ pub async fn programdata_cache_get_values_by_keys( } } Ok(None) => { - info!("Account for key {key:?} is None."); + info!("Account for key {addr:?} is None."); answer.push(None); } Err(e) => { - info!("Error fetching account for key {key:?}: {e:?}"); + info!("Error fetching account for key {addr:?}: {e:?}"); } } } @@ -320,24 +352,6 @@ mod tests { .is_none()); } - #[test] - fn test_add_and_get_value() { - let cache: ThreadSaveCache = ThreadSaveCache::new(); - let key = KeyAccountCache { - slot: 0, - addr: Pubkey::new_unique(), - }; - let value = "test_value".to_string(); - - // Add the value to the cache - cache.add(key.clone(), value.clone()); - - // Retrieve the value from the cache - let result = cache.get(&key); - assert!(result.is_some()); - assert_eq!(result.unwrap(), value); - } - #[test] fn test_get_nonexistent_key() { let cache: ThreadSaveCache = ThreadSaveCache::new(); diff --git a/evm_loader/program/src/evm/mod.rs b/evm_loader/program/src/evm/mod.rs index 77d8e36c5..c35481213 100644 --- a/evm_loader/program/src/evm/mod.rs +++ b/evm_loader/program/src/evm/mod.rs @@ -246,6 +246,7 @@ impl Machine { } } + #[allow(unused_mut)] #[maybe_async] async fn new_call( chain_id: u64, @@ -266,8 +267,7 @@ impl Machine { .await?; let execution_code = backend.code(target).await?; - - Ok(Self { + let mut machine = Self { origin, chain_id, context: Context { @@ -291,9 +291,23 @@ impl Machine { parent: None, phantom: PhantomData, tracer, - }) + }; + begin_vm!( + machine, + backend, + machine.context, + machine.chain_id, + machine.call_data.to_vec(), + opcode_table::CALL + ); + + Ok(machine) + } + pub fn take_tracer(&mut self) -> Option { + self.tracer.take() } + #[allow(unused_mut)] #[maybe_async] async fn new_create( chain_id: u64, @@ -318,8 +332,7 @@ impl Machine { backend .transfer(origin, target, chain_id, trx.value()) .await?; - - Ok(Self { + let mut machine = Self { origin, chain_id, context: Context { @@ -343,7 +356,17 @@ impl Machine { parent: None, phantom: PhantomData, tracer, - }) + }; + begin_vm!( + machine, + backend, + machine.context, + machine.chain_id, + machine.execution_code.to_vec(), + opcode_table::CREATE + ); + + Ok(machine) } #[maybe_async] @@ -354,23 +377,6 @@ impl Machine { ) -> Result<(ExitStatus, u64, Option)> { let mut step = 0_u64; - begin_vm!( - self, - backend, - self.context, - self.chain_id, - if self.reason == Reason::Call { - self.call_data.to_vec() - } else { - self.execution_code.to_vec() - }, - if self.reason == Reason::Call { - opcode_table::CALL - } else { - opcode_table::CREATE - } - ); - let status = if is_precompile_address(&self.context.contract) { let value = Self::precompile(&self.context.contract, &self.call_data).unwrap();