diff --git a/evm_loader/lib/src/account_storage_tests.rs b/evm_loader/lib/src/account_storage_tests.rs index 5445f3e15..5fceed714 100644 --- a/evm_loader/lib/src/account_storage_tests.rs +++ b/evm_loader/lib/src/account_storage_tests.rs @@ -4,24 +4,26 @@ use crate::tracing::AccountOverride; use evm_loader::types::vector::VectorVecExt; use hex_literal::hex; use solana_account_decoder::UiDataSliceConfig; + use std::collections::HashMap; use std::str::FromStr; - const STORAGE_LENGTH: usize = 32 * STORAGE_ENTRIES_IN_CONTRACT_ACCOUNT; mod mock_rpc_client { + use crate::types::programs_cache::get_programdata_slot_from_account; + use solana_sdk::bpf_loader_upgradeable::UpgradeableLoaderState; + use crate::commands::get_config::BuildConfigSimulator; use crate::NeonResult; use crate::{commands::get_config::ConfigSimulator, rpc::Rpc}; use async_trait::async_trait; + use solana_account_decoder::UiDataSliceConfig; use solana_client::client_error::Result as ClientResult; use solana_sdk::account::Account; use solana_sdk::clock::{Slot, UnixTimestamp}; use solana_sdk::pubkey::Pubkey; use std::collections::HashMap; - use solana_account_decoder::UiDataSliceConfig; - pub struct MockRpcClient { accounts: HashMap, } @@ -91,8 +93,17 @@ mod mock_rpc_client { #[async_trait(?Send)] impl BuildConfigSimulator for MockRpcClient { - fn use_cache(&self) -> bool { - false + async fn get_last_deployed_slot(&self, program_id: &Pubkey) -> u64 { + let slice = UiDataSliceConfig { + offset: 0, + length: UpgradeableLoaderState::size_of_programdata_metadata(), + }; + let result = self.get_account_slice(program_id, Some(slice)).await; + if let Ok(Some(acc)) = result { + get_programdata_slot_from_account(&acc).expect("NO slot value for acc") + } else { + panic!("get_account_slice return an Error "); + } } async fn build_config_simulator(&self, _program_id: Pubkey) -> NeonResult { unimplemented!(); diff --git a/evm_loader/lib/src/commands/get_config.rs b/evm_loader/lib/src/commands/get_config.rs index 9fde6a2f6..c5d2085e7 100644 --- a/evm_loader/lib/src/commands/get_config.rs +++ b/evm_loader/lib/src/commands/get_config.rs @@ -2,21 +2,24 @@ use std::collections::BTreeMap; +use crate::rpc::{CallDbClient, CloneRpcClient, Rpc}; +use crate::solana_simulator::SolanaSimulator; +use crate::types::programs_cache::{get_programdata_slot_from_account, KeyAccountCache}; +use crate::types::programs_cache::{program_config_cache_add, program_config_cache_get}; use async_trait::async_trait; use base64::Engine; use enum_dispatch::enum_dispatch; +use evm_loader::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; +pub use solana_account_decoder::UiDataSliceConfig as SliceConfig; use solana_client::rpc_config::RpcSimulateTransactionConfig; use solana_sdk::signer::Signer; use solana_sdk::{instruction::Instruction, pubkey::Pubkey, transaction::Transaction}; -use tokio::sync::OnceCell; -use crate::rpc::{CallDbClient, CloneRpcClient}; -use crate::solana_simulator::SolanaSimulator; use crate::NeonResult; -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub enum Status { Ok, Emergency, @@ -33,7 +36,7 @@ pub struct ChainInfo { } #[serde_as] -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct GetConfigResponse { pub version: String, pub revision: String, @@ -58,16 +61,51 @@ pub enum ConfigSimulator<'r> { #[async_trait(?Send)] #[enum_dispatch] pub trait BuildConfigSimulator { - fn use_cache(&self) -> bool; + async fn get_last_deployed_slot(&self, program_id: &Pubkey) -> u64; + + async fn get_config_simulator(&self, program_id: Pubkey) -> NeonResult { + let key = KeyAccountCache { + addr: program_id, + slot: self.get_last_deployed_slot(&program_id).await, + }; + + let rz = program_config_cache_get(&key).await; + if rz.is_some() { + return Ok(rz.unwrap()); + }; + let mut simulator = self.build_config_simulator(program_id).await?; + + let (version, revision) = simulator.get_version().await?; + + let result = GetConfigResponse { + version, + revision, + status: simulator.get_status().await?, + environment: simulator.get_environment().await?, + chains: simulator.get_chains().await?, + config: simulator.get_properties().await?, + }; + program_config_cache_add(key, result.clone()).await; + Ok(result) + } + async fn build_config_simulator(&self, program_id: Pubkey) -> NeonResult; } #[async_trait(?Send)] impl BuildConfigSimulator for CloneRpcClient { - fn use_cache(&self) -> bool { - true + async fn get_last_deployed_slot(&self, program_id: &Pubkey) -> u64 { + let slice = SliceConfig { + offset: 0, + length: UpgradeableLoaderState::size_of_programdata_metadata(), + }; + let result = self.get_account_slice(program_id, Some(slice)).await; + if let Ok(Some(acc)) = result { + get_programdata_slot_from_account(&acc).expect("NO slot value for acc") + } else { + panic!("get_account_slice return an Error "); + } } - async fn build_config_simulator(&self, program_id: Pubkey) -> NeonResult { Ok(ConfigSimulator::CloneRpcClient { program_id, @@ -78,13 +116,13 @@ impl BuildConfigSimulator for CloneRpcClient { #[async_trait(?Send)] impl BuildConfigSimulator for CallDbClient { - fn use_cache(&self) -> bool { - false + async fn get_last_deployed_slot(&self, _program_id: &Pubkey) -> u64 { + 0 } - async fn build_config_simulator(&self, program_id: Pubkey) -> NeonResult { let mut simulator = SolanaSimulator::new_without_sync(self).await?; simulator.sync_accounts(self, &[program_id]).await?; + Ok(ConfigSimulator::ProgramTestContext { program_id, simulator, @@ -264,44 +302,16 @@ pub async fn execute( rpc: &impl BuildConfigSimulator, program_id: Pubkey, ) -> NeonResult { - let mut simulator = rpc.build_config_simulator(program_id).await?; - - let (version, revision) = simulator.get_version().await?; - - Ok(GetConfigResponse { - version, - revision, - status: simulator.get_status().await?, - environment: simulator.get_environment().await?, - chains: simulator.get_chains().await?, - config: simulator.get_properties().await?, - }) + rpc.get_config_simulator(program_id).await } -static CHAINS_CACHE: OnceCell> = OnceCell::const_new(); +// static CHAINS_CACHE: OnceCell> = OnceCell::const_new(); pub async fn read_chains( rpc: &impl BuildConfigSimulator, program_id: Pubkey, ) -> NeonResult> { - if rpc.use_cache() { - return CHAINS_CACHE - .get_or_try_init(|| get_chains(rpc, program_id)) - .await - .cloned(); - } - - get_chains(rpc, program_id).await -} - -async fn get_chains( - rpc: &(impl BuildConfigSimulator + Sized), - program_id: Pubkey, -) -> NeonResult> { - rpc.build_config_simulator(program_id) - .await? - .get_chains() - .await + Ok(rpc.get_config_simulator(program_id).await?.chains) } pub async fn read_legacy_chain_id( diff --git a/evm_loader/lib/src/rpc/mod.rs b/evm_loader/lib/src/rpc/mod.rs index d14e1763c..3f2696b3b 100644 --- a/evm_loader/lib/src/rpc/mod.rs +++ b/evm_loader/lib/src/rpc/mod.rs @@ -2,12 +2,11 @@ mod db_call_client; mod emulator_client; mod validator_client; -pub use db_call_client::CallDbClient; -pub use validator_client::CloneRpcClient; - +use crate::commands::get_config::GetConfigResponse; use crate::commands::get_config::{BuildConfigSimulator, ConfigSimulator}; use crate::{NeonError, NeonResult}; use async_trait::async_trait; +pub use db_call_client::CallDbClient; use enum_dispatch::enum_dispatch; pub use solana_account_decoder::UiDataSliceConfig as SliceConfig; use solana_cli::cli::CliError; @@ -19,6 +18,7 @@ use solana_sdk::{ native_token::lamports_to_sol, pubkey::Pubkey, }; +pub use validator_client::CloneRpcClient; #[async_trait(?Send)] #[enum_dispatch] diff --git a/evm_loader/lib/src/types/programs_cache.rs b/evm_loader/lib/src/types/programs_cache.rs index a438b5b60..b07ef1782 100644 --- a/evm_loader/lib/src/types/programs_cache.rs +++ b/evm_loader/lib/src/types/programs_cache.rs @@ -2,6 +2,7 @@ use crate::rpc::Rpc; use async_trait::async_trait; +use crate::commands::get_config::GetConfigResponse; use bincode::deserialize; use futures::future::join_all; use solana_client::client_error::{ClientErrorKind, Result as ClientResult}; @@ -20,15 +21,18 @@ use tokio::sync::OnceCell; use tracing::info; #[derive(Debug, Eq, PartialEq, Hash)] pub struct KeyAccountCache { - addr: Pubkey, - slot: u64, + pub addr: Pubkey, + pub slot: u64, } - use crate::rpc::SliceConfig; -type ProgramDataCache = HashMap; -type ThreadSaveProgramDataCache = RwLock; +type ProgramDataCache = HashMap; +type ThreadSaveCache = RwLock>; + +type ThreadSaveProgramDataCache = ThreadSaveCache; +type ThreadSaveConfigCache<'a> = ThreadSaveCache; -static LOCAL_CONFIG: OnceCell = OnceCell::const_new(); +static ACCOUNT_CACHE_TABLE: OnceCell = OnceCell::const_new(); +static CONFIG_CACHE_TABLE: OnceCell = OnceCell::const_new(); pub async fn cut_programdata_from_acc(account: &mut Account, data_slice: SliceConfig) { if data_slice.offset != 0 { @@ -38,9 +42,25 @@ pub async fn cut_programdata_from_acc(account: &mut Account, data_slice: SliceCo } account.data.truncate(data_slice.length); } - -async fn programdata_hash_get_instance() -> &'static ThreadSaveProgramDataCache { - LOCAL_CONFIG +fn cache_get( + key: &KeyAccountCache, + table: &ThreadSaveCache, +) -> Option { + table + .read() + .expect("acc_hash_get_instance poisoned") + .get(key) + .cloned() +} +fn cache_add( + key: KeyAccountCache, + value: Value, + table: &ThreadSaveCache, +) { + table.write().expect("PANIC, no space ").insert(key, value); +} +async fn programdata_account_cache_get_instance() -> &'static ThreadSaveProgramDataCache { + ACCOUNT_CACHE_TABLE .get_or_init(|| async { let map = HashMap::new(); @@ -49,26 +69,17 @@ async fn programdata_hash_get_instance() -> &'static ThreadSaveProgramDataCache .await } -async fn programdata_hash_get(addr: Pubkey, slot: u64) -> Option { +async fn programdata_account_cache_get(addr: Pubkey, slot: u64) -> Option { let val = KeyAccountCache { addr, slot }; - programdata_hash_get_instance() - .await - .read() - .expect("acc_hash_get_instance poisoned") - .get(&val) - .cloned() + cache_get(&val, programdata_account_cache_get_instance().await) } -async fn programdata_hash_add(addr: Pubkey, slot: u64, acc: Account) { - let val = KeyAccountCache { addr, slot }; - programdata_hash_get_instance() - .await - .write() - .expect("PANIC, no nable") - .insert(val, acc); +async fn programdata_account_cache_add(addr: Pubkey, slot: u64, acc: Account) { + let key = KeyAccountCache { addr, slot }; + cache_add(key, acc, programdata_account_cache_get_instance().await ); } -fn get_programdata_slot_from_account(acc: &Account) -> ClientResult { +pub fn get_programdata_slot_from_account(acc: &Account) -> ClientResult { if !bpf_loader_upgradeable::check_id(&acc.owner) { return Err(ClientErrorKind::Custom("Not upgradeable account".to_string()).into()); } @@ -112,11 +123,11 @@ pub async fn programdata_cache_get_values_by_keys( match result { Ok(Some(account)) => { let slot_val = get_programdata_slot_from_account(account)?; - if let Some(acc) = programdata_hash_get(*key, slot_val).await { + if let Some(acc) = programdata_account_cache_get(*key, slot_val).await { answer.push(Some(acc)); } else if let Ok(Some(tmp_acc)) = rpc.get_account(key).await { let current_slot = get_programdata_slot_from_account(&tmp_acc)?; - programdata_hash_add(*key, current_slot, tmp_acc.clone()).await; + programdata_account_cache_add(*key, current_slot, tmp_acc.clone()).await; answer.push(Some(tmp_acc)); } else { @@ -169,6 +180,24 @@ impl FakeRpc { } } +async fn program_config_cache_get_instance() -> &'static ThreadSaveConfigCache<'static> { + CONFIG_CACHE_TABLE + .get_or_init(|| async { + let map = HashMap::new(); + + RwLock::new(map) + }) + .await +} + +pub async fn program_config_cache_get(key: &KeyAccountCache) -> Option { + cache_get(key, program_config_cache_get_instance().await) +} + +pub async fn program_config_cache_add(key: KeyAccountCache, val: GetConfigResponse) { + cache_add(key, val, program_config_cache_get_instance().await); +} + #[async_trait(?Send)] impl Rpc for FakeRpc {