Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 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
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ RUN cargo fmt --check && \


# Add neon_test_invoke_program to the genesis
FROM neonlabsorg/neon_test_invoke_program:develop AS neon_test_invoke_program
FROM neonlabsorg/neon_test_programs:latest AS neon_test_programs

# Define solana-image that contains utility
FROM builder AS base
Expand All @@ -40,8 +40,8 @@ COPY --from=evm-loader-builder /opt/neon-evm/evm_loader/target/deploy/evm_loader
COPY --from=evm-loader-builder /opt/neon-evm/evm_loader/target/deploy/evm_loader-dump.txt /opt/
COPY --from=evm-loader-builder /opt/neon-evm/evm_loader/target/release/neon-cli /opt/
COPY --from=evm-loader-builder /opt/neon-evm/evm_loader/target/release/neon-api /opt/
COPY --from=neon_test_invoke_program /opt/neon_test_invoke_program.so /opt/
COPY --from=neon_test_invoke_program /opt/neon_test_invoke_program-keypair.json /opt/

COPY --from=neon_test_programs /opt/deploy/ /opt/deploy/

COPY ci/wait-for-solana.sh \
ci/wait-for-neon.sh \
Expand Down
12 changes: 8 additions & 4 deletions ci/solana-run-neon.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ EVM_LOADER_PATH=${NEON_BIN}/evm_loader.so
METAPLEX=metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s
METAPLEX_PATH=${NEON_BIN}/metaplex.so

TEST_INVOKE_PROGRAM_ID_KEYPAIR=${NEON_BIN}/neon_test_invoke_program-keypair.json
TEST_INVOKE=$(solana address -k ${TEST_INVOKE_PROGRAM_ID_KEYPAIR})
TEST_INVOKE_PATH=${NEON_BIN}/neon_test_invoke_program.so

VALIDATOR_ARGS=(
--reset
Expand All @@ -23,9 +20,16 @@ VALIDATOR_ARGS=(
--ticks-per-slot 16
--upgradeable-program ${EVM_LOADER} ${EVM_LOADER_PATH} ${EVM_LOADER_AUTHORITY_KEYPAIR}
--bpf-program ${METAPLEX} ${METAPLEX_PATH}
--bpf-program ${TEST_INVOKE} ${TEST_INVOKE_PATH}
)

LIST_OF_TEST_PROGRAMS=("test_invoke_program" "counter" "cross_program_invocation" "transfer_sol" "transfer_tokens")

for program in "${LIST_OF_TEST_PROGRAMS[@]}"; do
keypair="${NEON_BIN}/deploy/${program}/${program}-keypair.json"
address=$(solana address -k $keypair)
VALIDATOR_ARGS+=(--bpf-program $address ${NEON_BIN}/deploy/$program/$program.so)
done

if [[ -n $GEYSER_PLUGIN_CONFIG ]]; then
echo "Using geyser plugin with config: $GEYSER_PLUGIN_CONFIG"
VALIDATOR_ARGS+=(--geyser-plugin-config $GEYSER_PLUGIN_CONFIG)
Expand Down
51 changes: 41 additions & 10 deletions evm_loader/lib/src/account_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub struct EmulatorAccountStorage<'rpc, T: Rpc> {
chains: Vec<ChainInfo>,
block_number: u64,
block_timestamp: i64,
rent: Rent,
state_overrides: Option<AccountOverrides>,
}

Expand Down Expand Up @@ -86,6 +87,25 @@ impl<'rpc, T: Rpc + BuildConfigSimulator> EmulatorAccountStorage<'rpc, T> {
Some(chains) => chains,
};

// TODO: is it correct to get rent from the account
let mut rent_account = rpc
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is correct

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed

.get_account(&solana_sdk::sysvar::rent::id())
.await?
.value
.unwrap();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for error instead of unwarping

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

let rent = Rent::from_account_info(&AccountInfo {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified to
let rent = bincode::deserialize(&rent_account.data)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

key: &solana_sdk::sysvar::rent::id(),
is_signer: false,
is_writable: false,
lamports: Rc::new(RefCell::new(&mut rent_account.lamports)),
data: Rc::new(RefCell::new(&mut rent_account.data)),
owner: &rent_account.owner,
executable: rent_account.executable,
rent_epoch: rent_account.rent_epoch,
})?;

info!("Rent: {rent:?}");

