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 3386 caching build config #581

Merged
merged 13 commits into from
Dec 17, 2024
8 changes: 4 additions & 4 deletions evm_loader/lib/src/account_storage_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ 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::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<Pubkey, Account>,
}
Expand Down Expand Up @@ -91,7 +91,7 @@ mod mock_rpc_client {

#[async_trait(?Send)]
impl BuildConfigSimulator for MockRpcClient {
fn use_cache(&self) -> bool {
fn use_cache_for_chains(&self) -> bool {
false
}
async fn build_config_simulator(&self, _program_id: Pubkey) -> NeonResult<ConfigSimulator> {
Expand Down
105 changes: 62 additions & 43 deletions evm_loader/lib/src/commands/get_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@

use std::collections::BTreeMap;

use crate::rpc::{CallDbClient, CloneRpcClient, Rpc};
use crate::solana_simulator::SolanaSimulator;
use crate::types::programs_cache::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 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)]
use tokio::sync::OnceCell;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Status {
Ok,
Emergency,
Expand All @@ -33,7 +35,7 @@ pub struct ChainInfo {
}

#[serde_as]
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetConfigResponse {
pub version: String,
pub revision: String,
Expand All @@ -57,17 +59,52 @@ pub enum ConfigSimulator<'r> {

#[async_trait(?Send)]
#[enum_dispatch]
pub trait BuildConfigSimulator {
fn use_cache(&self) -> bool;
pub trait BuildConfigSimulator: Rpc {
fn use_cache_for_chains(&self) -> bool;
async fn get_config(&self, program_id: Pubkey) -> NeonResult<GetConfigResponse> {
let maybe_slot = self.get_last_deployed_slot(&program_id).await?;
if let Some(slot) = maybe_slot {
let key = KeyAccountCache {
addr: program_id,
slot,
};

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?,
};
if let Some(slot) = maybe_slot {
let key = KeyAccountCache {
addr: program_id,
slot,
};
program_config_cache_add(key, result.clone()).await;
}

Ok(result)
}

async fn build_config_simulator(&self, program_id: Pubkey) -> NeonResult<ConfigSimulator>;
}

#[async_trait(?Send)]
impl BuildConfigSimulator for CloneRpcClient {
fn use_cache(&self) -> bool {
fn use_cache_for_chains(&self) -> bool {
true
}

async fn build_config_simulator(&self, program_id: Pubkey) -> NeonResult<ConfigSimulator> {
Ok(ConfigSimulator::CloneRpcClient {
program_id,
Expand All @@ -78,13 +115,13 @@ impl BuildConfigSimulator for CloneRpcClient {

#[async_trait(?Send)]
impl BuildConfigSimulator for CallDbClient {
fn use_cache(&self) -> bool {
fn use_cache_for_chains(&self) -> bool {
false
}

async fn build_config_simulator(&self, program_id: Pubkey) -> NeonResult<ConfigSimulator> {
let mut simulator = SolanaSimulator::new_without_sync(self).await?;
simulator.sync_accounts(self, &[program_id]).await?;

Ok(ConfigSimulator::ProgramTestContext {
program_id,
simulator,
Expand Down Expand Up @@ -259,49 +296,31 @@ impl ConfigSimulator<'_> {
Ok(result)
}
}

static CHAINS_CACHE: OnceCell<Vec<ChainInfo>> = OnceCell::const_new();
pub async fn execute(
rpc: &impl BuildConfigSimulator,
program_id: Pubkey,
) -> NeonResult<GetConfigResponse> {
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(program_id).await
}

static CHAINS_CACHE: OnceCell<Vec<ChainInfo>> = OnceCell::const_new();

pub async fn read_chains(
rpc: &impl BuildConfigSimulator,
program_id: Pubkey,
) -> NeonResult<Vec<ChainInfo>> {
if rpc.use_cache() {
return CHAINS_CACHE
.get_or_try_init(|| get_chains(rpc, program_id))
if rpc.use_cache_for_chains() {
let result = CHAINS_CACHE
.get_or_init(|| async {
rpc.get_config(program_id)
.await
.expect(" get config error for chain info")
.chains
})
.await
.cloned();
.clone();
return Ok(result);
}

get_chains(rpc, program_id).await
}

async fn get_chains(
rpc: &(impl BuildConfigSimulator + Sized),
program_id: Pubkey,
) -> NeonResult<Vec<ChainInfo>> {
rpc.build_config_simulator(program_id)
.await?
.get_chains()
.await
Ok(rpc.get_config(program_id).await?.chains)
}

async fn read_chain_id(
Expand Down
25 changes: 20 additions & 5 deletions evm_loader/lib/src/rpc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
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;
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::Result as ClientResult;
use solana_client::client_error::{ClientErrorKind, Result as ClientResult};
use solana_sdk::{
account::Account,
clock::{Slot, UnixTimestamp},
message::Message,
native_token::lamports_to_sol,
pubkey::Pubkey,
};
pub use validator_client::CloneRpcClient;

#[async_trait(?Send)]
#[enum_dispatch]
Expand All @@ -28,6 +28,20 @@ pub trait Rpc {
key: &Pubkey,
slice: Option<SliceConfig>,
) -> ClientResult<Option<Account>>;

async fn get_last_deployed_slot(&self, program_id: &Pubkey) -> ClientResult<Option<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 {
let slot = get_programdata_slot_from_account(&acc)?;
return Ok(slot);
}
Err(ClientErrorKind::Custom("Not account on slot ".to_string()).into())
}

async fn get_account(&self, key: &Pubkey) -> ClientResult<Option<Account>> {
self.get_account_slice(key, None).await
}
Expand Down Expand Up @@ -61,6 +75,7 @@ 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
Loading
Loading