Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ndev 3467 perf bug fix #594

Merged
merged 19 commits into from
Jan 30, 2025
Merged
1 change: 1 addition & 0 deletions evm_loader/lib/src/commands/emulate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,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);
Expand Down
33 changes: 28 additions & 5 deletions evm_loader/lib/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ 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]
Expand All @@ -29,14 +33,34 @@ pub trait Rpc {
) -> ClientResult<Option<Account>>;

async fn get_last_deployed_slot(&self, program_id: &Pubkey) -> ClientResult<Option<u64>> {
let slice_len = max(
std::mem::size_of::<UpgradeableLoaderState>(),
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) {
return Ok(Some(0));
} else if bpf_loader_upgradeable::check_id(&acc.owner) {
return match deserialize::<UpgradeableLoaderState>(&acc.data) {
Ok(UpgradeableLoaderState::Program {
programdata_address,
..
}) => self.get_last_deployed_slot(&programdata_address).await,
Ok(UpgradeableLoaderState::ProgramData { slot, .. }) => Ok(Some(slot)),
Ok(_) => Ok(None),
Err(_) => {
Err(ClientErrorKind::Custom("Data corruption error? ".to_string()).into())
}
};
}

return Ok(None);
}
Err(ClientErrorKind::Custom("Not account on slot ".to_string()).into())
}
Expand Down Expand Up @@ -85,7 +109,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(
Expand Down
5 changes: 3 additions & 2 deletions evm_loader/lib/src/tracing/tracers/call_tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
5 changes: 5 additions & 0 deletions evm_loader/lib/src/tracing/tracers/struct_logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
82 changes: 48 additions & 34 deletions evm_loader/lib/src/types/programs_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value> = HashMap<KeyAccountCache, Value>;

Expand Down Expand Up @@ -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<Account> {
let key = KeyAccountCache { addr, slot };
programdata_account_cache_get_instance().await.get(&key)
async fn programdata_account_cache_get(key: &KeyAccountCache) -> Option<Account> {
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<Option<u64>> {
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<Option<Pubkey>> {
assert!(!bpf_loader_upgradeable::check_id(&acc.owner), "NOT AN ACC");

match deserialize::<UpgradeableLoaderState>(&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<Option<u64>> {
assert!(bpf_loader_upgradeable::check_id(&acc.owner), "NOT AN ACC");
match deserialize::<UpgradeableLoaderState>(&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.");
Expand Down Expand Up @@ -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 {
Expand All @@ -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:?}");
}
}
}
Expand Down Expand Up @@ -320,24 +352,6 @@ mod tests {
.is_none());
}

#[test]
fn test_add_and_get_value() {
let cache: ThreadSaveCache<String> = 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<String> = ThreadSaveCache::new();
Expand Down
52 changes: 29 additions & 23 deletions evm_loader/program/src/evm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ impl<B: Database, T: EventListener> Machine<B, T> {
}
}

#[allow(unused_mut)]
#[maybe_async]
async fn new_call(
chain_id: u64,
Expand All @@ -266,8 +267,7 @@ impl<B: Database, T: EventListener> Machine<B, T> {
.await?;

let execution_code = backend.code(target).await?;

Ok(Self {
let mut machine = Self {
origin,
chain_id,
context: Context {
Expand All @@ -291,9 +291,23 @@ impl<B: Database, T: EventListener> Machine<B, T> {
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<T> {
self.tracer.take()
}

#[allow(unused_mut)]
#[maybe_async]
async fn new_create(
chain_id: u64,
Expand All @@ -318,8 +332,7 @@ impl<B: Database, T: EventListener> Machine<B, T> {
backend
.transfer(origin, target, chain_id, trx.value())
.await?;

Ok(Self {
let mut machine = Self {
origin,
chain_id,
context: Context {
Expand All @@ -343,7 +356,17 @@ impl<B: Database, T: EventListener> Machine<B, T> {
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]
Expand All @@ -354,23 +377,6 @@ impl<B: Database, T: EventListener> Machine<B, T> {
) -> Result<(ExitStatus, u64, Option<T>)> {
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();

Expand Down
Loading