Ok(Self {
accounts: RefCell::new(HashMap::new()),
program_id,
Expand All @@ -95,6 +115,7 @@ impl<'rpc, T: Rpc + BuildConfigSimulator> EmulatorAccountStorage<'rpc, T> {
block_number,
block_timestamp,
state_overrides,
rent,
})
}

Expand Down Expand Up @@ -213,8 +234,6 @@ impl<T: Rpc> EmulatorAccountStorage<'_, T> {
pub async fn apply_actions(&mut self, actions: Vec<Action>) -> Result<(), NeonError> {
info!("apply_actions");

let rent = Rent::get()?;

let mut new_balance_accounts = HashSet::new();

for action in actions {
Expand Down Expand Up @@ -262,12 +281,13 @@ impl<T: Rpc> EmulatorAccountStorage<'_, T> {
let empty_size = StorageCell::required_account_size(0);

let gas = if account.is_none() {
rent.minimum_balance(cell_size)
self.rent.minimum_balance(cell_size)
} else {
let existing_value = self.storage(address, index).await;
if existing_value == [0_u8; 32] {
rent.minimum_balance(cell_size)
.saturating_sub(rent.minimum_balance(empty_size))
self.rent
.minimum_balance(cell_size)
.saturating_sub(self.rent.minimum_balance(empty_size))
} else {
0
}
Expand All @@ -294,7 +314,7 @@ impl<T: Rpc> EmulatorAccountStorage<'_, T> {
self.use_contract_account(address, true).await?;

let space = ContractAccount::required_account_size(&code);
self.gas = self.gas.saturating_add(rent.minimum_balance(space));
self.gas = self.gas.saturating_add(self.rent.minimum_balance(space));
}
Action::EvmSelfDestruct { address } => {
info!("selfdestruct {address}");
Expand All @@ -320,7 +340,8 @@ impl<T: Rpc> EmulatorAccountStorage<'_, T> {
}

self.gas = self.gas.saturating_add(
rent.minimum_balance(BalanceAccount::required_account_size())
self.rent
.minimum_balance(BalanceAccount::required_account_size())
.saturating_mul(new_balance_accounts.len() as u64),
);

Expand All @@ -331,8 +352,6 @@ impl<T: Rpc> EmulatorAccountStorage<'_, T> {
let mut accounts = self.accounts.borrow_mut();
let mut additional_balances = Vec::new();

let rent = Rent::get()?;

for (key, account) in accounts.iter_mut() {
let Some(account_data) = account.data.as_mut() else {
continue;
Expand Down Expand Up @@ -361,7 +380,9 @@ impl<T: Rpc> EmulatorAccountStorage<'_, T> {

if (legacy_data.code_size > 0) || (legacy_data.generation > 0) {
// This is a contract, we need additional gas for conversion
let lamports = rent.minimum_balance(BalanceAccount::required_account_size());
let lamports = self
.rent
.minimum_balance(BalanceAccount::required_account_size());
self.gas = self.gas.saturating_add(lamports);
}
}
Expand Down Expand Up @@ -533,6 +554,16 @@ impl<T: Rpc> AccountStorage for EmulatorAccountStorage<'_, T> {
self.block_timestamp.try_into().unwrap()
}

fn rent(&self) -> &Rent {
&self.rent
}

fn return_data(&self) -> Option<(Pubkey, Vec<u8>)> {
info!("return_data");
// TODO: implement return_data() method with SyncedAccountStorage implementation
unimplemented!();
}

async fn block_hash(&self, slot: u64) -> [u8; 32] {
info!("block_hash {slot}");

Expand Down
9 changes: 9 additions & 0 deletions evm_loader/lib/src/emulator_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use evm_loader::executor::{
use evm_loader::types::Address;
use solana_sdk::instruction::Instruction;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::rent::Rent;

#[derive(Default, Clone)]
pub struct ExecuteStatus {
Expand Down Expand Up @@ -132,6 +133,14 @@ impl<'a, B: AccountStorage> Database for EmulatorState<'a, B> {
self.inner_state.block_timestamp()
}

fn rent(&self) -> &Rent {
self.inner_state.rent()
}

fn return_data(&self) -> Option<(Pubkey, Vec<u8>)> {
self.inner_state.return_data()
}

async fn external_account(&self, address: Pubkey) -> Result<OwnedAccountInfo> {
self.inner_state.external_account(address).await
}
Expand Down
1 change: 0 additions & 1 deletion evm_loader/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ pub mod emulator_state;
pub mod errors;
pub mod rpc;
pub mod solana_emulator;
pub mod syscall_stubs;
pub mod tracing;
pub mod types;

Expand Down
17 changes: 1 addition & 16 deletions evm_loader/lib/src/solana_emulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use std::collections::BTreeMap;
use tokio::sync::{Mutex, MutexGuard, OnceCell};

use crate::rpc::Rpc;
use crate::syscall_stubs;
use crate::NeonError;
use evm_loader::{account_storage::AccountStorage, executor::OwnedAccountInfo};
use solana_program_test::{processor, ProgramTest, ProgramTestContext};
Expand All @@ -31,17 +30,6 @@ pub struct SolanaEmulator {
static SOLANA_EMULATOR: OnceCell<Mutex<SolanaEmulator>> = OnceCell::const_new();
const SEEDS_PUBKEY: Pubkey = pubkey!("Seeds11111111111111111111111111111111111111");

macro_rules! processor_with_original_stubs {
($process_instruction:expr) => {
processor!(|program_id, accounts, instruction_data| {
let use_original_stubs_saved = syscall_stubs::use_original_stubs_for_thread(true);
let result = $process_instruction(program_id, accounts, instruction_data);
syscall_stubs::use_original_stubs_for_thread(use_original_stubs_saved);
result
})
};
}

pub async fn get_solana_emulator() -> MutexGuard<'static, SolanaEmulator> {
SOLANA_EMULATOR
.get()
Expand All @@ -59,9 +47,6 @@ pub async fn init_solana_emulator(
let emulator = SolanaEmulator::new(program_id, rpc_client)
.await
.expect("Initialize SolanaEmulator");
syscall_stubs::setup_emulator_syscall_stubs(rpc_client)
.await
.expect("Setup emulator syscall stubs");
Mutex::new(emulator)
})
.await
Expand Down Expand Up @@ -121,7 +106,7 @@ impl SolanaEmulator {
program_test.add_program(
"evm_loader",
program_id,
processor_with_original_stubs!(process_emulator_instruction),
processor!(process_emulator_instruction),
);

// Disable features (get known feature list and disable by actual value)
Expand Down
4 changes: 3 additions & 1 deletion evm_loader/program/src/account/ether_balance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
types::Address,
};
use ethnum::U256;
use solana_program::{account_info::AccountInfo, pubkey::Pubkey, system_program};
use solana_program::{account_info::AccountInfo, pubkey::Pubkey, rent::Rent, system_program};

use super::{AccountsDB, ACCOUNT_PREFIX_LEN, ACCOUNT_SEED_VERSION, TAG_ACCOUNT_BALANCE};

Expand Down Expand Up @@ -46,6 +46,7 @@ impl<'a> BalanceAccount<'a> {
chain_id: u64,
accounts: &AccountsDB<'a>,
keys: Option<&KeysCache>,
rent: &Rent,
) -> Result<Self> {
let (pubkey, bump_seed) = keys.map_or_else(
|| address.find_balance_address(&crate::ID, chain_id),
Expand Down Expand Up @@ -93,6 +94,7 @@ impl<'a> BalanceAccount<'a> {
&account,
program_seeds,
ACCOUNT_PREFIX_LEN + size_of::<Header>(),
rent,
)?;

super::set_tag(&crate::ID, &account, TAG_ACCOUNT_BALANCE)?;
Expand Down
2 changes: 1 addition & 1 deletion evm_loader/program/src/account/ether_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl<'a> ContractAccount<'a> {
if system_program::check_id(info.owner) {
let seeds: &[&[u8]] = &[&[ACCOUNT_SEED_VERSION], address.as_bytes(), &[bump_seed]];
let space = required_size.min(MAX_PERMITTED_DATA_INCREASE);
system.create_pda_account(&crate::ID, operator, info, seeds, space)?;
system.create_pda_account(&crate::ID, operator, info, seeds, space, rent)?;
} else if crate::check_id(info.owner) {
super::validate_tag(&crate::ID, info, TAG_EMPTY)?;

Expand Down
4 changes: 3 additions & 1 deletion evm_loader/program/src/account/ether_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ impl<'a> StorageCell<'a> {
allocate_cells: usize,
accounts: &AccountsDB<'a>,
signer_seeds: &[&[u8]],
rent: &Rent,
) -> Result<Self> {
let base_account = accounts.get(&address.base);
let cell_account = accounts.get(&address.pubkey);
Expand All @@ -114,6 +115,7 @@ impl<'a> StorageCell<'a> {
cell_account,
address.seed(),
space,
rent,
)?;

super::set_tag(&crate::ID, cell_account, TAG_STORAGE_CELL)?;
Expand Down Expand Up @@ -200,7 +202,7 @@ impl<'a> StorageCell<'a> {
Ok(())
}

pub fn sync_lamports(&mut self, rent: Rent, accounts: &AccountsDB<'a>) -> Result<()> {
pub fn sync_lamports(&mut self, rent: &Rent, accounts: &AccountsDB<'a>) -> Result<()> {
let original_data_len = unsafe { self.account.original_data_len() };
if original_data_len == self.account.data_len() {
return Ok(());
Expand Down
16 changes: 10 additions & 6 deletions evm_loader/program/src/account/legacy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,12 @@ pub const TAG_HOLDER_DEPRECATED: u8 = 51;
pub const TAG_ACCOUNT_CONTRACT_DEPRECATED: u8 = 12;
pub const TAG_STORAGE_CELL_DEPRECATED: u8 = 42;

fn reduce_account_size(account: &AccountInfo, required_len: usize) -> Result<u64> {
fn reduce_account_size(account: &AccountInfo, required_len: usize, rent: &Rent) -> Result<u64> {
assert!(account.data_len() > required_len);

account.realloc(required_len, false)?;

// Return excessive lamports to the operator
let rent = Rent::get()?;
let minimum_balance = rent.minimum_balance(account.data_len());
if account.lamports() > minimum_balance {
let value = account.lamports() - minimum_balance;
Expand All @@ -59,6 +58,7 @@ fn update_ether_account(
legacy_data: &LegacyEtherData,
db: &AccountsDB,
keys: &KeysCache,
rent: &Rent,
) -> Result<u64> {
let pubkey = keys.contract(&crate::ID, legacy_data.address);
let account = db.get(&pubkey);
Expand All @@ -75,7 +75,7 @@ fn update_ether_account(

// Make account smaller
let required_len = ContractAccount::required_account_size(&code);
lamports_collected += reduce_account_size(account, required_len)?;
lamports_collected += reduce_account_size(account, required_len, rent)?;

// Fill it with new data
account.try_borrow_mut_data()?.fill(0);
Expand Down Expand Up @@ -103,6 +103,7 @@ fn update_ether_account(
crate::config::DEFAULT_CHAIN_ID,
db,
Some(keys),
rent,
)?;
balance.mint(legacy_data.balance)?;
balance.increment_nonce_by(legacy_data.trx_count)?;
Expand All @@ -117,6 +118,7 @@ fn update_storage_account(
legacy_data: &LegacyStorageData,
db: &AccountsDB,
keys: &KeysCache,
rent: &Rent,
) -> Result<u64> {
let mut lamports_collected = 0_u64;

Expand All @@ -137,7 +139,7 @@ fn update_storage_account(

// Make account smaller
let required_len = StorageCell::required_account_size(cells.len());
lamports_collected += reduce_account_size(&cell_account, required_len)?;
lamports_collected += reduce_account_size(&cell_account, required_len, rent)?;

// Fill it with new data
cell_account.try_borrow_mut_data()?.fill(0);
Expand Down Expand Up @@ -187,6 +189,8 @@ pub fn update_holder_account(account: &AccountInfo) -> Result<u8> {
pub fn update_legacy_accounts(accounts: &AccountsDB) -> Result<u64> {
let keys = KeysCache::new();

let rent = Rent::get()?;

let mut lamports_collected = 0_u64;
let mut legacy_storage = Vec::with_capacity(accounts.accounts_len());

Expand All @@ -203,7 +207,7 @@ pub fn update_legacy_accounts(accounts: &AccountsDB) -> Result<u64> {
match tag {
LegacyEtherData::TAG => {
let legacy_data = LegacyEtherData::from_account(&crate::ID, account)?;
lamports_collected += update_ether_account(&legacy_data, accounts, &keys)?;
lamports_collected += update_ether_account(&legacy_data, accounts, &keys, &rent)?;
}
LegacyStorageData::TAG => {
let legacy_data = LegacyStorageData::from_account(&crate::ID, account)?;
Expand All @@ -214,7 +218,7 @@ pub fn update_legacy_accounts(accounts: &AccountsDB) -> Result<u64> {
}

for data in legacy_storage {
lamports_collected += update_storage_account(&data, accounts, &keys)?;
lamports_collected += update_storage_account(&data, accounts, &keys, &rent)?;
}

Ok(lamports_collected)
Expand Down
Loading