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

[WIP] try to fix #591

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
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
12 changes: 12 additions & 0 deletions evm_loader/lib/src/commands/get_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,18 @@ pub enum ConfigSimulator<'r> {
pub trait BuildConfigSimulator: Rpc {
fn use_cache_for_chains(&self) -> bool;
async fn get_config(&self, program_id: Pubkey) -> NeonResult<GetConfigResponse> {
let key_default = KeyAccountCache {
addr: program_id,
slot: 0,
};

let maybe_account = program_config_cache_get(&key_default).await;
if let Some(account) = maybe_account {
return Ok(account);
}

let maybe_slot = self.get_last_deployed_slot(&program_id).await?;

if let Some(slot) = maybe_slot {
let key = KeyAccountCache {
addr: program_id,
Expand All @@ -74,6 +85,7 @@ pub trait BuildConfigSimulator: Rpc {
return Ok(rz.unwrap());
};
}

let mut simulator = self.build_config_simulator(program_id).await?;

let (version, revision) = simulator.get_version().await?;
Expand Down
29 changes: 25 additions & 4 deletions evm_loader/lib/src/rpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ use crate::commands::get_config::GetConfigResponse;
use crate::commands::get_config::{BuildConfigSimulator, ConfigSimulator};
use crate::{NeonError, NeonResult};
use async_trait::async_trait;

use crate::types::programs_cache::get_program_programdata_address;
use crate::types::programs_cache::get_programdata_slot_from_account;
pub use db_call_client::CallDbClient;
use enum_dispatch::enum_dispatch;
use evm_loader::solana_program::bpf_loader_upgradeable::UpgradeableLoaderState;
Expand All @@ -30,14 +33,33 @@ pub trait Rpc {
) -> ClientResult<Option<Account>>;

async fn get_last_deployed_slot(&self, program_id: &Pubkey) -> ClientResult<Option<u64>> {
let mut slice_len = std::mem::size_of::<UpgradeableLoaderState>();
if slice_len < UpgradeableLoaderState::size_of_programdata_metadata() {
slice_len = 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;
// bpfv2 and request account from link

if let Ok(Some(acc)) = result {
let slot = get_programdata_slot_from_account(&acc)?;
return Ok(slot);
let slot = if acc.executable {
get_programdata_slot_from_account(&acc)
.expect("error")
.expect("No slot info")
} else {
let pd_addr = get_program_programdata_address(&acc)?.expect("no program info");
let rz = self
.get_account_slice(&pd_addr, Some(slice))
.await?
.expect("No account ");
get_programdata_slot_from_account(&rz)?.expect("No slice ")
};
return Ok(Some(slot));
}
Err(ClientErrorKind::Custom("Not account on slot ".to_string()).into())
}
Expand Down Expand Up @@ -75,7 +97,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
89 changes: 68 additions & 21 deletions evm_loader/lib/src/types/programs_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ 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 @@ -62,9 +68,26 @@ where

type ThreadSaveProgramDataCache = ThreadSaveCache<Account>;
type ThreadSaveConfigCache = ThreadSaveCache<GetConfigResponse>;
type ThreadSaveTestCache = ThreadSaveCache<String>;

static ACCOUNT_CACHE_TABLE: OnceCell<ThreadSaveProgramDataCache> = OnceCell::const_new();
static CONFIG_CACHE_TABLE: OnceCell<ThreadSaveConfigCache> = OnceCell::const_new();
#[allow(dead_code)]
static TEST_CACHE_TABLE: OnceCell<ThreadSaveTestCache> = OnceCell::const_new();
#[allow(dead_code)]
async fn programdata_test_cache_get_instance() -> &'static ThreadSaveTestCache {
TEST_CACHE_TABLE
.get_or_init(|| async { ThreadSaveTestCache::new() })
.await
}
#[allow(dead_code)]
async fn programdata_test_cache_get(key: &KeyAccountCache) -> Option<String> {
programdata_test_cache_get_instance().await.get(key)
}
#[allow(dead_code)]
async fn programdata_test_cache_add(key: KeyAccountCache, acc: String) {
programdata_test_cache_get_instance().await.add(key, acc);
}

pub async fn cut_programdata_from_acc(account: &mut Account, data_slice: SliceConfig) {
if data_slice.offset != 0 {
Expand All @@ -81,25 +104,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! ProgramData and data type is acceptable ");
}
Err(e) => {
eprintln!("Error occurred: {e:?}");
panic!("Failed to deserialize account data.");
Expand Down Expand Up @@ -130,16 +176,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 @@ -150,11 +198,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 @@ -330,20 +378,19 @@ mod tests {
.is_none());
}

#[test]
fn test_add_and_get_value() {
let cache: ThreadSaveCache<String> = ThreadSaveCache::new();
#[tokio::test]
async fn test_add_and_get_value() {
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());
programdata_test_cache_add(key.clone(), value.clone()).await;

// Retrieve the value from the cache
let result = cache.get(&key);
let result = programdata_test_cache_get(&key).await;
assert!(result.is_some());
assert_eq!(result.unwrap(), value);
}
Expand Down
Loading