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 3385 Caching ProgramData accounts (#569) #575

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions evm_loader/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions evm_loader/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ clap = "2.34.0"
lazy_static = "1.5.0"
elsa = "1.10.0"
arrayref = "0.3.8"
futures = "0.3.30"

[dev-dependencies]
hex-literal = "0.4.1"
Expand Down
61 changes: 60 additions & 1 deletion evm_loader/lib/src/account_storage_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::rpc;
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;

Expand All @@ -19,6 +20,8 @@ mod mock_rpc_client {
use solana_sdk::pubkey::Pubkey;
use std::collections::HashMap;

use solana_account_decoder::UiDataSliceConfig;

pub struct MockRpcClient {
accounts: HashMap<Pubkey, Account>,
}
Expand All @@ -33,7 +36,31 @@ mod mock_rpc_client {

#[async_trait(?Send)]
impl Rpc for MockRpcClient {
async fn get_account(&self, key: &Pubkey) -> ClientResult<Option<Account>> {
async fn get_account_slice(
&self,
key: &Pubkey,
slice: Option<UiDataSliceConfig>,
) -> ClientResult<Option<Account>> {
if let Some(data_slice) = slice {
if let Some(orig_acc) = self.accounts.get(key) {
let cut_to =
usize::min(data_slice.offset + data_slice.length, orig_acc.data.len());
let sliced_data = if data_slice.offset < orig_acc.data.len() {
orig_acc.data[data_slice.offset..cut_to].to_vec()
} else {
vec![]
};

return Ok(Some(Account {
lamports: orig_acc.lamports,
data: sliced_data,
owner: orig_acc.owner,
executable: orig_acc.executable,
rent_epoch: orig_acc.rent_epoch,
}));
}
}

let result = self.accounts.get(key).cloned();
Ok(result)
}
Expand Down Expand Up @@ -1790,3 +1817,35 @@ async fn test_storage_new_from_other_and_override() {
expected_balance
);
}

#[tokio::test]
async fn test_storage_get_account_slice() {
let slice_from = 2;
let slice_size = 20;
let test_key = Pubkey::new_unique();
let acc = Account::new(10, 1 * 1024 * 1024, &solana_sdk::sysvar::rent::id());

let account_tuple = (test_key, acc);
let accounts_for_rpc = vec![
(solana_sdk::sysvar::rent::id(), account_tuple.1.clone()),
account_tuple.clone(),
];
let rpc_client = mock_rpc_client::MockRpcClient::new(&accounts_for_rpc);
let acc_no_slice = rpc_client
.get_account(&test_key)
.await
.expect("Failed to get account slice");

let slice_cfg = UiDataSliceConfig {
offset: slice_from,
length: slice_size,
};
let sliced_acc = rpc_client
.get_account_slice(&test_key, Some(slice_cfg))
.await
.expect("Failed to get account slice");
assert!(acc_no_slice.is_some());
assert!(sliced_acc.is_some());
assert!(acc_no_slice.unwrap().data.len() > 2000);
assert_eq!(sliced_acc.unwrap().data.len(), slice_size);
}
1 change: 0 additions & 1 deletion evm_loader/lib/src/commands/get_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ impl BuildConfigSimulator for CallDbClient {
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
20 changes: 14 additions & 6 deletions evm_loader/lib/src/rpc/db_call_client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{e, Rpc};
use super::{e, Rpc, SliceConfig};
use crate::types::{TracerDb, TracerDbTrait};
use crate::NeonError;
use crate::NeonError::RocksDb;
Expand Down Expand Up @@ -42,18 +42,26 @@ impl CallDbClient {
})
}

async fn get_account_at(&self, key: &Pubkey) -> ClientResult<Option<Account>> {
async fn get_account_at(
&self,
key: &Pubkey,
slice: Option<SliceConfig>,
) -> ClientResult<Option<Account>> {
self.tracer_db
.get_account_at(key, self.slot, self.tx_index_in_block)
.get_account_at(key, self.slot, self.tx_index_in_block, slice)
.await
.map_err(|e| e!("load account error", key, e))
}
}

#[async_trait(?Send)]
impl Rpc for CallDbClient {
async fn get_account(&self, key: &Pubkey) -> ClientResult<Option<Account>> {
self.get_account_at(key).await
async fn get_account_slice(
&self,
key: &Pubkey,
slice: Option<SliceConfig>,
) -> ClientResult<Option<Account>> {
self.get_account_at(key, slice).await
}

async fn get_multiple_accounts(
Expand All @@ -62,7 +70,7 @@ impl Rpc for CallDbClient {
) -> ClientResult<Vec<Option<Account>>> {
let mut result = Vec::new();
for key in pubkeys {
result.push(self.get_account_at(key).await?);
result.push(self.get_account_at(key, None).await?);
}
debug!("get_multiple_accounts: pubkeys={pubkeys:?} result={result:?}");
Ok(result)
Expand Down
34 changes: 24 additions & 10 deletions evm_loader/lib/src/rpc/emulator_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,35 @@ use solana_sdk::{

use crate::account_storage::{fake_operator, EmulatorAccountStorage};

use super::Rpc;
use super::{Rpc, SliceConfig};

#[async_trait(?Send)]
impl<'rpc, T: Rpc> Rpc for EmulatorAccountStorage<'rpc, T> {
async fn get_account(&self, key: &Pubkey) -> ClientResult<Option<Account>> {
if *key == self.operator() {
return Ok(Some(fake_operator()));
}

if let Some(account_data) = self.accounts_get(key) {
return Ok(Some(Account::from(&*account_data)));
async fn get_account_slice(
&self,
key: &Pubkey,
slice: Option<SliceConfig>,
) -> ClientResult<Option<Account>> {
let answer_account = if *key == self.operator() {
Some(fake_operator())
} else if let Some(account_data) = self.accounts_get(key) {
Some(Account::from(&*account_data))
} else {
self._get_account_from_rpc(*key).await?.cloned()
};

if let Some(data_slice) = slice {
// if only slice is necessary - cut data
if let Some(mut account) = answer_account {
if data_slice.offset != 0 {
account.data.drain(0..data_slice.offset);
}
account.data.truncate(data_slice.length);
return Ok(Some(account));
}
}

let account = self._get_account_from_rpc(*key).await?.cloned();
Ok(account)
Ok(answer_account)
}

async fn get_multiple_accounts(
Expand Down
15 changes: 12 additions & 3 deletions evm_loader/lib/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,29 @@ use crate::commands::get_config::{BuildConfigSimulator, ConfigSimulator};
use crate::{NeonError, NeonResult};
use async_trait::async_trait;
use enum_dispatch::enum_dispatch;
pub use solana_account_decoder::UiDataSliceConfig as SliceConfig;
use solana_cli::cli::CliError;
use solana_client::client_error::Result as ClientResult;
use solana_sdk::message::Message;
use solana_sdk::native_token::lamports_to_sol;
use solana_sdk::{
account::Account,
clock::{Slot, UnixTimestamp},
message::Message,
native_token::lamports_to_sol,
pubkey::Pubkey,
};

#[async_trait(?Send)]
#[enum_dispatch]
pub trait Rpc {
async fn get_account(&self, key: &Pubkey) -> ClientResult<Option<Account>>;
async fn get_account_slice(
&self,
key: &Pubkey,
slice: Option<SliceConfig>,
) -> ClientResult<Option<Account>>;
async fn get_account(&self, key: &Pubkey) -> ClientResult<Option<Account>> {
self.get_account_slice(key, None).await
}

async fn get_multiple_accounts(&self, pubkeys: &[Pubkey])
-> ClientResult<Vec<Option<Account>>>;
async fn get_block_time(&self, slot: Slot) -> ClientResult<UnixTimestamp>;
Expand Down
11 changes: 8 additions & 3 deletions evm_loader/lib/src/rpc/validator_client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{config::APIOptions, Config};

use super::Rpc;
use super::{Rpc, SliceConfig};
use async_trait::async_trait;
use solana_account_decoder::{UiAccount, UiAccountEncoding};
use solana_client::{
Expand All @@ -15,6 +15,7 @@ use solana_sdk::{
clock::{Slot, UnixTimestamp},
pubkey::Pubkey,
};

use std::{error::Error, ops::Deref, time::Duration};
use std::{future::Future, sync::Arc};
use tracing::debug;
Expand Down Expand Up @@ -113,12 +114,16 @@ impl Deref for CloneRpcClient {

#[async_trait(?Send)]
impl Rpc for CloneRpcClient {
async fn get_account(&self, key: &Pubkey) -> ClientResult<Option<Account>> {
async fn get_account_slice(
&self,
key: &Pubkey,
slice: Option<SliceConfig>,
) -> ClientResult<Option<Account>> {
let request = || {
let config = RpcAccountInfoConfig {
encoding: Some(UiAccountEncoding::Base64Zstd),
commitment: Some(self.commitment()),
data_slice: None,
data_slice: slice,
min_context_slot: None,
};
let params = serde_json::json!([key.to_string(), config]);
Expand Down
7 changes: 5 additions & 2 deletions evm_loader/lib/src/solana_simulator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::sync::Arc;
pub use error::Error;
use evm_loader::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState;
use evm_loader::solana_program::clock::Slot;

use evm_loader::solana_program::loader_v4;
use evm_loader::solana_program::loader_v4::{LoaderV4State, LoaderV4Status};
use evm_loader::solana_program::message::SanitizedMessage;
Expand Down Expand Up @@ -51,6 +52,7 @@ use solana_sdk::{
pub use utils::SyncState;

use crate::rpc::Rpc;
use crate::types::programs_cache::programdata_cache_get_values_by_keys;

mod error;
mod utils;
Expand Down Expand Up @@ -113,7 +115,6 @@ impl SolanaSimulator {
let Some(account) = account else {
continue;
};

if account.executable && bpf_loader_upgradeable::check_id(&account.owner) {
let programdata_address = utils::program_data_address(account)?;
debug!(
Expand All @@ -129,7 +130,9 @@ impl SolanaSimulator {
storable_accounts.push((key, account));
}

let mut programdata_accounts = rpc.get_multiple_accounts(&programdata_keys).await?;
let mut programdata_accounts =
programdata_cache_get_values_by_keys(&programdata_keys, rpc).await?;

for (key, account) in programdata_keys.iter().zip(&mut programdata_accounts) {
let Some(account) = account else {
continue;
Expand Down
4 changes: 4 additions & 0 deletions evm_loader/lib/src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod tracer_ch_common;

pub mod programs_cache;
pub(crate) mod tracer_ch_db;
pub mod tracer_rocks_db;

Expand All @@ -26,6 +27,8 @@ use evm_loader::{
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use serde_with::{hex::Hex, serde_as, DisplayFromStr, OneOrMany};

use crate::rpc::SliceConfig;
use solana_sdk::signature::Signature;
use solana_sdk::{account::Account, pubkey::Pubkey};
use std::collections::HashMap;
Expand Down Expand Up @@ -79,6 +82,7 @@ pub trait TracerDbTrait {
pubkey: &Pubkey,
slot: u64,
tx_index_in_block: Option<u64>,
data_slice: Option<SliceConfig>,
) -> DbResult<Option<Account>>;

async fn get_transaction_index(&self, signature: Signature) -> DbResult<u64>;
Expand Down
Loading
Loading