From 4236c916031f9e6385b831769a8539d552299df0 Mon Sep 17 00:00:00 2001 From: Semen Medvedev Date: Thu, 18 Jan 2024 00:10:01 +0700 Subject: [PATCH 1/8] update version to 1.9.0-dev --- evm_loader/Cargo.lock | 4 ++-- evm_loader/cli/Cargo.toml | 2 +- evm_loader/program/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/evm_loader/Cargo.lock b/evm_loader/Cargo.lock index d9d6e2ae2..617fa3580 100644 --- a/evm_loader/Cargo.lock +++ b/evm_loader/Cargo.lock @@ -1866,7 +1866,7 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "evm-loader" -version = "1.7.0-dev" +version = "1.9.0-dev" dependencies = [ "arrayref", "async-trait", @@ -3127,7 +3127,7 @@ dependencies = [ [[package]] name = "neon-cli" -version = "1.7.0-dev" +version = "1.9.0-dev" dependencies = [ "build-info", "build-info-build", diff --git a/evm_loader/cli/Cargo.toml b/evm_loader/cli/Cargo.toml index d638202e8..593c1b94d 100644 --- a/evm_loader/cli/Cargo.toml +++ b/evm_loader/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "neon-cli" -version = "1.7.0-dev" +version = "1.9.0-dev" authors = ["NeonLabs Maintainers "] edition = "2021" diff --git a/evm_loader/program/Cargo.toml b/evm_loader/program/Cargo.toml index 52fa5da58..a4996d6bf 100644 --- a/evm_loader/program/Cargo.toml +++ b/evm_loader/program/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "evm-loader" -version = "1.7.0-dev" +version = "1.9.0-dev" description = "Neon EVM loader" authors = ["NeonLabs Maintainers "] edition = "2021" From ad4512f0fda1e6dd37d592b3c9fea3d606e86b63 Mon Sep 17 00:00:00 2001 From: Miroslav Nedelchev <45385208+mnedelchev-vn@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:35:57 +0700 Subject: [PATCH 2/8] =?UTF-8?q?whitelist=20DeBank/=20Rabby=20partner=20to?= =?UTF-8?q?=20use=20local=20neon=20evm=20proxy=20with=20quic=E2=80=A6=20(#?= =?UTF-8?q?266)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add keys for Miro, DeBank/ Rabby and Alfie for devnet WL --- evm_loader/program/config/testnet.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/evm_loader/program/config/testnet.toml b/evm_loader/program/config/testnet.toml index 8ea5d890e..8e22e1371 100644 --- a/evm_loader/program/config/testnet.toml +++ b/evm_loader/program/config/testnet.toml @@ -229,6 +229,9 @@ operators_whitelist = [ "AuikYUkrP9bRCxPq99YpEkFCgWLS9KM2oe3sCPkTCEwr", "AyEE2tf4AezMxtBYXoWgoK1PwMDMsPfDahQRtZvU8BLc", "B5Gefd2yR3nBi4eFDtp3grmVsRq6sw4UYmGVZG6vrda3", + "7P1VpfLJNo1rMJbHmz2P6U34ygkRM5UNogknFUXP2b1k", + "8HzCjhBNP3rs7SydUrZAiQGEoqXHNtpNPE475zzHmzba", + "CRJ7MFYvMjXysVDkifFmiS8jmpDMS5qZRwyu3EN3Rfav", ] [chain.neon] From b730019f9489381f4d9f4de3dceab515af957ccf Mon Sep 17 00:00:00 2001 From: Andrei Silviu Dragnea Date: Fri, 16 Feb 2024 13:36:53 +0200 Subject: [PATCH 3/8] NDEV-2620: Add executor_state: &impl Database parameter to EventListener::event method (#273) * NDEV-2620: Add executor_state: &impl Database parameter to EventListener::event method * NDEV-2620: Extract Tracer trait from EventListener trait * NDEV-2620: Move Tracer trait to neon-lib crate --- .../api/src/api_server/handlers/emulate.rs | 13 ++- evm_loader/cli/src/main.rs | 5 +- evm_loader/lib/Cargo.toml | 2 +- evm_loader/lib/src/commands/emulate.rs | 57 +++++------ evm_loader/lib/src/commands/trace.rs | 11 +-- evm_loader/lib/src/tracing/tracers/mod.rs | 52 +++++++--- .../lib/src/tracing/tracers/struct_logger.rs | 8 +- evm_loader/program/Cargo.toml | 2 +- evm_loader/program/src/evm/mod.rs | 97 +++++++++---------- evm_loader/program/src/evm/opcode.rs | 32 ++++-- evm_loader/program/src/evm/opcode_table.rs | 11 ++- evm_loader/program/src/evm/precompile/mod.rs | 3 +- evm_loader/program/src/evm/tracing.rs | 19 ++-- .../src/instruction/transaction_execute.rs | 5 +- .../src/instruction/transaction_step.rs | 9 +- 15 files changed, 186 insertions(+), 140 deletions(-) diff --git a/evm_loader/api/src/api_server/handlers/emulate.rs b/evm_loader/api/src/api_server/handlers/emulate.rs index 6d5015e15..cbb03472a 100644 --- a/evm_loader/api/src/api_server/handlers/emulate.rs +++ b/evm_loader/api/src/api_server/handlers/emulate.rs @@ -1,5 +1,6 @@ use actix_request_identifier::RequestId; use actix_web::{http::StatusCode, post, web::Json, Responder}; +use neon_lib::tracing::tracers::TracerTypeEnum; use std::convert::Into; use tracing::info; @@ -26,8 +27,14 @@ pub async fn emulate( }; process_result( - &EmulateCommand::execute(&rpc, state.config.evm_loader, emulate_request.body, None) - .await - .map_err(Into::into), + &EmulateCommand::execute( + &rpc, + state.config.evm_loader, + emulate_request.body, + None::, + ) + .await + .map(|(response, _)| response) + .map_err(Into::into), ) } diff --git a/evm_loader/cli/src/main.rs b/evm_loader/cli/src/main.rs index edcab4691..36d6b16d7 100644 --- a/evm_loader/cli/src/main.rs +++ b/evm_loader/cli/src/main.rs @@ -29,6 +29,7 @@ use crate::build_info::get_build_info; use evm_loader::types::Address; use neon_lib::errors::NeonError; use neon_lib::rpc::{CallDbClient, RpcEnum}; +use neon_lib::tracing::tracers::TracerTypeEnum; use neon_lib::types::TracerDb; use solana_clap_utils::keypair::signer_from_path; use solana_sdk::signature::Signer; @@ -44,9 +45,9 @@ async fn run(options: &ArgMatches<'_>) -> NeonCliResult { let rpc = build_rpc(options, config).await?; let request = read_tx_from_stdin()?; - emulate::execute(&rpc, config.evm_loader, request, None) + emulate::execute(&rpc, config.evm_loader, request, None::) .await - .map(|result| json!(result)) + .map(|(result, _)| json!(result)) } ("trace", Some(_)) => { let rpc = build_rpc(options, config).await?; diff --git a/evm_loader/lib/Cargo.toml b/evm_loader/lib/Cargo.toml index 7ecba68df..df35d7614 100644 --- a/evm_loader/lib/Cargo.toml +++ b/evm_loader/lib/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" thiserror = "1.0" anyhow = "1.0" bincode = "1.3.1" -evm-loader = { path = "../program", default-features = false, features = ["log", "async-trait", "serde_json"] } +evm-loader = { path = "../program", default-features = false, features = ["log", "async-trait"] } solana-sdk = "=1.16.23" solana-client = "=1.16.23" solana-clap-utils = "=1.16.23" diff --git a/evm_loader/lib/src/commands/emulate.rs b/evm_loader/lib/src/commands/emulate.rs index 0271b6248..05de4cd0c 100644 --- a/evm_loader/lib/src/commands/emulate.rs +++ b/evm_loader/lib/src/commands/emulate.rs @@ -2,19 +2,20 @@ use evm_loader::account::ContractAccount; use evm_loader::error::build_revert_message; use log::{debug, info}; use serde::{Deserialize, Serialize}; +use serde_json::Value; use solana_sdk::entrypoint::MAX_PERMITTED_DATA_INCREASE; use solana_sdk::pubkey::Pubkey; use crate::commands::get_config::BuildConfigSimulator; use crate::rpc::Rpc; use crate::syscall_stubs::setup_emulator_syscall_stubs; +use crate::tracing::tracers::Tracer; use crate::types::{EmulateRequest, TxParams}; use crate::{ account_storage::{EmulatorAccountStorage, SolanaAccount}, errors::NeonError, NeonResult, }; -use evm_loader::evm::tracing::TracerType; use evm_loader::{ config::{EVM_STEPS_MIN, PAYMENT_TO_TREASURE}, evm::{ExitStatus, Machine}, @@ -50,12 +51,12 @@ impl EmulateResponse { } } -pub async fn execute( +pub async fn execute( rpc: &(impl Rpc + BuildConfigSimulator), program_id: Pubkey, emulate_request: EmulateRequest, - tracer: Option, -) -> NeonResult { + tracer: Option, +) -> NeonResult<(EmulateResponse, Option)> { let block_overrides = emulate_request .trace_config .as_ref() @@ -81,12 +82,12 @@ pub async fn execute( emulate_trx(emulate_request.tx, &mut storage, step_limit, tracer).await } -async fn emulate_trx( +async fn emulate_trx( tx_params: TxParams, storage: &mut EmulatorAccountStorage<'_, impl Rpc>, step_limit: u64, - tracer: Option, -) -> NeonResult { + tracer: Option, +) -> NeonResult<(EmulateResponse, Option)> { info!("tx_params: {:?}", tx_params); let (origin, tx) = tx_params.into_transaction(storage).await; @@ -94,21 +95,18 @@ async fn emulate_trx( info!("origin: {:?}", origin); info!("tx: {:?}", tx); - let (exit_status, actions, steps_executed) = { - let mut backend = ExecutorState::new(storage); - let mut evm = match Machine::new(tx, origin, &mut backend, tracer).await { - Ok(evm) => evm, - Err(e) => return Ok(EmulateResponse::revert(e)), - }; + let mut backend = ExecutorState::new(storage); + let mut evm = match Machine::new(tx, origin, &mut backend, tracer).await { + Ok(evm) => evm, + Err(e) => return Ok((EmulateResponse::revert(e), None)), + }; - let (result, steps_executed) = evm.execute(step_limit, &mut backend).await?; - if result == ExitStatus::StepLimit { - return Err(NeonError::TooManySteps); - } + let (exit_status, steps_executed, tracer) = evm.execute(step_limit, &mut backend).await?; + if exit_status == ExitStatus::StepLimit { + return Err(NeonError::TooManySteps); + } - let actions = backend.into_actions(); - (result, actions, steps_executed) - }; + let actions = backend.into_actions(); storage.apply_actions(actions.clone()).await?; storage.mark_legacy_accounts().await?; @@ -128,14 +126,17 @@ async fn emulate_trx( let solana_accounts = storage.accounts.borrow().values().cloned().collect(); - Ok(EmulateResponse { - exit_status: exit_status.to_string(), - steps_executed, - used_gas, - solana_accounts, - result: exit_status.into_result().unwrap_or_default(), - iterations, - }) + Ok(( + EmulateResponse { + exit_status: exit_status.to_string(), + steps_executed, + used_gas, + solana_accounts, + result: exit_status.into_result().unwrap_or_default(), + iterations, + }, + tracer.map(|tracer| tracer.into_traces()), + )) } fn realloc_iterations(actions: &[Action]) -> u64 { diff --git a/evm_loader/lib/src/commands/trace.rs b/evm_loader/lib/src/commands/trace.rs index b6f8251fb..96fdc5c97 100644 --- a/evm_loader/lib/src/commands/trace.rs +++ b/evm_loader/lib/src/commands/trace.rs @@ -1,5 +1,3 @@ -use std::rc::Rc; - use serde_json::Value; use solana_sdk::pubkey::Pubkey; @@ -22,13 +20,10 @@ pub async fn trace_transaction( let tracer = new_tracer(&trace_config)?; - let emulation_tracer = Some(Rc::clone(&tracer)); - let r = super::emulate::execute(rpc, program_id, emulate_request, emulation_tracer).await?; + let (r, traces) = + super::emulate::execute(rpc, program_id, emulate_request, Some(tracer)).await?; - let mut traces = Rc::try_unwrap(tracer) - .expect("There is must be only one reference") - .into_inner() - .into_traces(); + let mut traces = traces.expect("traces should not be None"); traces["gas"] = r.used_gas.into(); Ok(traces) diff --git a/evm_loader/lib/src/tracing/tracers/mod.rs b/evm_loader/lib/src/tracing/tracers/mod.rs index e0dd86756..0f54f6391 100644 --- a/evm_loader/lib/src/tracing/tracers/mod.rs +++ b/evm_loader/lib/src/tracing/tracers/mod.rs @@ -1,21 +1,45 @@ use crate::tracing::tracers::struct_logger::StructLogger; use crate::tracing::TraceConfig; -use evm_loader::evm::tracing::TracerType; -use std::cell::RefCell; -use std::rc::Rc; +use evm_loader::evm::database::Database; +use evm_loader::evm::tracing::{Event, EventListener}; +use serde_json::Value; pub mod struct_logger; -pub fn new_tracer(trace_config: &TraceConfig) -> evm_loader::error::Result { - Ok(Rc::new(RefCell::new( - match trace_config.tracer.as_deref() { - None | Some("") => Box::new(StructLogger::new(trace_config)), - _ => { - return Err(evm_loader::error::Error::Custom(format!( - "Unsupported tracer: {:?}", - trace_config.tracer - ))) +pub enum TracerTypeEnum { + StructLogger(StructLogger), +} + +impl EventListener for TracerTypeEnum { + fn event(&mut self, executor_state: &impl Database, event: Event) { + match self { + TracerTypeEnum::StructLogger(struct_logger) => { + struct_logger.event(executor_state, event) } - }, - ))) + } + } +} + +pub trait Tracer: EventListener { + fn into_traces(self) -> Value; +} + +impl Tracer for TracerTypeEnum { + fn into_traces(self) -> Value { + match self { + TracerTypeEnum::StructLogger(struct_logger) => struct_logger.into_traces(), + } + } +} + +pub fn new_tracer(trace_config: &TraceConfig) -> evm_loader::error::Result { + match trace_config.tracer.as_deref() { + None | Some("") => Ok(TracerTypeEnum::StructLogger(StructLogger::new( + trace_config, + ))), + _ => Err(evm_loader::error::Error::Custom(format!( + "Unsupported tracer: {:?}", + trace_config.tracer + ))), + } } diff --git a/evm_loader/lib/src/tracing/tracers/struct_logger.rs b/evm_loader/lib/src/tracing/tracers/struct_logger.rs index a842557f6..e8d23c922 100644 --- a/evm_loader/lib/src/tracing/tracers/struct_logger.rs +++ b/evm_loader/lib/src/tracing/tracers/struct_logger.rs @@ -1,11 +1,13 @@ use std::collections::BTreeMap; use ethnum::U256; +use evm_loader::evm::database::Database; use evm_loader::evm::ExitStatus; use serde::Serialize; use serde_json::Value; use web3::types::Bytes; +use crate::tracing::tracers::Tracer; use crate::tracing::TraceConfig; use evm_loader::evm::opcode_table::OPNAMES; use evm_loader::evm::tracing::{Event, EventListener}; @@ -138,7 +140,7 @@ impl StructLogger { } impl EventListener for StructLogger { - fn event(&mut self, event: Event) { + fn event(&mut self, _executor_state: &impl Database, event: Event) { match event { Event::BeginVM { .. } => { self.depth += 1; @@ -203,8 +205,10 @@ impl EventListener for StructLogger { } }; } +} - fn into_traces(self: Box) -> Value { +impl Tracer for StructLogger { + fn into_traces(self) -> Value { let exit_status = self.exit_status.expect("Emulation is not completed"); let result = StructLoggerResult { gas: 0, diff --git a/evm_loader/program/Cargo.toml b/evm_loader/program/Cargo.toml index a4996d6bf..2cb4e8486 100644 --- a/evm_loader/program/Cargo.toml +++ b/evm_loader/program/Cargo.toml @@ -51,7 +51,6 @@ borsh = "0.9" bincode = "1" serde_bytes = "0.11.12" serde = { version = "1.0.186", default-features = false, features = ["derive", "rc"] } -serde_json = { version = "1.0.107", features = ["preserve_order"], optional = true } ethnum = { version = "1.4", default-features = false, features = ["serde"] } cfg-if = { version = "1.0" } log = { version = "0.4", default-features = false, optional = true } @@ -64,6 +63,7 @@ features = ["is_sync"] [dev-dependencies] tokio = { version = "1.0", features = ["full"] } +serde_json = { version = "1.0.107", features = ["preserve_order"] } [lib] crate-type = ["cdylib", "lib"] diff --git a/evm_loader/program/src/evm/mod.rs b/evm_loader/program/src/evm/mod.rs index f84010139..8ac20e696 100644 --- a/evm_loader/program/src/evm/mod.rs +++ b/evm_loader/program/src/evm/mod.rs @@ -11,8 +11,9 @@ use solana_program::log::sol_log_data; pub use buffer::Buffer; -#[cfg(not(target_os = "solana"))] -use crate::evm::tracing::TracerTypeOpt; +use crate::evm::tracing::EventListener; +#[cfg(target_os = "solana")] +use crate::evm::tracing::NoopEventListener; use crate::{ error::{build_revert_message, Error, Result}, evm::{opcode::Action, precompile::is_precompile_address}, @@ -28,43 +29,43 @@ mod opcode; pub mod opcode_table; mod precompile; mod stack; -#[cfg(not(target_os = "solana"))] pub mod tracing; mod utils; macro_rules! tracing_event { - ($self:ident, $x:expr) => { + ($self:ident, $backend:ident, $x:expr) => { #[cfg(not(target_os = "solana"))] - if let Some(tracer) = &$self.tracer { - tracer.borrow_mut().event($x); + if let Some(tracer) = &mut $self.tracer { + tracer.event($backend, $x); } }; - ($self:ident, $condition:expr, $x:expr) => { + ($self:ident, $backend:ident, $condition:expr, $x:expr) => { #[cfg(not(target_os = "solana"))] - if let Some(tracer) = &$self.tracer { + if let Some(tracer) = &mut $self.tracer { if $condition { - tracer.borrow_mut().event($x); + tracer.event($backend, $x); } } }; } macro_rules! trace_end_step { - ($self:ident, $return_data:expr) => { + ($self:ident, $backend:ident, $return_data:expr) => { #[cfg(not(target_os = "solana"))] - if let Some(tracer) = &$self.tracer { - tracer - .borrow_mut() - .event(crate::evm::tracing::Event::EndStep { + if let Some(tracer) = &mut $self.tracer { + tracer.event( + $backend, + crate::evm::tracing::Event::EndStep { gas_used: 0_u64, return_data: $return_data, - }) + }, + ) } }; - ($self:ident, $condition:expr; $return_data_getter:expr) => { + ($self:ident, $backend:ident, $condition:expr; $return_data_getter:expr) => { #[cfg(not(target_os = "solana"))] if $condition { - trace_end_step!($self, $return_data_getter) + trace_end_step!($self, $backend, $return_data_getter) } }; } @@ -134,7 +135,7 @@ pub struct Context { #[derive(Serialize, Deserialize)] #[serde(bound = "B: Database")] -pub struct Machine { +pub struct Machine { origin: Address, chain_id: u64, context: Context, @@ -161,12 +162,12 @@ pub struct Machine { #[serde(skip)] phantom: PhantomData<*const B>, - #[cfg(not(target_os = "solana"))] #[serde(skip)] - tracer: TracerTypeOpt, + tracer: Option, } -impl Machine { +#[cfg(target_os = "solana")] +impl Machine { pub fn serialize_into(&self, buffer: &mut [u8]) -> Result { let mut cursor = std::io::Cursor::new(buffer); @@ -175,7 +176,6 @@ impl Machine { cursor.position().try_into().map_err(Error::from) } - #[cfg(target_os = "solana")] pub fn deserialize_from(buffer: &[u8], backend: &B) -> Result { fn reinit_buffer(buffer: &mut Buffer, backend: &B) { if let Some((key, range)) = buffer.uninit_data() { @@ -184,7 +184,10 @@ impl Machine { } } - fn reinit_machine(mut machine: &mut Machine, backend: &B) { + fn reinit_machine( + mut machine: &mut Machine, + backend: &B, + ) { loop { reinit_buffer(&mut machine.call_data, backend); reinit_buffer(&mut machine.execution_code, backend); @@ -202,13 +205,15 @@ impl Machine { Ok(evm) } +} +impl Machine { #[maybe_async] pub async fn new( trx: Transaction, origin: Address, backend: &mut B, - #[cfg(not(target_os = "solana"))] tracer: TracerTypeOpt, + tracer: Option, ) -> Result { let trx_chain_id = trx.chain_id().unwrap_or_else(|| backend.default_chain_id()); @@ -241,25 +246,9 @@ impl Machine { // } if trx.target().is_some() { - Self::new_call( - trx_chain_id, - trx, - origin, - backend, - #[cfg(not(target_os = "solana"))] - tracer, - ) - .await + Self::new_call(trx_chain_id, trx, origin, backend, tracer).await } else { - Self::new_create( - trx_chain_id, - trx, - origin, - backend, - #[cfg(not(target_os = "solana"))] - tracer, - ) - .await + Self::new_create(trx_chain_id, trx, origin, backend, tracer).await } } @@ -269,7 +258,7 @@ impl Machine { trx: Transaction, origin: Address, backend: &mut B, - #[cfg(not(target_os = "solana"))] tracer: TracerTypeOpt, + tracer: Option, ) -> Result { assert!(trx.target().is_some()); @@ -308,7 +297,6 @@ impl Machine { reason: Reason::Call, parent: None, phantom: PhantomData, - #[cfg(not(target_os = "solana"))] tracer, }) } @@ -319,7 +307,7 @@ impl Machine { trx: Transaction, origin: Address, backend: &mut B, - #[cfg(not(target_os = "solana"))] tracer: TracerTypeOpt, + tracer: Option, ) -> Result { assert!(trx.target().is_none()); @@ -362,13 +350,16 @@ impl Machine { call_data: Buffer::empty(), parent: None, phantom: PhantomData, - #[cfg(not(target_os = "solana"))] tracer, }) } #[maybe_async] - pub async fn execute(&mut self, step_limit: u64, backend: &mut B) -> Result<(ExitStatus, u64)> { + pub async fn execute( + &mut self, + step_limit: u64, + backend: &mut B, + ) -> Result<(ExitStatus, u64, Option)> { assert!(self.execution_code.is_initialized()); assert!(self.call_data.is_initialized()); assert!(self.return_data.is_initialized()); @@ -377,6 +368,7 @@ impl Machine { tracing_event!( self, + backend, tracing::Event::BeginVM { context: self.context, code: self.execution_code.to_vec() @@ -399,6 +391,7 @@ impl Machine { tracing_event!( self, + backend, tracing::Event::BeginStep { opcode, pc: self.pc, @@ -415,7 +408,7 @@ impl Machine { } }; - trace_end_step!(self, opcode_result != Action::Noop; match &opcode_result { + trace_end_step!(self, backend, opcode_result != Action::Noop; match &opcode_result { Action::Return(value) | Action::Revert(value) => Some(value.clone()), _ => None, }); @@ -434,12 +427,13 @@ impl Machine { tracing_event!( self, + backend, tracing::Event::EndVM { status: status.clone() } ); - Ok((status, step)) + Ok((status, step, self.tracer.take())) } fn fork( @@ -468,8 +462,7 @@ impl Machine { reason, parent: None, phantom: PhantomData, - #[cfg(not(target_os = "solana"))] - tracer: self.tracer.clone(), + tracer: self.tracer.take(), }; core::mem::swap(self, &mut other); @@ -482,6 +475,8 @@ impl Machine { let mut other = *self.parent.take().unwrap(); core::mem::swap(self, &mut other); + self.tracer = other.tracer.take(); + other } } diff --git a/evm_loader/program/src/evm/opcode.rs b/evm_loader/program/src/evm/opcode.rs index 165111245..d1d42d2df 100644 --- a/evm_loader/program/src/evm/opcode.rs +++ b/evm_loader/program/src/evm/opcode.rs @@ -7,6 +7,7 @@ use super::{ database::{Database, DatabaseExt}, tracing_event, Context, Machine, Reason, }; +use crate::evm::tracing::EventListener; use crate::{ error::{Error, Result}, evm::{trace_end_step, Buffer}, @@ -25,7 +26,7 @@ pub enum Action { } #[allow(clippy::unused_async)] -impl Machine { +impl Machine { /// Unknown instruction #[maybe_async] pub async fn opcode_unknown(&mut self, _backend: &mut B) -> Result { @@ -799,7 +800,11 @@ impl Machine { let index = self.stack.pop_u256()?; let value = backend.storage(self.context.contract, index).await?; - tracing_event!(self, super::tracing::Event::StorageAccess { index, value }); + tracing_event!( + self, + backend, + super::tracing::Event::StorageAccess { index, value } + ); self.stack.push_array(&value)?; @@ -816,7 +821,11 @@ impl Machine { let index = self.stack.pop_u256()?; let value = *self.stack.pop_array()?; - tracing_event!(self, super::tracing::Event::StorageAccess { index, value }); + tracing_event!( + self, + backend, + super::tracing::Event::StorageAccess { index, value } + ); backend.set_storage(self.context.contract, index, value)?; @@ -1073,6 +1082,7 @@ impl Machine { tracing_event!( self, + backend, super::tracing::Event::BeginVM { context, code: init_code.to_vec() @@ -1133,6 +1143,7 @@ impl Machine { tracing_event!( self, + backend, super::tracing::Event::BeginVM { context, code: code.to_vec() @@ -1188,6 +1199,7 @@ impl Machine { tracing_event!( self, + backend, super::tracing::Event::BeginVM { context, code: code.to_vec() @@ -1241,6 +1253,7 @@ impl Machine { tracing_event!( self, + backend, super::tracing::Event::BeginVM { context, code: code.to_vec() @@ -1290,6 +1303,7 @@ impl Machine { tracing_event!( self, + backend, super::tracing::Event::BeginVM { context, code: code.to_vec() @@ -1367,9 +1381,10 @@ impl Machine { return Ok(Action::Return(return_data)); } - trace_end_step!(self, Some(return_data.clone())); + trace_end_step!(self, backend, Some(return_data.clone())); tracing_event!( self, + backend, super::tracing::Event::EndVM { status: super::ExitStatus::Return(return_data.clone()) } @@ -1416,9 +1431,10 @@ impl Machine { return Ok(Action::Revert(return_data)); } - trace_end_step!(self, Some(return_data.clone())); + trace_end_step!(self, backend, Some(return_data.clone())); tracing_event!( self, + backend, super::tracing::Event::EndVM { status: super::ExitStatus::Revert(return_data.clone()) } @@ -1472,9 +1488,10 @@ impl Machine { return Ok(Action::Suicide); } - trace_end_step!(self, None); + trace_end_step!(self, backend, None); tracing_event!( self, + backend, super::tracing::Event::EndVM { status: super::ExitStatus::Suicide } @@ -1504,9 +1521,10 @@ impl Machine { return Ok(Action::Stop); } - trace_end_step!(self, None); + trace_end_step!(self, backend, None); tracing_event!( self, + backend, super::tracing::Event::EndVM { status: super::ExitStatus::Stop } diff --git a/evm_loader/program/src/evm/opcode_table.rs b/evm_loader/program/src/evm/opcode_table.rs index cc169efc2..2f954da47 100644 --- a/evm_loader/program/src/evm/opcode_table.rs +++ b/evm_loader/program/src/evm/opcode_table.rs @@ -1,16 +1,17 @@ use crate::error::Result; +use crate::evm::tracing::EventListener; use super::{database::Database, opcode::Action, Machine}; macro_rules! opcode_table { ($( $opcode:literal, $opname:literal, $op:path;)*) => { #[cfg(target_os = "solana")] - type OpCode = fn(&mut Machine, &mut B) -> Result; + type OpCode = fn(&mut Machine, &mut B) -> Result; #[cfg(target_os = "solana")] - impl Machine { - const OPCODES: [OpCode; 256] = { - let mut opcodes: [OpCode; 256] = [Self::opcode_unknown; 256]; + impl Machine { + const OPCODES: [OpCode; 256] = { + let mut opcodes: [OpCode; 256] = [Self::opcode_unknown; 256]; $(opcodes[$opcode as usize] = $op;)* @@ -25,7 +26,7 @@ macro_rules! opcode_table { } #[cfg(not(target_os = "solana"))] - impl Machine { + impl Machine { pub async fn execute_opcode(&mut self, backend: &mut B, opcode: u8) -> Result { match opcode { $($opcode => $op(self, backend).await,)* diff --git a/evm_loader/program/src/evm/precompile/mod.rs b/evm_loader/program/src/evm/precompile/mod.rs index 16bb5666c..c22b65a8b 100644 --- a/evm_loader/program/src/evm/precompile/mod.rs +++ b/evm_loader/program/src/evm/precompile/mod.rs @@ -1,3 +1,4 @@ +use crate::evm::tracing::EventListener; use crate::evm::{database::Database, Machine}; use crate::types::Address; @@ -56,7 +57,7 @@ pub fn is_precompile_address(address: &Address) -> bool { || *address == SYSTEM_ACCOUNT_BLAKE2F } -impl Machine { +impl Machine { #[must_use] pub fn precompile(address: &Address, data: &[u8]) -> Option> { match *address { diff --git a/evm_loader/program/src/evm/tracing.rs b/evm_loader/program/src/evm/tracing.rs index d9386f6ee..8f7776206 100644 --- a/evm_loader/program/src/evm/tracing.rs +++ b/evm_loader/program/src/evm/tracing.rs @@ -1,19 +1,16 @@ -use std::cell::RefCell; -use std::fmt::Debug; -use std::rc::Rc; - +use super::{Context, ExitStatus}; +use crate::evm::database::Database; use ethnum::U256; -use serde_json::Value; -use super::{Context, ExitStatus}; +pub struct NoopEventListener; -pub trait EventListener: Debug { - fn event(&mut self, event: Event); - fn into_traces(self: Box) -> Value; +pub trait EventListener { + fn event(&mut self, executor_state: &impl Database, event: Event); } -pub type TracerType = Rc>>; -pub type TracerTypeOpt = Option; +impl EventListener for NoopEventListener { + fn event(&mut self, _executor_state: &impl Database, _event: Event) {} +} /// Trace event pub enum Event { diff --git a/evm_loader/program/src/instruction/transaction_execute.rs b/evm_loader/program/src/instruction/transaction_execute.rs index 9c27de786..a55800d5b 100644 --- a/evm_loader/program/src/instruction/transaction_execute.rs +++ b/evm_loader/program/src/instruction/transaction_execute.rs @@ -3,6 +3,7 @@ use solana_program::pubkey::Pubkey; use crate::account::{AccountsDB, AllocateResult}; use crate::account_storage::ProgramAccountStorage; use crate::error::{Error, Result}; +use crate::evm::tracing::NoopEventListener; use crate::evm::Machine; use crate::executor::ExecutorState; use crate::gasometer::Gasometer; @@ -38,8 +39,8 @@ pub fn execute( let (exit_reason, apply_state) = { let mut backend = ExecutorState::new(&account_storage); - let mut evm = Machine::new(trx, origin, &mut backend)?; - let (result, _) = evm.execute(u64::MAX, &mut backend)?; + let mut evm = Machine::new(trx, origin, &mut backend, None::)?; + let (result, _, _) = evm.execute(u64::MAX, &mut backend)?; let actions = backend.into_actions(); diff --git a/evm_loader/program/src/instruction/transaction_step.rs b/evm_loader/program/src/instruction/transaction_step.rs index c8b20a3af..f6ba7d642 100644 --- a/evm_loader/program/src/instruction/transaction_step.rs +++ b/evm_loader/program/src/instruction/transaction_step.rs @@ -2,13 +2,14 @@ use crate::account::{AccountsDB, AllocateResult, StateAccount}; use crate::account_storage::{AccountStorage, ProgramAccountStorage}; use crate::config::{EVM_STEPS_LAST_ITERATION_MAX, EVM_STEPS_MIN}; use crate::error::{Error, Result}; +use crate::evm::tracing::NoopEventListener; use crate::evm::{ExitStatus, Machine}; use crate::executor::{Action, ExecutorState}; use crate::gasometer::Gasometer; use crate::types::{Address, Transaction}; type EvmBackend<'a, 'r> = ExecutorState<'r, ProgramAccountStorage<'a>>; -type Evm<'a, 'r> = Machine>; +type Evm<'a, 'r> = Machine, NoopEventListener>; pub fn do_begin<'a>( accounts: AccountsDB<'a>, @@ -22,7 +23,7 @@ pub fn do_begin<'a>( let accounts = ProgramAccountStorage::new(accounts)?; let mut backend = ExecutorState::new(&accounts); - let evm = Machine::new(trx, origin, &mut backend)?; + let evm = Machine::new(trx, origin, &mut backend, None::)?; // Burn `gas_limit` tokens from the origin account // Later we will mint them to the operator @@ -50,9 +51,9 @@ pub fn do_continue<'a>( let account_storage = ProgramAccountStorage::new(accounts)?; let (mut backend, mut evm) = deserialize_evm_state(&storage, &account_storage)?; - let (result, steps_executed) = { + let (result, steps_executed, _) = { match backend.exit_status() { - Some(status) => (status.clone(), 0_u64), + Some(status) => (status.clone(), 0_u64, None), None => evm.execute(step_count, &mut backend)?, } }; From ce759a5e67c2c69c7b0842748869b2836a1c834e Mon Sep 17 00:00:00 2001 From: s-medvedev <40623263+s-medvedev@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:50:18 +0700 Subject: [PATCH 4/8] NDEV-2651 Remove syscall_stabs (#279) * NDEV-2651 Remove syscall_stabs --------- Co-authored-by: Semen Medvedev --- evm_loader/lib/src/account_storage.rs | 42 +++++++++---- evm_loader/lib/src/commands/emulate.rs | 2 - evm_loader/lib/src/lib.rs | 2 +- evm_loader/lib/src/syscall_stubs.rs | 60 ------------------- .../program/src/account/ether_balance.rs | 4 +- .../program/src/account/ether_contract.rs | 2 +- .../program/src/account/ether_storage.rs | 4 +- evm_loader/program/src/account/legacy/mod.rs | 16 +++-- evm_loader/program/src/account/program.rs | 7 ++- .../program/src/account_storage/apply.rs | 13 ++-- .../program/src/account_storage/backend.rs | 10 +++- .../program/src/account_storage/base.rs | 13 ++-- evm_loader/program/src/account_storage/mod.rs | 8 +++ evm_loader/program/src/debug.rs | 31 ++++++++++ evm_loader/program/src/entrypoint.rs | 2 +- evm_loader/program/src/error.rs | 10 ++-- evm_loader/program/src/evm/database.rs | 12 +++- evm_loader/program/src/evm/mod.rs | 6 +- evm_loader/program/src/evm/opcode.rs | 30 +++++----- .../executor/precompile_extension/metaplex.rs | 10 +--- .../precompile_extension/neon_token.rs | 10 ++-- .../precompile_extension/spl_token.rs | 20 ++----- evm_loader/program/src/executor/state.rs | 17 +++++- .../program/src/external_programs/metaplex.rs | 12 ++-- .../external_programs/spl_associated_token.rs | 5 +- .../src/instruction/account_block_add.rs | 2 +- .../src/instruction/account_create_balance.rs | 9 +-- .../src/instruction/account_holder_create.rs | 2 +- .../src/instruction/account_holder_delete.rs | 2 +- .../src/instruction/account_holder_write.rs | 5 +- .../src/instruction/collect_treasury.rs | 2 +- .../src/instruction/config_get_chain_count.rs | 2 +- .../src/instruction/config_get_chain_info.rs | 2 +- .../src/instruction/config_get_environment.rs | 2 +- .../config_get_property_by_index.rs | 2 +- .../config_get_property_by_name.rs | 2 +- .../instruction/config_get_property_count.rs | 2 +- .../src/instruction/config_get_status.rs | 2 +- .../src/instruction/config_get_version.rs | 2 +- .../src/instruction/create_main_treasury.rs | 6 +- .../src/instruction/neon_tokens_deposit.rs | 8 ++- .../src/instruction/transaction_cancel.rs | 9 +-- .../src/instruction/transaction_execute.rs | 3 +- .../transaction_execute_from_account.rs | 7 ++- .../transaction_execute_from_instruction.rs | 7 ++- .../src/instruction/transaction_step.rs | 9 ++- .../transaction_step_from_account.rs | 12 ++-- ...ransaction_step_from_account_no_chainid.rs | 2 +- .../transaction_step_from_instruction.rs | 11 ++-- 49 files changed, 247 insertions(+), 213 deletions(-) delete mode 100644 evm_loader/lib/src/syscall_stubs.rs diff --git a/evm_loader/lib/src/account_storage.rs b/evm_loader/lib/src/account_storage.rs index e7172e3b4..eaa6ed55a 100644 --- a/evm_loader/lib/src/account_storage.rs +++ b/evm_loader/lib/src/account_storage.rs @@ -8,7 +8,7 @@ use evm_loader::account_storage::find_slot_hash; use evm_loader::types::Address; use solana_sdk::rent::Rent; use solana_sdk::system_program; -use solana_sdk::sysvar::{slot_hashes, Sysvar}; +use solana_sdk::sysvar::slot_hashes; use std::collections::HashSet; use std::{cell::RefCell, collections::HashMap, convert::TryInto, rc::Rc}; @@ -51,6 +51,7 @@ pub struct EmulatorAccountStorage<'rpc, T: Rpc> { chains: Vec, block_number: u64, block_timestamp: i64, + rent: Rent, state_overrides: Option, } @@ -79,6 +80,14 @@ impl<'rpc, T: Rpc + BuildConfigSimulator> EmulatorAccountStorage<'rpc, T> { Some(chains) => chains, }; + let rent_account = rpc + .get_account(&solana_sdk::sysvar::rent::id()) + .await? + .value + .ok_or(NeonError::AccountNotFound(solana_sdk::sysvar::rent::id()))?; + let rent = bincode::deserialize::(&rent_account.data)?; + info!("Rent: {rent:?}"); + Ok(Self { accounts: RefCell::new(HashMap::new()), program_id, @@ -88,6 +97,7 @@ impl<'rpc, T: Rpc + BuildConfigSimulator> EmulatorAccountStorage<'rpc, T> { block_number, block_timestamp, state_overrides, + rent, }) } @@ -206,8 +216,6 @@ impl EmulatorAccountStorage<'_, T> { pub async fn apply_actions(&mut self, actions: Vec) -> Result<(), NeonError> { info!("apply_actions"); - let rent = Rent::get()?; - let mut new_balance_accounts = HashSet::new(); for action in actions { @@ -255,12 +263,13 @@ impl 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 } @@ -287,7 +296,7 @@ impl 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}"); @@ -313,7 +322,8 @@ impl 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), ); @@ -324,8 +334,6 @@ impl 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; @@ -354,7 +362,9 @@ impl 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); } } @@ -526,6 +536,16 @@ impl AccountStorage for EmulatorAccountStorage<'_, T> { self.block_timestamp.try_into().unwrap() } + fn rent(&self) -> &Rent { + &self.rent + } + + fn return_data(&self) -> Option<(Pubkey, Vec)> { + 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}"); diff --git a/evm_loader/lib/src/commands/emulate.rs b/evm_loader/lib/src/commands/emulate.rs index 05de4cd0c..05b76c922 100644 --- a/evm_loader/lib/src/commands/emulate.rs +++ b/evm_loader/lib/src/commands/emulate.rs @@ -8,7 +8,6 @@ use solana_sdk::pubkey::Pubkey; use crate::commands::get_config::BuildConfigSimulator; use crate::rpc::Rpc; -use crate::syscall_stubs::setup_emulator_syscall_stubs; use crate::tracing::tracers::Tracer; use crate::types::{EmulateRequest, TxParams}; use crate::{ @@ -78,7 +77,6 @@ pub async fn execute( let step_limit = emulate_request.step_limit.unwrap_or(100000); - setup_emulator_syscall_stubs(rpc).await?; emulate_trx(emulate_request.tx, &mut storage, step_limit, tracer).await } diff --git a/evm_loader/lib/src/lib.rs b/evm_loader/lib/src/lib.rs index eb80c7e78..d50c2f17b 100644 --- a/evm_loader/lib/src/lib.rs +++ b/evm_loader/lib/src/lib.rs @@ -5,7 +5,7 @@ pub mod commands; pub mod config; pub mod errors; pub mod rpc; -pub mod syscall_stubs; + pub mod tracing; pub mod types; diff --git a/evm_loader/lib/src/syscall_stubs.rs b/evm_loader/lib/src/syscall_stubs.rs deleted file mode 100644 index 0def2705b..000000000 --- a/evm_loader/lib/src/syscall_stubs.rs +++ /dev/null @@ -1,60 +0,0 @@ -use log::info; -use solana_sdk::{program_error::ProgramError, program_stubs::SyscallStubs, sysvar::rent::Rent}; - -use crate::{errors::NeonError, rpc::Rpc}; - -pub struct EmulatorStubs { - rent: Rent, -} - -impl EmulatorStubs { - pub async fn new(rpc: &impl Rpc) -> Result, NeonError> { - let rent_pubkey = solana_sdk::sysvar::rent::id(); - let data = rpc - .get_account(&rent_pubkey) - .await? - .value - .map(|a| a.data) - .unwrap_or_default(); - let rent = bincode::deserialize(&data).map_err(|_| ProgramError::InvalidArgument)?; - - Ok(Box::new(Self { rent })) - } -} - -impl SyscallStubs for EmulatorStubs { - fn sol_get_rent_sysvar(&self, pointer: *mut u8) -> u64 { - unsafe { - #[allow(clippy::cast_ptr_alignment)] - let rent = pointer.cast::(); - *rent = self.rent; - } - - 0 - } - - fn sol_log(&self, message: &str) { - info!("{}", message); - } - - fn sol_log_data(&self, fields: &[&[u8]]) { - let mut messages: Vec = Vec::new(); - - for f in fields { - if let Ok(str) = String::from_utf8(f.to_vec()) { - messages.push(str); - } else { - messages.push(hex::encode(f)); - } - } - - info!("Program Data: {}", messages.join(" ")); - } -} - -pub async fn setup_emulator_syscall_stubs(rpc: &impl Rpc) -> Result<(), NeonError> { - let syscall_stubs = EmulatorStubs::new(rpc).await?; - solana_sdk::program_stubs::set_syscall_stubs(syscall_stubs); - - Ok(()) -} diff --git a/evm_loader/program/src/account/ether_balance.rs b/evm_loader/program/src/account/ether_balance.rs index d84dd3925..dd316d95e 100644 --- a/evm_loader/program/src/account/ether_balance.rs +++ b/evm_loader/program/src/account/ether_balance.rs @@ -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}; @@ -46,6 +46,7 @@ impl<'a> BalanceAccount<'a> { chain_id: u64, accounts: &AccountsDB<'a>, keys: Option<&KeysCache>, + rent: &Rent, ) -> Result { let (pubkey, bump_seed) = keys.map_or_else( || address.find_balance_address(&crate::ID, chain_id), @@ -93,6 +94,7 @@ impl<'a> BalanceAccount<'a> { &account, program_seeds, ACCOUNT_PREFIX_LEN + size_of::
(), + rent, )?; super::set_tag(&crate::ID, &account, TAG_ACCOUNT_BALANCE)?; diff --git a/evm_loader/program/src/account/ether_contract.rs b/evm_loader/program/src/account/ether_contract.rs index 4eb41c658..64c30a91b 100644 --- a/evm_loader/program/src/account/ether_contract.rs +++ b/evm_loader/program/src/account/ether_contract.rs @@ -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)?; diff --git a/evm_loader/program/src/account/ether_storage.rs b/evm_loader/program/src/account/ether_storage.rs index f047b4da9..1e6b1dc97 100644 --- a/evm_loader/program/src/account/ether_storage.rs +++ b/evm_loader/program/src/account/ether_storage.rs @@ -97,6 +97,7 @@ impl<'a> StorageCell<'a> { allocate_cells: usize, accounts: &AccountsDB<'a>, signer_seeds: &[&[u8]], + rent: &Rent, ) -> Result { let base_account = accounts.get(&address.base); let cell_account = accounts.get(&address.pubkey); @@ -114,6 +115,7 @@ impl<'a> StorageCell<'a> { cell_account, address.seed(), space, + rent, )?; super::set_tag(&crate::ID, cell_account, TAG_STORAGE_CELL)?; @@ -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(()); diff --git a/evm_loader/program/src/account/legacy/mod.rs b/evm_loader/program/src/account/legacy/mod.rs index cf730df67..414ad4e65 100644 --- a/evm_loader/program/src/account/legacy/mod.rs +++ b/evm_loader/program/src/account/legacy/mod.rs @@ -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 { +fn reduce_account_size(account: &AccountInfo, required_len: usize, rent: &Rent) -> Result { 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; @@ -59,6 +58,7 @@ fn update_ether_account( legacy_data: &LegacyEtherData, db: &AccountsDB, keys: &KeysCache, + rent: &Rent, ) -> Result { let pubkey = keys.contract(&crate::ID, legacy_data.address); let account = db.get(&pubkey); @@ -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); @@ -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)?; @@ -117,6 +118,7 @@ fn update_storage_account( legacy_data: &LegacyStorageData, db: &AccountsDB, keys: &KeysCache, + rent: &Rent, ) -> Result { let mut lamports_collected = 0_u64; @@ -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); @@ -187,6 +189,8 @@ pub fn update_holder_account(account: &AccountInfo) -> Result { pub fn update_legacy_accounts(accounts: &AccountsDB) -> Result { let keys = KeysCache::new(); + let rent = Rent::get()?; + let mut lamports_collected = 0_u64; let mut legacy_storage = Vec::with_capacity(accounts.accounts_len()); @@ -203,7 +207,7 @@ pub fn update_legacy_accounts(accounts: &AccountsDB) -> Result { 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)?; @@ -214,7 +218,7 @@ pub fn update_legacy_accounts(accounts: &AccountsDB) -> Result { } 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) diff --git a/evm_loader/program/src/account/program.rs b/evm_loader/program/src/account/program.rs index 756d01220..0acdd48f0 100644 --- a/evm_loader/program/src/account/program.rs +++ b/evm_loader/program/src/account/program.rs @@ -3,7 +3,7 @@ use solana_program::account_info::AccountInfo; use solana_program::program::{invoke_signed_unchecked, invoke_unchecked}; use solana_program::program_error::ProgramError; use solana_program::pubkey::Pubkey; -use solana_program::{rent::Rent, system_instruction, sysvar::Sysvar}; +use solana_program::{rent::Rent, system_instruction}; use std::convert::From; use std::ops::Deref; @@ -31,8 +31,8 @@ impl<'a> System<'a> { new_account: &AccountInfo<'a>, new_account_seeds: &[&[u8]], space: usize, + rent: &Rent, ) -> Result<(), ProgramError> { - let rent = Rent::get()?; let minimum_balance = rent.minimum_balance(space).max(1); if new_account.lamports() > 0 { @@ -81,8 +81,9 @@ impl<'a> System<'a> { new_account: &AccountInfo<'a>, seed: &str, space: usize, + rent: &Rent, ) -> Result<(), ProgramError> { - let minimum_balance = Rent::get()?.minimum_balance(space).max(1); + let minimum_balance = rent.minimum_balance(space).max(1); if new_account.lamports() > 0 { let required_lamports = minimum_balance.saturating_sub(new_account.lamports()); diff --git a/evm_loader/program/src/account_storage/apply.rs b/evm_loader/program/src/account_storage/apply.rs index a98bfacde..0392d0a3a 100644 --- a/evm_loader/program/src/account_storage/apply.rs +++ b/evm_loader/program/src/account_storage/apply.rs @@ -4,9 +4,7 @@ use ethnum::U256; use solana_program::account_info::AccountInfo; use solana_program::instruction::Instruction; use solana_program::program::{invoke_signed_unchecked, invoke_unchecked}; -use solana_program::rent::Rent; use solana_program::system_program; -use solana_program::sysvar::Sysvar; use crate::account::BalanceAccount; use crate::account::{AllocateResult, ContractAccount, StorageCell}; @@ -48,14 +46,12 @@ impl<'a> ProgramAccountStorage<'a> { pub fn allocate(&mut self, actions: &[Action]) -> Result { let mut total_result = AllocateResult::Ready; - let rent = Rent::get()?; - for action in actions { if let Action::EvmSetCode { address, code, .. } = action { let result = ContractAccount::allocate( *address, code, - &rent, + &self.rent, &self.accounts, Some(&self.keys), )?; @@ -174,8 +170,6 @@ impl<'a> ProgramAccountStorage<'a> { fn apply_storage(&mut self, storage: HashMap>) -> Result<()> { const STATIC_STORAGE_LIMIT: U256 = U256::new(STORAGE_ENTRIES_IN_CONTRACT_ACCOUNT as u128); - let rent = Rent::get()?; - for (address, storage) in storage { let mut contract = self.contract_account(address)?; @@ -208,7 +202,8 @@ impl<'a> ProgramAccountStorage<'a> { let sign: &[&[u8]] = &[&[ACCOUNT_SEED_VERSION], address.as_bytes(), &[bump]]; let len = values.len(); - let mut storage = StorageCell::create(cell_address, len, &self.accounts, sign)?; + let mut storage = + StorageCell::create(cell_address, len, &self.accounts, sign, &self.rent)?; let mut cells = storage.cells_mut(); assert_eq!(cells.len(), len); @@ -222,7 +217,7 @@ impl<'a> ProgramAccountStorage<'a> { storage.update(subindex, &value)?; } - storage.sync_lamports(rent, &self.accounts)?; + storage.sync_lamports(&self.rent, &self.accounts)?; }; } } diff --git a/evm_loader/program/src/account_storage/backend.rs b/evm_loader/program/src/account_storage/backend.rs index 7cbd6406a..d6f7b0a05 100644 --- a/evm_loader/program/src/account_storage/backend.rs +++ b/evm_loader/program/src/account_storage/backend.rs @@ -5,7 +5,7 @@ use crate::executor::OwnedAccountInfo; use crate::types::Address; use ethnum::U256; use solana_program::account_info::AccountInfo; -use solana_program::{pubkey::Pubkey, sysvar::slot_hashes}; +use solana_program::{pubkey::Pubkey, rent::Rent, sysvar::slot_hashes}; use std::convert::TryInto; impl<'a> AccountStorage for ProgramAccountStorage<'a> { @@ -28,6 +28,14 @@ impl<'a> AccountStorage for ProgramAccountStorage<'a> { .expect("Timestamp is positive") } + fn rent(&self) -> &Rent { + &self.rent + } + + fn return_data(&self) -> Option<(Pubkey, Vec)> { + solana_program::program::get_return_data() + } + fn block_hash(&self, slot: u64) -> [u8; 32] { let slot_hashes_account = self.accounts.get(&slot_hashes::ID); let slot_hashes_data = slot_hashes_account.data.borrow(); diff --git a/evm_loader/program/src/account_storage/base.rs b/evm_loader/program/src/account_storage/base.rs index 3169d66a1..24464937d 100644 --- a/evm_loader/program/src/account_storage/base.rs +++ b/evm_loader/program/src/account_storage/base.rs @@ -6,9 +6,7 @@ use crate::config::DEFAULT_CHAIN_ID; use crate::error::Result; use crate::types::Address; use ethnum::U256; -use solana_program::clock::Clock; -use solana_program::system_program; -use solana_program::sysvar::Sysvar; +use solana_program::{clock::Clock, rent::Rent, system_program, sysvar::Sysvar}; use super::keys_cache::KeysCache; @@ -16,6 +14,7 @@ impl<'a> ProgramAccountStorage<'a> { pub fn new(accounts: AccountsDB<'a>) -> Result { Ok(Self { clock: Clock::get()?, + rent: Rent::get()?, accounts, keys: KeysCache::new(), }) @@ -88,6 +87,12 @@ impl<'a> ProgramAccountStorage<'a> { address: Address, chain_id: u64, ) -> Result> { - BalanceAccount::create(address, chain_id, &self.accounts, Some(&self.keys)) + BalanceAccount::create( + address, + chain_id, + &self.accounts, + Some(&self.keys), + &self.rent, + ) } } diff --git a/evm_loader/program/src/account_storage/mod.rs b/evm_loader/program/src/account_storage/mod.rs index 43489fe03..90972705f 100644 --- a/evm_loader/program/src/account_storage/mod.rs +++ b/evm_loader/program/src/account_storage/mod.rs @@ -8,6 +8,7 @@ use solana_program::account_info::AccountInfo; use {crate::account::AccountsDB, solana_program::clock::Clock}; use solana_program::pubkey::Pubkey; +use solana_program::rent::Rent; #[cfg(target_os = "solana")] mod apply; @@ -24,6 +25,7 @@ pub use keys_cache::KeysCache; #[cfg(target_os = "solana")] pub struct ProgramAccountStorage<'a> { clock: Clock, + rent: Rent, accounts: AccountsDB<'a>, keys: keys_cache::KeysCache, } @@ -44,6 +46,12 @@ pub trait AccountStorage { /// Get block hash async fn block_hash(&self, number: u64) -> [u8; 32]; + /// Get rent info + fn rent(&self) -> &Rent; + + /// Get return data from Solana + fn return_data(&self) -> Option<(Pubkey, Vec)>; + /// Get account nonce async fn nonce(&self, address: Address, chain_id: u64) -> u64; /// Get account balance diff --git a/evm_loader/program/src/debug.rs b/evm_loader/program/src/debug.rs index a23d4429d..e7fe5153f 100644 --- a/evm_loader/program/src/debug.rs +++ b/evm_loader/program/src/debug.rs @@ -15,3 +15,34 @@ macro_rules! debug_print { macro_rules! debug_print { ($( $args:expr ),*) => {}; } + +#[cfg(target_os = "solana")] +macro_rules! log_msg { + ($($arg:tt)*) => (solana_program::msg!($($arg)*)); +} + +#[cfg(not(target_os = "solana"))] +macro_rules! log_msg { + ($($arg:tt)*) => (log::info!($($arg)*)); +} + +#[inline] +pub fn log_data(data: &[&[u8]]) { + #[cfg(target_os = "solana")] + solana_program::log::sol_log_data(data); + + #[cfg(not(target_os = "solana"))] + { + let mut messages: Vec = Vec::new(); + + for f in data { + if let Ok(str) = String::from_utf8(f.to_vec()) { + messages.push(str); + } else { + messages.push(hex::encode(f)); + } + } + + log::info!("Program Data: {}", messages.join(" ")); + } +} diff --git a/evm_loader/program/src/entrypoint.rs b/evm_loader/program/src/entrypoint.rs index daa05f33c..3f64b0757 100644 --- a/evm_loader/program/src/entrypoint.rs +++ b/evm_loader/program/src/entrypoint.rs @@ -49,7 +49,7 @@ fn process_instruction<'a>( instruction::config_get_version::process(program_id, accounts, instruction) } _ => { - solana_program::msg!("Emergency image: all instructions are rejected"); + log_msg!("Emergency image: all instructions are rejected"); Err(ProgramError::InvalidInstructionData.into()) } } diff --git a/evm_loader/program/src/error.rs b/evm_loader/program/src/error.rs index d0049f7af..fdf64bccb 100644 --- a/evm_loader/program/src/error.rs +++ b/evm_loader/program/src/error.rs @@ -181,7 +181,7 @@ pub type Result = std::result::Result; impl From for ProgramError { fn from(e: Error) -> Self { - solana_program::msg!("{}", e); + log_msg!("{}", e); match e { Error::ProgramError(e) => e, _ => Self::Custom(0), @@ -269,18 +269,18 @@ fn format_revert_panic(msg: &[u8]) -> Option { pub fn print_revert_message(msg: &[u8]) { if msg.is_empty() { - return solana_program::msg!("Revert"); + return log_msg!("Revert"); } if let Some(reason) = format_revert_error(msg) { - return solana_program::msg!("Revert: Error(\"{}\")", reason); + return log_msg!("Revert: Error(\"{}\")", reason); } if let Some(reason) = format_revert_panic(msg) { - return solana_program::msg!("Revert: Panic({:#x})", reason); + return log_msg!("Revert: Panic({:#x})", reason); } - solana_program::msg!("Revert: {}", hex::encode(msg)); + log_msg!("Revert: {}", hex::encode(msg)); } #[must_use] diff --git a/evm_loader/program/src/evm/database.rs b/evm_loader/program/src/evm/database.rs index 73ea18cde..827bee83f 100644 --- a/evm_loader/program/src/evm/database.rs +++ b/evm_loader/program/src/evm/database.rs @@ -2,7 +2,7 @@ use super::{Buffer, Context}; use crate::{error::Result, types::Address}; use ethnum::U256; use maybe_async::maybe_async; -use solana_program::{account_info::AccountInfo, pubkey::Pubkey}; +use solana_program::{account_info::AccountInfo, pubkey::Pubkey, rent::Rent}; #[maybe_async(?Send)] pub trait Database { @@ -32,6 +32,8 @@ pub trait Database { async fn block_hash(&self, number: U256) -> Result<[u8; 32]>; fn block_number(&self) -> Result; fn block_timestamp(&self) -> Result; + fn rent(&self) -> &Rent; + fn return_data(&self) -> Option<(Pubkey, Vec)>; async fn map_solana_account(&self, address: &Pubkey, action: F) -> R where @@ -224,6 +226,14 @@ mod tests { unimplemented!(); } + fn rent(&self) -> &Rent { + unimplemented!(); + } + + fn return_data(&self) -> Option<(Pubkey, Vec)> { + unimplemented!(); + } + async fn map_solana_account(&self, address: &Pubkey, action: F) -> R where F: FnOnce(&AccountInfo) -> R, diff --git a/evm_loader/program/src/evm/mod.rs b/evm_loader/program/src/evm/mod.rs index 8ac20e696..b26c65cc1 100644 --- a/evm_loader/program/src/evm/mod.rs +++ b/evm_loader/program/src/evm/mod.rs @@ -7,7 +7,6 @@ use std::{fmt::Display, marker::PhantomData, ops::Range}; use ethnum::U256; use maybe_async::maybe_async; use serde::{Deserialize, Serialize}; -use solana_program::log::sol_log_data; pub use buffer::Buffer; @@ -15,6 +14,7 @@ use crate::evm::tracing::EventListener; #[cfg(target_os = "solana")] use crate::evm::tracing::NoopEventListener; use crate::{ + debug::log_data, error::{build_revert_message, Error, Result}, evm::{opcode::Action, precompile::is_precompile_address}, types::{Address, Transaction}, @@ -263,7 +263,7 @@ impl Machine { assert!(trx.target().is_some()); let target = trx.target().unwrap(); - sol_log_data(&[b"ENTER", b"CALL", target.as_bytes()]); + log_data(&[b"ENTER", b"CALL", target.as_bytes()]); backend.increment_nonce(origin, chain_id)?; backend.snapshot(); @@ -312,7 +312,7 @@ impl Machine { assert!(trx.target().is_none()); let target = Address::from_create(&origin, trx.nonce()); - sol_log_data(&[b"ENTER", b"CREATE", target.as_bytes()]); + log_data(&[b"ENTER", b"CREATE", target.as_bytes()]); if (backend.nonce(target, chain_id).await? != 0) || (backend.code_size(target).await? != 0) { diff --git a/evm_loader/program/src/evm/opcode.rs b/evm_loader/program/src/evm/opcode.rs index d1d42d2df..38941e9eb 100644 --- a/evm_loader/program/src/evm/opcode.rs +++ b/evm_loader/program/src/evm/opcode.rs @@ -1,7 +1,6 @@ /// use ethnum::{I256, U256}; use maybe_async::maybe_async; -use solana_program::log::sol_log_data; use super::{ database::{Database, DatabaseExt}, @@ -9,6 +8,7 @@ use super::{ }; use crate::evm::tracing::EventListener; use crate::{ + debug::log_data, error::{Error, Result}, evm::{trace_end_step, Buffer}, types::Address, @@ -993,11 +993,11 @@ impl Machine { let address = self.context.contract.as_bytes(); match N { - 0 => sol_log_data(&[b"LOG0", address, &[0], data]), - 1 => sol_log_data(&[b"LOG1", address, &[1], &topics[0], data]), - 2 => sol_log_data(&[b"LOG2", address, &[2], &topics[0], &topics[1], data]), - 3 => sol_log_data(&[b"LOG3", address, &[3], &topics[0], &topics[1], &topics[2], data]), - 4 => sol_log_data(&[b"LOG4", address, &[4], &topics[0], &topics[1], &topics[2], &topics[3], data]), + 0 => log_data(&[b"LOG0", address, &[0], data]), + 1 => log_data(&[b"LOG1", address, &[1], &topics[0], data]), + 2 => log_data(&[b"LOG2", address, &[2], &topics[0], &topics[1], data]), + 3 => log_data(&[b"LOG3", address, &[3], &topics[0], &topics[1], &topics[2], data]), + 4 => log_data(&[b"LOG4", address, &[4], &topics[0], &topics[1], &topics[2], &topics[3], data]), _ => unreachable!(), } @@ -1099,7 +1099,7 @@ impl Machine { ); backend.snapshot(); - sol_log_data(&[b"ENTER", b"CREATE", address.as_bytes()]); + log_data(&[b"ENTER", b"CREATE", address.as_bytes()]); if (backend.nonce(address, chain_id).await? != 0) || (backend.code_size(address).await? != 0) @@ -1160,7 +1160,7 @@ impl Machine { ); backend.snapshot(); - sol_log_data(&[b"ENTER", b"CALL", address.as_bytes()]); + log_data(&[b"ENTER", b"CALL", address.as_bytes()]); if self.is_static && (value != U256::ZERO) { return Err(Error::StaticModeViolation(self.context.caller)); @@ -1216,7 +1216,7 @@ impl Machine { ); backend.snapshot(); - sol_log_data(&[b"ENTER", b"CALLCODE", address.as_bytes()]); + log_data(&[b"ENTER", b"CALLCODE", address.as_bytes()]); if backend.balance(self.context.caller, chain_id).await? < value { return Err(Error::InsufficientBalance( @@ -1270,7 +1270,7 @@ impl Machine { ); backend.snapshot(); - sol_log_data(&[b"ENTER", b"DELEGATECALL", address.as_bytes()]); + log_data(&[b"ENTER", b"DELEGATECALL", address.as_bytes()]); self.opcode_call_precompile_impl(backend, &address).await } @@ -1322,7 +1322,7 @@ impl Machine { backend.snapshot(); - sol_log_data(&[b"ENTER", b"STATICCALL", address.as_bytes()]); + log_data(&[b"ENTER", b"STATICCALL", address.as_bytes()]); self.opcode_call_precompile_impl(backend, &address).await } @@ -1375,7 +1375,7 @@ impl Machine { } backend.commit_snapshot(); - sol_log_data(&[b"EXIT", b"RETURN"]); + log_data(&[b"EXIT", b"RETURN"]); if self.parent.is_none() { return Ok(Action::Return(return_data)); @@ -1425,7 +1425,7 @@ impl Machine { backend: &mut B, ) -> Result { backend.revert_snapshot(); - sol_log_data(&[b"EXIT", b"REVERT", &return_data]); + log_data(&[b"EXIT", b"REVERT", &return_data]); if self.parent.is_none() { return Ok(Action::Revert(return_data)); @@ -1482,7 +1482,7 @@ impl Machine { backend.selfdestruct(self.context.contract)?; backend.commit_snapshot(); - sol_log_data(&[b"EXIT", b"SELFDESTRUCT"]); + log_data(&[b"EXIT", b"SELFDESTRUCT"]); if self.parent.is_none() { return Ok(Action::Suicide); @@ -1515,7 +1515,7 @@ impl Machine { #[maybe_async] pub async fn opcode_stop(&mut self, backend: &mut B) -> Result { backend.commit_snapshot(); - sol_log_data(&[b"EXIT", b"STOP"]); + log_data(&[b"EXIT", b"STOP"]); if self.parent.is_none() { return Ok(Action::Stop); diff --git a/evm_loader/program/src/executor/precompile_extension/metaplex.rs b/evm_loader/program/src/executor/precompile_extension/metaplex.rs index aaca012ee..be7ed8194 100644 --- a/evm_loader/program/src/executor/precompile_extension/metaplex.rs +++ b/evm_loader/program/src/executor/precompile_extension/metaplex.rs @@ -7,7 +7,7 @@ use mpl_token_metadata::state::{ Creator, Metadata, TokenMetadataAccount, TokenStandard, CREATE_FEE, MAX_MASTER_EDITION_LEN, MAX_METADATA_LEN, }; -use solana_program::{pubkey::Pubkey, rent::Rent, sysvar::Sysvar}; +use solana_program::pubkey::Pubkey; use crate::{ account::ACCOUNT_SEED_VERSION, @@ -193,9 +193,7 @@ impl ExecutorState<'_, B> { None, // Collection Details ); - let rent = Rent::get()?; - let fee = rent.minimum_balance(MAX_METADATA_LEN) + CREATE_FEE; - + let fee = self.backend.rent().minimum_balance(MAX_METADATA_LEN) + CREATE_FEE; self.queue_external_instruction(instruction, seeds, fee); Ok(metadata_pubkey.to_bytes().to_vec()) @@ -230,9 +228,7 @@ impl ExecutorState<'_, B> { max_supply, ); - let rent = Rent::get()?; - let fee = rent.minimum_balance(MAX_MASTER_EDITION_LEN) + CREATE_FEE; - + let fee = self.backend.rent().minimum_balance(MAX_MASTER_EDITION_LEN) + CREATE_FEE; self.queue_external_instruction(instruction, seeds, fee); Ok(edition_pubkey.to_bytes().to_vec()) diff --git a/evm_loader/program/src/executor/precompile_extension/neon_token.rs b/evm_loader/program/src/executor/precompile_extension/neon_token.rs index 31f6c2d70..8fef61051 100644 --- a/evm_loader/program/src/executor/precompile_extension/neon_token.rs +++ b/evm_loader/program/src/executor/precompile_extension/neon_token.rs @@ -3,9 +3,7 @@ use std::convert::TryInto; use arrayref::array_ref; use ethnum::U256; use maybe_async::maybe_async; -use solana_program::{ - account_info::IntoAccountInfo, program_pack::Pack, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, -}; +use solana_program::{account_info::IntoAccountInfo, program_pack::Pack, pubkey::Pubkey}; use spl_associated_token_account::get_associated_token_address; use crate::{ @@ -118,8 +116,10 @@ impl ExecutorState<'_, B> { &spl_token::ID, ); - let rent = Rent::get()?; - let fee = rent.minimum_balance(spl_token::state::Account::LEN); + let fee = self + .backend + .rent() + .minimum_balance(spl_token::state::Account::LEN); self.queue_external_instruction(create_associated, vec![], fee); } diff --git a/evm_loader/program/src/executor/precompile_extension/spl_token.rs b/evm_loader/program/src/executor/precompile_extension/spl_token.rs index 39ca0b34e..20119d6ce 100644 --- a/evm_loader/program/src/executor/precompile_extension/spl_token.rs +++ b/evm_loader/program/src/executor/precompile_extension/spl_token.rs @@ -3,8 +3,8 @@ use std::convert::{Into, TryInto}; use ethnum::U256; use maybe_async::maybe_async; use solana_program::{ - program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, rent::Rent, - system_instruction, system_program, sysvar::Sysvar, + program_error::ProgramError, program_pack::Pack, pubkey::Pubkey, system_instruction, + system_program, }; use crate::{ @@ -260,14 +260,8 @@ impl ExecutorState<'_, B> { } } - fn create_account( - &mut self, - account: &OwnedAccountInfo, - space: usize, - seeds: Vec>, - ) -> Result<()> { - let rent = Rent::get()?; - let minimum_balance = rent.minimum_balance(space); + fn create_account(&mut self, account: &OwnedAccountInfo, space: usize, seeds: Vec>) { + let minimum_balance = self.backend.rent().minimum_balance(space); let required_lamports = minimum_balance.saturating_sub(account.lamports); @@ -285,8 +279,6 @@ impl ExecutorState<'_, B> { let assign = system_instruction::assign(&account.key, &spl_token::ID); self.queue_external_instruction(assign, seeds, 0); - - Ok(()) } #[maybe_async] @@ -324,7 +316,7 @@ impl ExecutorState<'_, B> { vec![bump_seed], ]; - self.create_account(&account, spl_token::state::Mint::LEN, seeds)?; + self.create_account(&account, spl_token::state::Mint::LEN, seeds); let initialize_mint = spl_token::instruction::initialize_mint( &spl_token::ID, @@ -372,7 +364,7 @@ impl ExecutorState<'_, B> { vec![bump_seed], ]; - self.create_account(&account, spl_token::state::Account::LEN, seeds)?; + self.create_account(&account, spl_token::state::Account::LEN, seeds); let initialize_mint = spl_token::instruction::initialize_account2( &spl_token::ID, diff --git a/evm_loader/program/src/executor/state.rs b/evm_loader/program/src/executor/state.rs index 5a79b16a8..9fc3b1980 100644 --- a/evm_loader/program/src/executor/state.rs +++ b/evm_loader/program/src/executor/state.rs @@ -5,6 +5,7 @@ use ethnum::{AsU256, U256}; use maybe_async::maybe_async; use solana_program::instruction::Instruction; use solana_program::pubkey::Pubkey; +use solana_program::rent::Rent; use crate::account_storage::AccountStorage; use crate::error::{Error, Result}; @@ -155,10 +156,16 @@ impl<'a, B: AccountStorage> ExecutorState<'a, B> { data, meta, &mut accounts, + self.rent(), )?; } program_id if mpl_token_metadata::check_id(program_id) => { - crate::external_programs::metaplex::emulate(data, meta, &mut accounts)?; + crate::external_programs::metaplex::emulate( + data, + meta, + &mut accounts, + self.rent(), + )?; } _ => { return Err(Error::Custom(format!( @@ -387,6 +394,14 @@ impl<'a, B: AccountStorage> Database for ExecutorState<'a, B> { Ok(cache.block_timestamp) } + fn rent(&self) -> &Rent { + self.backend.rent() + } + + fn return_data(&self) -> Option<(Pubkey, Vec)> { + self.backend.return_data() + } + async fn map_solana_account(&self, address: &Pubkey, action: F) -> R where F: FnOnce(&solana_program::account_info::AccountInfo) -> R, diff --git a/evm_loader/program/src/external_programs/metaplex.rs b/evm_loader/program/src/external_programs/metaplex.rs index 0f2c513ad..a9fe278b8 100644 --- a/evm_loader/program/src/external_programs/metaplex.rs +++ b/evm_loader/program/src/external_programs/metaplex.rs @@ -6,7 +6,6 @@ use solana_program::account_info::IntoAccountInfo; use solana_program::instruction::AccountMeta; use solana_program::program_option::COption; use solana_program::rent::Rent; -use solana_program::sysvar::Sysvar; use spl_token::state::Mint; use std::collections::BTreeMap; @@ -26,13 +25,14 @@ pub fn emulate( instruction: &[u8], meta: &[AccountMeta], accounts: &mut BTreeMap, + rent: &Rent, ) -> ProgramResult { match MetadataInstruction::try_from_slice(instruction)? { MetadataInstruction::CreateMetadataAccountV3(args) => { - create_metadata_accounts_v3(meta, accounts, &args) + create_metadata_accounts_v3(meta, accounts, &args, rent) } MetadataInstruction::CreateMasterEditionV3(args) => { - create_master_edition_v3(meta, accounts, &args) + create_master_edition_v3(meta, accounts, &args, rent) } _ => Err!(ProgramError::InvalidInstructionData; "Unknown Metaplex instruction"), } @@ -42,6 +42,7 @@ fn create_metadata_accounts_v3( meta: &[AccountMeta], accounts: &mut BTreeMap, args: &CreateMetadataAccountArgsV3, + rent: &Rent, ) -> ProgramResult { let metadata_account_key = &meta[0].pubkey; let mint_key = &meta[1].pubkey; @@ -52,8 +53,6 @@ fn create_metadata_accounts_v3( // let _rent_key = &meta[6].pubkey; let mut metadata: Metadata = { - let rent = Rent::get()?; - let metadata_account = accounts.get_mut(metadata_account_key).unwrap(); metadata_account.data.resize(MAX_METADATA_LEN, 0); metadata_account.owner = mpl_token_metadata::ID; @@ -123,6 +122,7 @@ fn create_master_edition_v3( meta: &[AccountMeta], accounts: &mut BTreeMap, args: &CreateMasterEditionArgs, + rent: &Rent, ) -> ProgramResult { let edition_account_key = &meta[0].pubkey; let mint_key = &meta[1].pubkey; @@ -160,8 +160,6 @@ fn create_master_edition_v3( } { - let rent = Rent::get()?; - let edition_account = accounts.get_mut(edition_account_key).unwrap(); edition_account.data.resize(MAX_MASTER_EDITION_LEN, 0); edition_account.owner = mpl_token_metadata::ID; diff --git a/evm_loader/program/src/external_programs/spl_associated_token.rs b/evm_loader/program/src/external_programs/spl_associated_token.rs index 9ef2ae843..0491cc903 100644 --- a/evm_loader/program/src/external_programs/spl_associated_token.rs +++ b/evm_loader/program/src/external_programs/spl_associated_token.rs @@ -4,7 +4,7 @@ use crate::executor::OwnedAccountInfo; use borsh::BorshDeserialize; use solana_program::{ entrypoint::ProgramResult, instruction::AccountMeta, program_error::ProgramError, - program_pack::Pack, pubkey::Pubkey, rent::Rent, sysvar::Sysvar, + program_pack::Pack, pubkey::Pubkey, rent::Rent, }; use spl_associated_token_account::instruction::AssociatedTokenAccountInstruction; @@ -12,6 +12,7 @@ pub fn emulate( instruction: &[u8], meta: &[AccountMeta], accounts: &mut BTreeMap, + rent: &Rent, ) -> ProgramResult { let instruction = if instruction.is_empty() { AssociatedTokenAccountInstruction::Create @@ -33,8 +34,6 @@ pub fn emulate( let required_lamports = { let associated_token_account = &accounts[associated_token_account_key]; - - let rent = Rent::get()?; rent.minimum_balance(spl_token::state::Account::LEN) .max(1) .saturating_sub(associated_token_account.lamports) diff --git a/evm_loader/program/src/instruction/account_block_add.rs b/evm_loader/program/src/instruction/account_block_add.rs index ab2f5254b..b875f6a12 100644 --- a/evm_loader/program/src/instruction/account_block_add.rs +++ b/evm_loader/program/src/instruction/account_block_add.rs @@ -10,7 +10,7 @@ pub fn process<'a>( let stack_height = solana_program::instruction::get_stack_height(); assert_eq!(stack_height, TRANSACTION_LEVEL_STACK_HEIGHT); - solana_program::msg!("Instruction: Block Accounts"); + log_msg!("Instruction: Block Accounts"); todo!(); diff --git a/evm_loader/program/src/instruction/account_create_balance.rs b/evm_loader/program/src/instruction/account_create_balance.rs index 878eb4c78..8693310ab 100644 --- a/evm_loader/program/src/instruction/account_create_balance.rs +++ b/evm_loader/program/src/instruction/account_create_balance.rs @@ -1,5 +1,5 @@ use arrayref::array_ref; -use solana_program::{account_info::AccountInfo, pubkey::Pubkey}; +use solana_program::{account_info::AccountInfo, pubkey::Pubkey, rent::Rent, sysvar::Sysvar}; use crate::account::{program, AccountsDB, BalanceAccount, Operator}; use crate::config::{CHAIN_ID_LIST, DEFAULT_CHAIN_ID}; @@ -11,7 +11,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Create Balance Account"); + log_msg!("Instruction: Create Balance Account"); let operator = unsafe { Operator::from_account_not_whitelisted(&accounts[0]) }?; let system = program::System::from_account(&accounts[1])?; @@ -28,7 +28,7 @@ pub fn process<'a>( .binary_search_by_key(&chain_id, |c| c.0) .map_err(|_| Error::InvalidChainId(chain_id))?; - solana_program::msg!("Address: {}, ChainID: {}", address, chain_id); + log_msg!("Address: {}, ChainID: {}", address, chain_id); let mut excessive_lamports = 0; if chain_id == DEFAULT_CHAIN_ID { @@ -36,7 +36,8 @@ pub fn process<'a>( excessive_lamports += crate::account::legacy::update_legacy_accounts(&accounts_db)?; }; - BalanceAccount::create(address, chain_id, &accounts_db, None)?; + let rent = Rent::get()?; + BalanceAccount::create(address, chain_id, &accounts_db, None, &rent)?; **accounts_db.operator().try_borrow_mut_lamports()? += excessive_lamports; diff --git a/evm_loader/program/src/instruction/account_holder_create.rs b/evm_loader/program/src/instruction/account_holder_create.rs index 7509b9d1c..ac6687d2b 100644 --- a/evm_loader/program/src/instruction/account_holder_create.rs +++ b/evm_loader/program/src/instruction/account_holder_create.rs @@ -8,7 +8,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Create Holder Account"); + log_msg!("Instruction: Create Holder Account"); let holder = accounts[0].clone(); let operator = unsafe { Operator::from_account_not_whitelisted(&accounts[1]) }?; diff --git a/evm_loader/program/src/instruction/account_holder_delete.rs b/evm_loader/program/src/instruction/account_holder_delete.rs index 8873f2642..6920b803b 100644 --- a/evm_loader/program/src/instruction/account_holder_delete.rs +++ b/evm_loader/program/src/instruction/account_holder_delete.rs @@ -7,7 +7,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], _instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Delete Holder Account"); + log_msg!("Instruction: Delete Holder Account"); let holder_info = accounts[0].clone(); let operator = unsafe { Operator::from_account_not_whitelisted(&accounts[1]) }?; diff --git a/evm_loader/program/src/instruction/account_holder_write.rs b/evm_loader/program/src/instruction/account_holder_write.rs index 47cca15eb..e9a5e8781 100644 --- a/evm_loader/program/src/instruction/account_holder_write.rs +++ b/evm_loader/program/src/instruction/account_holder_write.rs @@ -1,4 +1,5 @@ use crate::account::{Holder, Operator}; +use crate::debug::log_data; use crate::error::Result; use arrayref::array_ref; use solana_program::{account_info::AccountInfo, pubkey::Pubkey}; @@ -8,7 +9,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Write To Holder"); + log_msg!("Instruction: Write To Holder"); let transaction_hash = *array_ref![instruction, 0, 32]; let offset = usize::from_le_bytes(*array_ref![instruction, 32, 8]); @@ -23,7 +24,7 @@ pub fn process<'a>( holder.validate_owner(&operator)?; holder.update_transaction_hash(transaction_hash); - solana_program::log::sol_log_data(&[b"HASH", &transaction_hash]); + log_data(&[b"HASH", &transaction_hash]); holder.write(offset, data)?; diff --git a/evm_loader/program/src/instruction/collect_treasury.rs b/evm_loader/program/src/instruction/collect_treasury.rs index bc950d378..af7547e26 100644 --- a/evm_loader/program/src/instruction/collect_treasury.rs +++ b/evm_loader/program/src/instruction/collect_treasury.rs @@ -13,7 +13,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> ProgramResult { - solana_program::msg!("Instruction: Collect treasury"); + log_msg!("Instruction: Collect treasury"); let treasury_index = u32::from_le_bytes(*array_ref![instruction, 0, 4]); diff --git a/evm_loader/program/src/instruction/config_get_chain_count.rs b/evm_loader/program/src/instruction/config_get_chain_count.rs index bc932f2e9..d917b85a6 100644 --- a/evm_loader/program/src/instruction/config_get_chain_count.rs +++ b/evm_loader/program/src/instruction/config_get_chain_count.rs @@ -7,7 +7,7 @@ pub fn process<'a>( _accounts: &'a [AccountInfo<'a>], _instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Config Get Chain Count"); + log_msg!("Instruction: Config Get Chain Count"); let count = crate::config::CHAIN_ID_LIST.len(); diff --git a/evm_loader/program/src/instruction/config_get_chain_info.rs b/evm_loader/program/src/instruction/config_get_chain_info.rs index 13b1a397e..157539c8e 100644 --- a/evm_loader/program/src/instruction/config_get_chain_info.rs +++ b/evm_loader/program/src/instruction/config_get_chain_info.rs @@ -7,7 +7,7 @@ pub fn process<'a>( _accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Config Get Chain Info"); + log_msg!("Instruction: Config Get Chain Info"); let bytes = instruction.try_into()?; let index = usize::from_le_bytes(bytes); diff --git a/evm_loader/program/src/instruction/config_get_environment.rs b/evm_loader/program/src/instruction/config_get_environment.rs index e4c8513f7..8a9e08074 100644 --- a/evm_loader/program/src/instruction/config_get_environment.rs +++ b/evm_loader/program/src/instruction/config_get_environment.rs @@ -7,7 +7,7 @@ pub fn process<'a>( _accounts: &'a [AccountInfo<'a>], _instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Config Get Environment"); + log_msg!("Instruction: Config Get Environment"); let environment: &str = if cfg!(feature = "mainnet") { "mainnet" diff --git a/evm_loader/program/src/instruction/config_get_property_by_index.rs b/evm_loader/program/src/instruction/config_get_property_by_index.rs index 19c6d6e31..ac9f62627 100644 --- a/evm_loader/program/src/instruction/config_get_property_by_index.rs +++ b/evm_loader/program/src/instruction/config_get_property_by_index.rs @@ -7,7 +7,7 @@ pub fn process<'a>( _accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Config Get Property by Index"); + log_msg!("Instruction: Config Get Property by Index"); let bytes = instruction.try_into()?; let index = usize::from_le_bytes(bytes); diff --git a/evm_loader/program/src/instruction/config_get_property_by_name.rs b/evm_loader/program/src/instruction/config_get_property_by_name.rs index 3937c7c69..e4397b2e9 100644 --- a/evm_loader/program/src/instruction/config_get_property_by_name.rs +++ b/evm_loader/program/src/instruction/config_get_property_by_name.rs @@ -7,7 +7,7 @@ pub fn process<'a>( _accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Config Get Property by Name"); + log_msg!("Instruction: Config Get Property by Name"); let requested_property = std::str::from_utf8(instruction)?; diff --git a/evm_loader/program/src/instruction/config_get_property_count.rs b/evm_loader/program/src/instruction/config_get_property_count.rs index c246b6b0a..2732f22f2 100644 --- a/evm_loader/program/src/instruction/config_get_property_count.rs +++ b/evm_loader/program/src/instruction/config_get_property_count.rs @@ -7,7 +7,7 @@ pub fn process<'a>( _accounts: &'a [AccountInfo<'a>], _instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Config Get Property Count"); + log_msg!("Instruction: Config Get Property Count"); let count = crate::config::PARAMETERS.len(); diff --git a/evm_loader/program/src/instruction/config_get_status.rs b/evm_loader/program/src/instruction/config_get_status.rs index 42c5b9af0..158e2c46d 100644 --- a/evm_loader/program/src/instruction/config_get_status.rs +++ b/evm_loader/program/src/instruction/config_get_status.rs @@ -7,7 +7,7 @@ pub fn process<'a>( _accounts: &'a [AccountInfo<'a>], _instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Config Get Status"); + log_msg!("Instruction: Config Get Status"); if cfg!(feature = "emergency") { solana_program::program::set_return_data(&[0]); diff --git a/evm_loader/program/src/instruction/config_get_version.rs b/evm_loader/program/src/instruction/config_get_version.rs index 3d16b2b52..9d0b0dc1a 100644 --- a/evm_loader/program/src/instruction/config_get_version.rs +++ b/evm_loader/program/src/instruction/config_get_version.rs @@ -10,7 +10,7 @@ pub fn process<'a>( _accounts: &'a [AccountInfo<'a>], _instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Config Get Version"); + log_msg!("Instruction: Config Get Version"); let version = std::str::from_utf8(&NEON_PKG_VERSION)?; let revision = std::str::from_utf8(&NEON_REVISION)?; diff --git a/evm_loader/program/src/instruction/create_main_treasury.rs b/evm_loader/program/src/instruction/create_main_treasury.rs index 86c5f7972..1378551ad 100644 --- a/evm_loader/program/src/instruction/create_main_treasury.rs +++ b/evm_loader/program/src/instruction/create_main_treasury.rs @@ -6,10 +6,11 @@ use crate::{ use solana_program::{ account_info::AccountInfo, bpf_loader_upgradeable::{self, UpgradeableLoaderState}, - msg, program_pack::Pack, pubkey::Pubkey, + rent::Rent, system_program, + sysvar::Sysvar, }; struct Accounts<'a> { @@ -69,7 +70,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], _instruction: &[u8], ) -> Result<()> { - msg!("Instruction: Create Main Treasury"); + log_msg!("Instruction: Create Main Treasury"); let accounts = Accounts::from_slice(accounts)?; let (expected_key, bump_seed) = MainTreasury::address(program_id); @@ -120,6 +121,7 @@ pub fn process<'a>( accounts.main_treasury, &[TREASURY_POOL_SEED.as_bytes(), &[bump_seed]], spl_token::state::Account::LEN, + &Rent::get()?, )?; accounts.token_program.create_account( diff --git a/evm_loader/program/src/instruction/neon_tokens_deposit.rs b/evm_loader/program/src/instruction/neon_tokens_deposit.rs index b0ecb80fc..bfab10115 100644 --- a/evm_loader/program/src/instruction/neon_tokens_deposit.rs +++ b/evm_loader/program/src/instruction/neon_tokens_deposit.rs @@ -1,7 +1,7 @@ use arrayref::array_ref; use ethnum::U256; use solana_program::program::invoke_signed; -use solana_program::{account_info::AccountInfo, pubkey::Pubkey}; +use solana_program::{account_info::AccountInfo, pubkey::Pubkey, rent::Rent, sysvar::Sysvar}; use spl_associated_token_account::get_associated_token_address; use crate::account::{program, token, AccountsDB, BalanceAccount, Operator, ACCOUNT_SEED_VERSION}; @@ -42,7 +42,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Deposit"); + log_msg!("Instruction: Deposit"); let parsed_accounts = Accounts::from_slice(accounts)?; @@ -162,7 +162,9 @@ fn execute(program_id: &Pubkey, accounts: Accounts, address: Address, chain_id: excessive_lamports += crate::account::legacy::update_legacy_accounts(&accounts_db)?; } - let mut balance_account = BalanceAccount::create(address, chain_id, &accounts_db, None)?; + let rent = Rent::get()?; + + let mut balance_account = BalanceAccount::create(address, chain_id, &accounts_db, None, &rent)?; balance_account.mint(deposit)?; **accounts_db.operator().try_borrow_mut_lamports()? += excessive_lamports; diff --git a/evm_loader/program/src/instruction/transaction_cancel.rs b/evm_loader/program/src/instruction/transaction_cancel.rs index 21360e828..bfe195ea5 100644 --- a/evm_loader/program/src/instruction/transaction_cancel.rs +++ b/evm_loader/program/src/instruction/transaction_cancel.rs @@ -1,4 +1,5 @@ use crate::account::{AccountsDB, BalanceAccount, Operator, StateAccount}; +use crate::debug::log_data; use crate::error::{Error, Result}; use crate::gasometer::{CANCEL_TRX_COST, LAST_ITERATION_COST}; use arrayref::array_ref; @@ -10,7 +11,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Cancel Transaction"); + log_msg!("Instruction: Cancel Transaction"); let transaction_hash = array_ref![instruction, 0, 32]; @@ -18,8 +19,8 @@ pub fn process<'a>( let operator = Operator::from_account(&accounts[1])?; let operator_balance = BalanceAccount::from_account(program_id, accounts[2].clone())?; - solana_program::log::sol_log_data(&[b"HASH", transaction_hash]); - solana_program::log::sol_log_data(&[b"MINER", operator_balance.address().as_bytes()]); + log_data(&[b"HASH", transaction_hash]); + log_data(&[b"MINER", operator_balance.address().as_bytes()]); let accounts_db = AccountsDB::new(&accounts[3..], operator, Some(operator_balance), None, None); let storage = StateAccount::restore(program_id, storage_info, &accounts_db, true)?; @@ -46,7 +47,7 @@ fn execute<'a>( ) -> Result<()> { let used_gas = U256::ZERO; let total_used_gas = storage.gas_used(); - solana_program::log::sol_log_data(&[ + log_data(&[ b"GAS", &used_gas.to_le_bytes(), &total_used_gas.to_le_bytes(), diff --git a/evm_loader/program/src/instruction/transaction_execute.rs b/evm_loader/program/src/instruction/transaction_execute.rs index a55800d5b..9bac6d615 100644 --- a/evm_loader/program/src/instruction/transaction_execute.rs +++ b/evm_loader/program/src/instruction/transaction_execute.rs @@ -2,6 +2,7 @@ use solana_program::pubkey::Pubkey; use crate::account::{AccountsDB, AllocateResult}; use crate::account_storage::ProgramAccountStorage; +use crate::debug::log_data; use crate::error::{Error, Result}; use crate::evm::tracing::NoopEventListener; use crate::evm::Machine; @@ -61,7 +62,7 @@ pub fn execute( return Err(Error::OutOfGas(gas_limit, used_gas)); } - solana_program::log::sol_log_data(&[b"GAS", &used_gas.to_le_bytes(), &used_gas.to_le_bytes()]); + log_data(&[b"GAS", &used_gas.to_le_bytes(), &used_gas.to_le_bytes()]); let gas_cost = used_gas.saturating_mul(gas_price); account_storage.transfer_gas_payment(origin, chain_id, gas_cost)?; diff --git a/evm_loader/program/src/instruction/transaction_execute_from_account.rs b/evm_loader/program/src/instruction/transaction_execute_from_account.rs index 5b28216f6..fbea0adfe 100644 --- a/evm_loader/program/src/instruction/transaction_execute_from_account.rs +++ b/evm_loader/program/src/instruction/transaction_execute_from_account.rs @@ -1,4 +1,5 @@ use crate::account::{program, AccountsDB, BalanceAccount, Holder, Operator, Treasury}; +use crate::debug::log_data; use crate::error::Result; use crate::gasometer::Gasometer; use crate::types::Transaction; @@ -12,7 +13,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Execute Transaction from Account"); + log_msg!("Instruction: Execute Transaction from Account"); let treasury_index = u32::from_le_bytes(*array_ref![instruction, 0, 4]); @@ -29,8 +30,8 @@ pub fn process<'a>( let origin = trx.recover_caller_address()?; - solana_program::log::sol_log_data(&[b"HASH", &trx.hash()]); - solana_program::log::sol_log_data(&[b"MINER", operator_balance.address().as_bytes()]); + log_data(&[b"HASH", &trx.hash()]); + log_data(&[b"MINER", operator_balance.address().as_bytes()]); let accounts_db = AccountsDB::new( &accounts[5..], diff --git a/evm_loader/program/src/instruction/transaction_execute_from_instruction.rs b/evm_loader/program/src/instruction/transaction_execute_from_instruction.rs index ffe964728..a2cd8117f 100644 --- a/evm_loader/program/src/instruction/transaction_execute_from_instruction.rs +++ b/evm_loader/program/src/instruction/transaction_execute_from_instruction.rs @@ -1,4 +1,5 @@ use crate::account::{program, AccountsDB, BalanceAccount, Operator, Treasury}; +use crate::debug::log_data; use crate::error::Result; use crate::gasometer::Gasometer; use crate::types::Transaction; @@ -12,7 +13,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Execute Transaction from Instruction"); + log_msg!("Instruction: Execute Transaction from Instruction"); let treasury_index = u32::from_le_bytes(*array_ref![instruction, 0, 4]); let messsage = &instruction[4..]; @@ -25,8 +26,8 @@ pub fn process<'a>( let trx = Transaction::from_rlp(messsage)?; let origin = trx.recover_caller_address()?; - solana_program::log::sol_log_data(&[b"HASH", &trx.hash()]); - solana_program::log::sol_log_data(&[b"MINER", operator_balance.address().as_bytes()]); + log_data(&[b"HASH", &trx.hash()]); + log_data(&[b"MINER", operator_balance.address().as_bytes()]); let accounts_db = AccountsDB::new( &accounts[4..], diff --git a/evm_loader/program/src/instruction/transaction_step.rs b/evm_loader/program/src/instruction/transaction_step.rs index f6ba7d642..83dc853d9 100644 --- a/evm_loader/program/src/instruction/transaction_step.rs +++ b/evm_loader/program/src/instruction/transaction_step.rs @@ -1,6 +1,7 @@ use crate::account::{AccountsDB, AllocateResult, StateAccount}; use crate::account_storage::{AccountStorage, ProgramAccountStorage}; use crate::config::{EVM_STEPS_LAST_ITERATION_MAX, EVM_STEPS_MIN}; +use crate::debug::log_data; use crate::error::{Error, Result}; use crate::evm::tracing::NoopEventListener; use crate::evm::{ExitStatus, Machine}; @@ -103,7 +104,7 @@ fn finalize<'a>( let used_gas = gasometer.used_gas(); let total_used_gas = gasometer.used_gas_total(); - solana_program::log::sol_log_data(&[ + log_data(&[ b"GAS", &used_gas.to_le_bytes(), &total_used_gas.to_le_bytes(), @@ -124,8 +125,6 @@ fn finalize<'a>( } pub fn log_return_value(status: &ExitStatus) { - use solana_program::log::sol_log_data; - let code: u8 = match status { ExitStatus::Stop => 0x11, ExitStatus::Return(_) => 0x12, @@ -134,12 +133,12 @@ pub fn log_return_value(status: &ExitStatus) { ExitStatus::StepLimit => unreachable!(), }; - solana_program::msg!("exit_status={:#04X}", code); // Tests compatibility + log_msg!("exit_status={:#04X}", code); // Tests compatibility if let ExitStatus::Revert(msg) = status { crate::error::print_revert_message(msg); } - sol_log_data(&[b"RETURN", &[code]]); + log_data(&[b"RETURN", &[code]]); } fn serialize_evm_state( diff --git a/evm_loader/program/src/instruction/transaction_step_from_account.rs b/evm_loader/program/src/instruction/transaction_step_from_account.rs index 2697a430b..6d7e21aa5 100644 --- a/evm_loader/program/src/instruction/transaction_step_from_account.rs +++ b/evm_loader/program/src/instruction/transaction_step_from_account.rs @@ -3,7 +3,7 @@ use crate::account::{ program, AccountsDB, BalanceAccount, Holder, Operator, StateAccount, Treasury, TAG_HOLDER, TAG_STATE, TAG_STATE_FINALIZED, }; - +use crate::debug::log_data; use crate::error::{Error, Result}; use crate::gasometer::Gasometer; use crate::instruction::transaction_step::{do_begin, do_continue}; @@ -17,7 +17,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Begin or Continue Transaction from Account"); + log_msg!("Instruction: Begin or Continue Transaction from Account"); process_inner(program_id, accounts, instruction, false) } @@ -69,8 +69,8 @@ pub fn process_inner<'a>( trx }; - solana_program::log::sol_log_data(&[b"HASH", &trx.hash]); - solana_program::log::sol_log_data(&[b"MINER", miner_address.as_bytes()]); + log_data(&[b"HASH", &trx.hash]); + log_data(&[b"MINER", miner_address.as_bytes()]); let origin = trx.recover_caller_address()?; @@ -101,8 +101,8 @@ pub fn process_inner<'a>( let storage = StateAccount::restore(program_id, holder_or_storage.clone(), &accounts_db, false)?; - solana_program::log::sol_log_data(&[b"HASH", &storage.trx_hash()]); - solana_program::log::sol_log_data(&[b"MINER", miner_address.as_bytes()]); + log_data(&[b"HASH", &storage.trx_hash()]); + log_data(&[b"MINER", miner_address.as_bytes()]); let mut gasometer = Gasometer::new(storage.gas_used(), accounts_db.operator())?; gasometer.record_solana_transaction_cost(); diff --git a/evm_loader/program/src/instruction/transaction_step_from_account_no_chainid.rs b/evm_loader/program/src/instruction/transaction_step_from_account_no_chainid.rs index 041ac6b42..f911b998a 100644 --- a/evm_loader/program/src/instruction/transaction_step_from_account_no_chainid.rs +++ b/evm_loader/program/src/instruction/transaction_step_from_account_no_chainid.rs @@ -6,7 +6,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Begin or Continue Transaction from Account Without ChainId"); + log_msg!("Instruction: Begin or Continue Transaction from Account Without ChainId"); super::transaction_step_from_account::process_inner(program_id, accounts, instruction, true) } diff --git a/evm_loader/program/src/instruction/transaction_step_from_instruction.rs b/evm_loader/program/src/instruction/transaction_step_from_instruction.rs index 656d8915d..cd41be54a 100644 --- a/evm_loader/program/src/instruction/transaction_step_from_instruction.rs +++ b/evm_loader/program/src/instruction/transaction_step_from_instruction.rs @@ -3,6 +3,7 @@ use crate::account::{ program, AccountsDB, BalanceAccount, Operator, StateAccount, Treasury, TAG_HOLDER, TAG_STATE, TAG_STATE_FINALIZED, }; +use crate::debug::log_data; use crate::error::{Error, Result}; use crate::gasometer::Gasometer; use crate::instruction::transaction_step::{do_begin, do_continue}; @@ -16,7 +17,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Begin or Continue Transaction from Instruction"); + log_msg!("Instruction: Begin or Continue Transaction from Instruction"); let treasury_index = u32::from_le_bytes(*array_ref![instruction, 0, 4]); let step_count = u64::from(u32::from_le_bytes(*array_ref![instruction, 4, 4])); @@ -52,8 +53,8 @@ pub fn process<'a>( let trx = Transaction::from_rlp(message)?; let origin = trx.recover_caller_address()?; - solana_program::log::sol_log_data(&[b"HASH", &trx.hash()]); - solana_program::log::sol_log_data(&[b"MINER", miner_address.as_bytes()]); + log_data(&[b"HASH", &trx.hash()]); + log_data(&[b"MINER", miner_address.as_bytes()]); let mut gasometer = Gasometer::new(U256::ZERO, accounts_db.operator())?; gasometer.record_solana_transaction_cost(); @@ -69,8 +70,8 @@ pub fn process<'a>( TAG_STATE => { let storage = StateAccount::restore(program_id, storage_info, &accounts_db, false)?; - solana_program::log::sol_log_data(&[b"HASH", &storage.trx_hash()]); - solana_program::log::sol_log_data(&[b"MINER", miner_address.as_bytes()]); + log_data(&[b"HASH", &storage.trx_hash()]); + log_data(&[b"MINER", miner_address.as_bytes()]); let mut gasometer = Gasometer::new(storage.gas_used(), accounts_db.operator())?; gasometer.record_solana_transaction_cost(); From 3a179a973ada2c5c2c803f3294bb7e75d768fe63 Mon Sep 17 00:00:00 2001 From: Anton Lisanin Date: Thu, 22 Feb 2024 10:44:24 +0300 Subject: [PATCH 5/8] Add Account Revision (#276) * Add account revision --- evm_loader/cli/src/main.rs | 4 +- evm_loader/lib/src/account_storage.rs | 54 ++- evm_loader/lib/src/account_update.rs | 45 +++ evm_loader/lib/src/commands/cancel_trx.rs | 34 +- evm_loader/lib/src/commands/emulate.rs | 6 +- evm_loader/lib/src/commands/get_balance.rs | 7 +- evm_loader/lib/src/commands/get_config.rs | 12 + evm_loader/lib/src/commands/get_contract.rs | 4 +- evm_loader/lib/src/commands/get_holder.rs | 32 +- evm_loader/lib/src/lib.rs | 1 + evm_loader/program/src/account/holder.rs | 4 +- evm_loader/program/src/account/legacy/mod.rs | 45 ++- .../program/src/account/legacy/update.rs | 34 ++ evm_loader/program/src/account/mod.rs | 53 +-- evm_loader/program/src/account/state.rs | 356 +++++++----------- .../program/src/account/state_finalized.rs | 25 +- .../program/src/account_storage/apply.rs | 46 ++- .../program/src/account_storage/base.rs | 14 +- evm_loader/program/src/entrypoint.rs | 3 - evm_loader/program/src/evm/mod.rs | 32 +- evm_loader/program/src/executor/action.rs | 109 +----- evm_loader/program/src/executor/state.rs | 3 +- .../src/instruction/account_block_add.rs | 50 --- evm_loader/program/src/instruction/mod.rs | 5 - .../src/instruction/transaction_cancel.rs | 15 +- .../src/instruction/transaction_execute.rs | 22 +- .../transaction_execute_from_account.rs | 1 - .../transaction_execute_from_instruction.rs | 1 - .../src/instruction/transaction_step.rs | 57 +-- .../transaction_step_from_account.rs | 31 +- .../transaction_step_from_instruction.rs | 16 +- evm_loader/program/src/types/mod.rs | 1 + evm_loader/program/src/types/serde.rs | 130 +++++++ evm_loader/program/src/types/transaction.rs | 92 ++++- 34 files changed, 735 insertions(+), 609 deletions(-) create mode 100644 evm_loader/lib/src/account_update.rs create mode 100644 evm_loader/program/src/account/legacy/update.rs delete mode 100644 evm_loader/program/src/instruction/account_block_add.rs create mode 100644 evm_loader/program/src/types/serde.rs diff --git a/evm_loader/cli/src/main.rs b/evm_loader/cli/src/main.rs index 36d6b16d7..3f3210849 100644 --- a/evm_loader/cli/src/main.rs +++ b/evm_loader/cli/src/main.rs @@ -90,12 +90,12 @@ async fn run(options: &ArgMatches<'_>) -> NeonCliResult { .map(|result| json!(result)) } ("cancel-trx", Some(params)) => { - let rpc_client = config.build_solana_rpc_client(); + let rpc_client = config.build_clone_solana_rpc_client(); let signer = build_signer(config)?; let storage_account = pubkey_of(params, "storage_account").expect("storage_account parse error"); - cancel_trx::execute(&rpc_client, &*signer, config.evm_loader, &storage_account) + cancel_trx::execute(rpc_client, &*signer, config.evm_loader, &storage_account) .await .map(|result| json!(result)) } diff --git a/evm_loader/lib/src/account_storage.rs b/evm_loader/lib/src/account_storage.rs index eaa6ed55a..9720c6bb1 100644 --- a/evm_loader/lib/src/account_storage.rs +++ b/evm_loader/lib/src/account_storage.rs @@ -1,9 +1,10 @@ use async_trait::async_trait; use evm_loader::account::legacy::{ - LegacyEtherData, LegacyStorageData, TAG_ACCOUNT_CONTRACT_DEPRECATED, - TAG_STORAGE_CELL_DEPRECATED, + LegacyEtherData, LegacyStorageData, ACCOUNT_PREFIX_LEN_BEFORE_REVISION, + TAG_ACCOUNT_BALANCE_BEFORE_REVISION, TAG_ACCOUNT_CONTRACT_BEFORE_REVISION, + TAG_ACCOUNT_CONTRACT_DEPRECATED, TAG_STORAGE_CELL_BEFORE_REVISION, TAG_STORAGE_CELL_DEPRECATED, }; -use evm_loader::account::{TAG_ACCOUNT_CONTRACT, TAG_STORAGE_CELL}; +use evm_loader::account::{ACCOUNT_PREFIX_LEN, TAG_ACCOUNT_CONTRACT, TAG_STORAGE_CELL}; use evm_loader::account_storage::find_slot_hash; use evm_loader::types::Address; use solana_sdk::rent::Rent; @@ -12,6 +13,8 @@ use solana_sdk::sysvar::slot_hashes; use std::collections::HashSet; use std::{cell::RefCell, collections::HashMap, convert::TryInto, rc::Rc}; +use crate::account_update::update_account; +use crate::NeonResult; use crate::{rpc::Rpc, NeonError}; use ethnum::U256; use evm_loader::{ @@ -172,9 +175,13 @@ impl EmulatorAccountStorage<'_, T> { address: Address, chain_id: u64, is_writable: bool, - ) -> client_error::Result<(Pubkey, Option, Option)> { + ) -> NeonResult<(Pubkey, Option, Option)> { let (pubkey, _) = address.find_balance_address(self.program_id(), chain_id); - let account = self.use_account(pubkey, is_writable).await?; + let mut account = self.use_account(pubkey, is_writable).await?; + + if let Some(account) = &mut account { + update_account(&self.program_id, &pubkey, account)?; + } let legacy_account = if account.is_none() && (chain_id == self.default_chain_id()) { let (legacy_pubkey, _) = address.find_solana_address(self.program_id()); @@ -190,9 +197,13 @@ impl EmulatorAccountStorage<'_, T> { &self, address: Address, is_writable: bool, - ) -> client_error::Result<(Pubkey, Option)> { + ) -> NeonResult<(Pubkey, Option)> { let (pubkey, _) = address.find_solana_address(self.program_id()); - let account = self.use_account(pubkey, is_writable).await?; + let mut account = self.use_account(pubkey, is_writable).await?; + + if let Some(account) = &mut account { + update_account(&self.program_id, &pubkey, account)?; + } Ok((pubkey, account)) } @@ -202,14 +213,18 @@ impl EmulatorAccountStorage<'_, T> { address: Address, index: U256, is_writable: bool, - ) -> client_error::Result<(Pubkey, Option)> { + ) -> NeonResult<(Pubkey, Option)> { let (base, _) = address.find_solana_address(self.program_id()); let cell_address = StorageCellAddress::new(self.program_id(), &base, &index); - let account = self + let mut account = self .use_account(*cell_address.pubkey(), is_writable) .await?; + if let Some(account) = &mut account { + update_account(&self.program_id, cell_address.pubkey(), account)?; + } + Ok((*cell_address.pubkey(), account)) } @@ -368,6 +383,27 @@ impl EmulatorAccountStorage<'_, T> { self.gas = self.gas.saturating_add(lamports); } } + + if matches!( + tag, + TAG_ACCOUNT_BALANCE_BEFORE_REVISION + | TAG_ACCOUNT_CONTRACT_BEFORE_REVISION + | TAG_STORAGE_CELL_BEFORE_REVISION + ) { + const PREFIX_BEFORE: usize = ACCOUNT_PREFIX_LEN_BEFORE_REVISION; + const PREFIX_AFTER: usize = ACCOUNT_PREFIX_LEN; + const EXPANSION_LEN: usize = PREFIX_AFTER - PREFIX_BEFORE; + + account.is_writable = true; + account.is_legacy = true; + + let lamports = self + .rent + .minimum_balance(EXPANSION_LEN) + .saturating_sub(self.rent.minimum_balance(0)); + + self.gas = self.gas.saturating_add(lamports); + } } for a in additional_balances { diff --git a/evm_loader/lib/src/account_update.rs b/evm_loader/lib/src/account_update.rs new file mode 100644 index 000000000..9927fb020 --- /dev/null +++ b/evm_loader/lib/src/account_update.rs @@ -0,0 +1,45 @@ +use evm_loader::account::{ + legacy::{ + ACCOUNT_PREFIX_LEN_BEFORE_REVISION, TAG_ACCOUNT_BALANCE_BEFORE_REVISION, + TAG_ACCOUNT_CONTRACT_BEFORE_REVISION, TAG_STORAGE_CELL_BEFORE_REVISION, + }, + ACCOUNT_PREFIX_LEN, TAG_ACCOUNT_BALANCE, TAG_ACCOUNT_CONTRACT, TAG_STORAGE_CELL, +}; +use solana_sdk::{account::Account, pubkey::Pubkey}; + +use crate::{account_storage::account_info, NeonResult}; + +fn from_before_revision(account: &mut Account, new_tag: u8) { + const PREFIX_BEFORE: usize = ACCOUNT_PREFIX_LEN_BEFORE_REVISION; + const PREFIX_AFTER: usize = ACCOUNT_PREFIX_LEN; + + assert!(account.data.len() > PREFIX_BEFORE); + + let required_len = account.data.len() + PREFIX_AFTER - PREFIX_BEFORE; + account.data.resize(required_len, 0); + + account.data.copy_within(PREFIX_BEFORE.., PREFIX_AFTER); + account.data[0] = new_tag; +} + +pub fn update_account(program_id: &Pubkey, key: &Pubkey, account: &mut Account) -> NeonResult<()> { + let tag = { + let info = account_info(key, account); + evm_loader::account::tag(program_id, &info)? + }; + + match tag { + TAG_ACCOUNT_BALANCE_BEFORE_REVISION => { + from_before_revision(account, TAG_ACCOUNT_BALANCE); + } + TAG_ACCOUNT_CONTRACT_BEFORE_REVISION => { + from_before_revision(account, TAG_ACCOUNT_CONTRACT); + } + TAG_STORAGE_CELL_BEFORE_REVISION => { + from_before_revision(account, TAG_STORAGE_CELL); + } + _ => {} + } + + Ok(()) +} diff --git a/evm_loader/lib/src/commands/cancel_trx.rs b/evm_loader/lib/src/commands/cancel_trx.rs index 22e288c41..f68ee6441 100644 --- a/evm_loader/lib/src/commands/cancel_trx.rs +++ b/evm_loader/lib/src/commands/cancel_trx.rs @@ -2,7 +2,6 @@ use evm_loader::account::StateAccount; use log::info; use serde::{Deserialize, Serialize}; -use solana_client::nonblocking::rpc_client::RpcClient; use solana_sdk::{ instruction::{AccountMeta, Instruction}, pubkey::Pubkey, @@ -10,7 +9,9 @@ use solana_sdk::{ signer::Signer, }; -use crate::{account_storage::account_info, commands::send_transaction, NeonResult}; +use crate::{ + account_storage::account_info, commands::send_transaction, rpc::CloneRpcClient, NeonResult, +}; #[derive(Debug, Serialize, Deserialize)] pub struct CancelTrxReturn { @@ -18,20 +19,23 @@ pub struct CancelTrxReturn { } pub async fn execute( - rpc_client: &RpcClient, + rpc_client: CloneRpcClient, signer: &dyn Signer, - evm_loader: Pubkey, + program_id: Pubkey, storage_account: &Pubkey, ) -> NeonResult { let mut acc = rpc_client.get_account(storage_account).await?; let storage_info = account_info(storage_account, &mut acc); - let storage = StateAccount::from_account(&evm_loader, storage_info)?; + let storage = StateAccount::from_account(&program_id, storage_info)?; let operator = &signer.pubkey(); + let default_chain_id = + crate::commands::get_config::read_default_chain_id(&rpc_client, program_id).await?; + let chain_id = storage.trx().chain_id().unwrap_or(default_chain_id); + let origin = storage.trx_origin(); - let chain_id: u64 = storage.trx_chain_id(); - let (origin_pubkey, _) = origin.find_balance_address(&evm_loader, chain_id); + let (origin_pubkey, _) = origin.find_balance_address(&program_id, chain_id); let mut accounts_meta: Vec = vec![ AccountMeta::new(*storage_account, false), // State account @@ -39,23 +43,19 @@ pub async fn execute( AccountMeta::new(origin_pubkey, false), ]; - for blocked_account_meta in storage.blocked_accounts().iter() { - if blocked_account_meta.is_writable { - accounts_meta.push(AccountMeta::new(blocked_account_meta.key, false)); - } else { - accounts_meta.push(AccountMeta::new_readonly(blocked_account_meta.key, false)); - } - } - for meta in &accounts_meta { + for key in storage.accounts() { + let meta = AccountMeta::new(*key, true); info!("\t{:?}", meta); + + accounts_meta.push(meta); } let cancel_with_nonce_instruction = - Instruction::new_with_bincode(evm_loader, &(0x37_u8, storage.trx_hash()), accounts_meta); + Instruction::new_with_bincode(program_id, &(0x37_u8, storage.trx().hash()), accounts_meta); let instructions = vec![cancel_with_nonce_instruction]; - let signature = send_transaction(rpc_client, signer, &instructions).await?; + let signature = send_transaction(&rpc_client, signer, &instructions).await?; Ok(CancelTrxReturn { transaction: signature, diff --git a/evm_loader/lib/src/commands/emulate.rs b/evm_loader/lib/src/commands/emulate.rs index 05b76c922..6501b0c82 100644 --- a/evm_loader/lib/src/commands/emulate.rs +++ b/evm_loader/lib/src/commands/emulate.rs @@ -1,4 +1,5 @@ use evm_loader::account::ContractAccount; +use evm_loader::account_storage::AccountStorage; use evm_loader::error::build_revert_message; use log::{debug, info}; use serde::{Deserialize, Serialize}; @@ -93,8 +94,11 @@ async fn emulate_trx( info!("origin: {:?}", origin); info!("tx: {:?}", tx); + let chain_id = tx.chain_id().unwrap_or_else(|| storage.default_chain_id()); + storage.use_balance_account(origin, chain_id, true).await?; + let mut backend = ExecutorState::new(storage); - let mut evm = match Machine::new(tx, origin, &mut backend, tracer).await { + let mut evm = match Machine::new(&tx, origin, &mut backend, tracer).await { Ok(evm) => evm, Err(e) => return Ok((EmulateResponse::revert(e), None)), }; diff --git a/evm_loader/lib/src/commands/get_balance.rs b/evm_loader/lib/src/commands/get_balance.rs index e9d6561a9..4879dd1c9 100644 --- a/evm_loader/lib/src/commands/get_balance.rs +++ b/evm_loader/lib/src/commands/get_balance.rs @@ -4,7 +4,10 @@ use evm_loader::account::BalanceAccount; use serde::{Deserialize, Serialize}; use solana_sdk::{account::Account, pubkey::Pubkey}; -use crate::{account_storage::account_info, rpc::Rpc, types::BalanceAddress, NeonResult}; +use crate::{ + account_storage::account_info, account_update::update_account, rpc::Rpc, types::BalanceAddress, + NeonResult, +}; use serde_with::{serde_as, DisplayFromStr}; @@ -48,6 +51,8 @@ fn read_account( ) -> NeonResult { let solana_address = address.find_pubkey(program_id); + update_account(program_id, &solana_address, &mut account)?; + let account_info = account_info(&solana_address, &mut account); let balance_account = BalanceAccount::from_account(program_id, account_info)?; diff --git a/evm_loader/lib/src/commands/get_config.rs b/evm_loader/lib/src/commands/get_config.rs index 84f2b8b17..3934ce22b 100644 --- a/evm_loader/lib/src/commands/get_config.rs +++ b/evm_loader/lib/src/commands/get_config.rs @@ -357,6 +357,18 @@ pub async fn read_chains( simulator.get_chains().await } +pub async fn read_default_chain_id( + rpc: &impl BuildConfigSimulator, + program_id: Pubkey, +) -> NeonResult { + let mut simulator = rpc.build_config_simulator(program_id).await?; + + let chains = simulator.get_chains().await?; + let default_chain = chains.iter().find(|chain| chain.name == "neon").unwrap(); + + Ok(default_chain.id) +} + impl CloneRpcClient { async fn get_account_with_sol(&self) -> ClientResult { let r = self diff --git a/evm_loader/lib/src/commands/get_contract.rs b/evm_loader/lib/src/commands/get_contract.rs index 6f1a3d324..938a1a4c6 100644 --- a/evm_loader/lib/src/commands/get_contract.rs +++ b/evm_loader/lib/src/commands/get_contract.rs @@ -5,7 +5,7 @@ use evm_loader::{ use serde::{Deserialize, Serialize}; use solana_sdk::{account::Account, pubkey::Pubkey}; -use crate::{account_storage::account_info, rpc::Rpc, NeonResult}; +use crate::{account_storage::account_info, account_update::update_account, rpc::Rpc, NeonResult}; use serde_with::{hex::Hex, serde_as, DisplayFromStr}; @@ -51,6 +51,8 @@ fn read_account( return Ok(GetContractResponse::empty(solana_address)); }; + update_account(program_id, &solana_address, &mut account)?; + let account_info = account_info(&solana_address, &mut account); let (chain_id, code) = if let Ok(contract) = ContractAccount::from_account(program_id, account_info.clone()) { diff --git a/evm_loader/lib/src/commands/get_holder.rs b/evm_loader/lib/src/commands/get_holder.rs index 1ad6aff13..aa1cddb25 100644 --- a/evm_loader/lib/src/commands/get_holder.rs +++ b/evm_loader/lib/src/commands/get_holder.rs @@ -1,4 +1,3 @@ -use ethnum::U256; use evm_loader::account::{ legacy::{ LegacyFinalizedData, LegacyHolderData, TAG_HOLDER_DEPRECATED, @@ -24,14 +23,6 @@ pub enum Status { Finalized, } -#[serde_as] -#[derive(Debug, Default, Serialize)] -pub struct AccountMeta { - pub is_writable: bool, - #[serde_as(as = "DisplayFromStr")] - pub key: Pubkey, -} - #[serde_as] #[skip_serializing_none] #[derive(Debug, Default, Serialize)] @@ -45,11 +36,8 @@ pub struct GetHolderResponse { pub tx: Option<[u8; 32]>, pub chain_id: Option, - pub gas_price: Option, - pub gas_limit: Option, - pub gas_used: Option, - - pub accounts: Option>, + #[serde_as(as = "Option>")] + pub accounts: Option>, } impl GetHolderResponse { @@ -114,24 +102,14 @@ pub fn read_holder(program_id: &Pubkey, info: AccountInfo) -> NeonResult { let state = StateAccount::from_account(program_id, info)?; - let accounts = state - .blocked_accounts() - .iter() - .map(|a| AccountMeta { - is_writable: a.is_writable, - key: a.key, - }) - .collect(); + let accounts = state.accounts().copied().collect(); Ok(GetHolderResponse { status: Status::Active, len: Some(data_len), owner: Some(state.owner()), - tx: Some(state.trx_hash()), - chain_id: Some(state.trx_chain_id()), - gas_limit: Some(state.trx_gas_limit()), - gas_price: Some(state.trx_gas_price()), - gas_used: Some(state.gas_used()), + tx: Some(state.trx().hash()), + chain_id: state.trx().chain_id(), accounts: Some(accounts), }) } diff --git a/evm_loader/lib/src/lib.rs b/evm_loader/lib/src/lib.rs index d50c2f17b..512257992 100644 --- a/evm_loader/lib/src/lib.rs +++ b/evm_loader/lib/src/lib.rs @@ -1,4 +1,5 @@ pub mod account_storage; +pub mod account_update; pub mod build_info; pub mod build_info_common; pub mod commands; diff --git a/evm_loader/program/src/account/holder.rs b/evm_loader/program/src/account/holder.rs index cb12fc852..2d015555d 100644 --- a/evm_loader/program/src/account/holder.rs +++ b/evm_loader/program/src/account/holder.rs @@ -7,7 +7,7 @@ use crate::account::TAG_STATE_FINALIZED; use crate::error::{Error, Result}; use crate::types::Transaction; -use super::{Operator, ACCOUNT_PREFIX_LEN, TAG_EMPTY, TAG_HOLDER}; +use super::{Operator, HOLDER_PREFIX_LEN, TAG_EMPTY, TAG_HOLDER}; /// Ethereum holder data account #[repr(C, packed)] @@ -21,7 +21,7 @@ pub struct Holder<'a> { account: AccountInfo<'a>, } -const HEADER_OFFSET: usize = ACCOUNT_PREFIX_LEN; +const HEADER_OFFSET: usize = HOLDER_PREFIX_LEN; const BUFFER_OFFSET: usize = HEADER_OFFSET + size_of::
(); impl<'a> Holder<'a> { diff --git a/evm_loader/program/src/account/legacy/mod.rs b/evm_loader/program/src/account/legacy/mod.rs index 414ad4e65..2a92e275c 100644 --- a/evm_loader/program/src/account/legacy/mod.rs +++ b/evm_loader/program/src/account/legacy/mod.rs @@ -1,6 +1,7 @@ mod legacy_ether; mod legacy_holder; mod legacy_storage_cell; +mod update; pub use legacy_ether::LegacyEtherData; pub use legacy_holder::LegacyFinalizedData; @@ -12,6 +13,8 @@ use solana_program::{account_info::AccountInfo, rent::Rent, sysvar::Sysvar}; use super::Holder; use super::StateFinalizedAccount; +use super::TAG_ACCOUNT_BALANCE; +use super::TAG_ACCOUNT_CONTRACT; use super::TAG_HOLDER; use super::TAG_STATE_FINALIZED; use super::{AccountsDB, ContractAccount, TAG_STORAGE_CELL}; @@ -21,11 +24,19 @@ use crate::{ error::Result, }; +// First version pub const TAG_STATE_DEPRECATED: u8 = 22; pub const TAG_STATE_FINALIZED_DEPRECATED: u8 = 31; pub const TAG_HOLDER_DEPRECATED: u8 = 51; pub const TAG_ACCOUNT_CONTRACT_DEPRECATED: u8 = 12; pub const TAG_STORAGE_CELL_DEPRECATED: u8 = 42; +// Before account revision (Holder and Finalized remain the same) +pub const TAG_STATE_BEFORE_REVISION: u8 = 23; +pub const TAG_ACCOUNT_BALANCE_BEFORE_REVISION: u8 = 60; +pub const TAG_ACCOUNT_CONTRACT_BEFORE_REVISION: u8 = 70; +pub const TAG_STORAGE_CELL_BEFORE_REVISION: u8 = 43; + +pub const ACCOUNT_PREFIX_LEN_BEFORE_REVISION: usize = 2; fn reduce_account_size(account: &AccountInfo, required_len: usize, rent: &Rent) -> Result { assert!(account.data_len() > required_len); @@ -54,7 +65,7 @@ fn kill_account(account: &AccountInfo) -> Result { Ok(value) } -fn update_ether_account( +fn update_ether_account_from_v1( legacy_data: &LegacyEtherData, db: &AccountsDB, keys: &KeysCache, @@ -89,8 +100,6 @@ fn update_ether_account( Some(keys), )?; contract.set_storage_multiple_values(0, &storage); - - super::set_block(&crate::ID, account, legacy_data.rw_blocked)?; } else { // This is not contract. Just kill it. lamports_collected += kill_account(account)?; @@ -107,14 +116,12 @@ fn update_ether_account( )?; balance.mint(legacy_data.balance)?; balance.increment_nonce_by(legacy_data.trx_count)?; - - super::set_block(&crate::ID, db.get(balance.pubkey()), legacy_data.rw_blocked)?; } Ok(lamports_collected) } -fn update_storage_account( +fn update_storage_account_from_v1( legacy_data: &LegacyStorageData, db: &AccountsDB, keys: &KeysCache, @@ -157,7 +164,6 @@ pub fn update_holder_account(account: &AccountInfo) -> Result { let legacy_data = LegacyHolderData::from_account(&crate::ID, account)?; super::set_tag(&crate::ID, account, TAG_HOLDER)?; - super::set_block(&crate::ID, account, false)?; let mut holder = Holder::from_account(&crate::ID, account.clone())?; holder.update(|data| { @@ -172,7 +178,6 @@ pub fn update_holder_account(account: &AccountInfo) -> Result { let legacy_data = LegacyFinalizedData::from_account(&crate::ID, account)?; super::set_tag(&crate::ID, account, TAG_STATE_FINALIZED)?; - super::set_block(&crate::ID, account, false)?; let mut state = StateFinalizedAccount::from_account(&crate::ID, account.clone())?; state.update(|data| { @@ -189,11 +194,13 @@ pub fn update_holder_account(account: &AccountInfo) -> Result { pub fn update_legacy_accounts(accounts: &AccountsDB) -> Result { let keys = KeysCache::new(); - let rent = Rent::get()?; - let mut lamports_collected = 0_u64; let mut legacy_storage = Vec::with_capacity(accounts.accounts_len()); + let rent = Rent::get()?; + let op = accounts.operator(); + let system = accounts.system(); + for account in accounts { if !crate::check_id(account.owner) { continue; @@ -207,18 +214,28 @@ pub fn update_legacy_accounts(accounts: &AccountsDB) -> Result { match tag { LegacyEtherData::TAG => { let legacy_data = LegacyEtherData::from_account(&crate::ID, account)?; - lamports_collected += update_ether_account(&legacy_data, accounts, &keys, &rent)?; + lamports_collected += + update_ether_account_from_v1(&legacy_data, accounts, &keys, &rent)?; } LegacyStorageData::TAG => { let legacy_data = LegacyStorageData::from_account(&crate::ID, account)?; legacy_storage.push(legacy_data); } + TAG_ACCOUNT_BALANCE_BEFORE_REVISION => { + update::from_before_revision(account, TAG_ACCOUNT_BALANCE, op, system, &rent)?; + } + TAG_ACCOUNT_CONTRACT_BEFORE_REVISION => { + update::from_before_revision(account, TAG_ACCOUNT_CONTRACT, op, system, &rent)?; + } + TAG_STORAGE_CELL_BEFORE_REVISION => { + update::from_before_revision(account, TAG_STORAGE_CELL, op, system, &rent)?; + } _ => {} } } for data in legacy_storage { - lamports_collected += update_storage_account(&data, accounts, &keys, &rent)?; + lamports_collected += update_storage_account_from_v1(&data, accounts, &keys, &rent)?; } Ok(lamports_collected) @@ -233,5 +250,9 @@ pub fn is_legacy_tag(tag: u8) -> bool { | TAG_HOLDER_DEPRECATED | TAG_STATE_FINALIZED_DEPRECATED | TAG_STATE_DEPRECATED + | TAG_STATE_BEFORE_REVISION + | TAG_ACCOUNT_BALANCE_BEFORE_REVISION + | TAG_ACCOUNT_CONTRACT_BEFORE_REVISION + | TAG_STORAGE_CELL_BEFORE_REVISION ) } diff --git a/evm_loader/program/src/account/legacy/update.rs b/evm_loader/program/src/account/legacy/update.rs new file mode 100644 index 000000000..19670df64 --- /dev/null +++ b/evm_loader/program/src/account/legacy/update.rs @@ -0,0 +1,34 @@ +use crate::account::legacy::ACCOUNT_PREFIX_LEN_BEFORE_REVISION; +use solana_program::account_info::AccountInfo; +use solana_program::rent::Rent; + +use crate::account::program::System; +use crate::account::Operator; +use crate::account::ACCOUNT_PREFIX_LEN; +use crate::error::Result; + +pub fn from_before_revision<'a>( + account: &AccountInfo<'a>, + new_tag: u8, + operator: &Operator<'a>, + system: &System<'a>, + rent: &Rent, +) -> Result<()> { + const PREFIX_BEFORE: usize = ACCOUNT_PREFIX_LEN_BEFORE_REVISION; + const PREFIX_AFTER: usize = ACCOUNT_PREFIX_LEN; + + let required_len = account.data_len() + PREFIX_AFTER - PREFIX_BEFORE; + account.realloc(required_len, false)?; + + let minimum_balance = rent.minimum_balance(account.data_len()); + if account.lamports() < minimum_balance { + let required_lamports = minimum_balance - account.lamports(); + system.transfer(operator, account, required_lamports)?; + } + + let mut account_data = account.try_borrow_mut_data()?; + account_data[0] = new_tag; + account_data.copy_within(PREFIX_BEFORE.., PREFIX_AFTER); + + Ok(()) +} diff --git a/evm_loader/program/src/account/mod.rs b/evm_loader/program/src/account/mod.rs index 8ba611a8c..0be5568cd 100644 --- a/evm_loader/program/src/account/mod.rs +++ b/evm_loader/program/src/account/mod.rs @@ -6,13 +6,13 @@ use std::cell::{Ref, RefMut}; pub use crate::config::ACCOUNT_SEED_VERSION; -pub use ether_balance::BalanceAccount; -pub use ether_contract::{AllocateResult, ContractAccount}; +pub use ether_balance::{BalanceAccount, Header as BalanceHeader}; +pub use ether_contract::{AllocateResult, ContractAccount, Header as ContractHeader}; pub use ether_storage::{StorageCell, StorageCellAddress}; pub use holder::Holder; pub use incinerator::Incinerator; pub use operator::Operator; -pub use state::StateAccount; +pub use state::{AccountsStatus, StateAccount}; pub use state_finalized::StateFinalizedAccount; pub use treasury::{MainTreasury, Treasury}; @@ -32,15 +32,16 @@ pub mod token; mod treasury; pub const TAG_EMPTY: u8 = 0; -pub const TAG_STATE: u8 = 23; +pub const TAG_STATE: u8 = 24; pub const TAG_STATE_FINALIZED: u8 = 32; pub const TAG_HOLDER: u8 = 52; -pub const TAG_ACCOUNT_BALANCE: u8 = 60; -pub const TAG_ACCOUNT_CONTRACT: u8 = 70; -pub const TAG_STORAGE_CELL: u8 = 43; +pub const TAG_ACCOUNT_BALANCE: u8 = 61; +pub const TAG_ACCOUNT_CONTRACT: u8 = 71; +pub const TAG_STORAGE_CELL: u8 = 44; -const ACCOUNT_PREFIX_LEN: usize = 2; +pub const ACCOUNT_PREFIX_LEN: usize = 1/*tag*/ + 4/*revision*/; +pub const HOLDER_PREFIX_LEN: usize = 1/*tag*/ + 1/*reserved*/; #[inline] fn section<'r, T>(account: &'r AccountInfo<'_>, offset: usize) -> Ref<'r, T> { @@ -106,7 +107,7 @@ pub fn validate_tag(program_id: &Pubkey, info: &AccountInfo, tag: u8) -> Result< } } -pub fn is_blocked(program_id: &Pubkey, info: &AccountInfo) -> Result { +pub fn revision(program_id: &Pubkey, info: &AccountInfo) -> Result { if info.owner != program_id { return Err(Error::AccountInvalidOwner(*info.key, *program_id)); } @@ -116,43 +117,27 @@ pub fn is_blocked(program_id: &Pubkey, info: &AccountInfo) -> Result { return Err(Error::AccountInvalidData(*info.key)); } - if legacy::is_legacy_tag(data[0]) { - return Err(Error::AccountLegacy(*info.key)); - } - - Ok(data[1] == 1) + let buffer = arrayref::array_ref![data, 1, 4]; + Ok(u32::from_le_bytes(*buffer)) } -#[inline] -fn set_block(program_id: &Pubkey, info: &AccountInfo, block: bool) -> Result<()> { - assert_eq!(info.owner, program_id); +pub fn increment_revision(program_id: &Pubkey, info: &AccountInfo) -> Result<()> { + if info.owner != program_id { + return Err(Error::AccountInvalidOwner(*info.key, *program_id)); + } let mut data = info.try_borrow_mut_data()?; if data.len() < ACCOUNT_PREFIX_LEN { return Err(Error::AccountInvalidData(*info.key)); } - if legacy::is_legacy_tag(data[0]) { - return Err(Error::AccountLegacy(*info.key)); - } - - if block && (data[1] != 0) { - return Err(Error::AccountBlocked(*info.key)); - } - - data[1] = block.into(); + let buffer = arrayref::array_mut_ref![data, 1, 4]; + let revision = u32::from_le_bytes(*buffer); + *buffer = (revision + 1).to_le_bytes(); Ok(()) } -pub fn block(program_id: &Pubkey, info: &AccountInfo) -> Result<()> { - set_block(program_id, info, true) -} - -pub fn unblock(program_id: &Pubkey, info: &AccountInfo) -> Result<()> { - set_block(program_id, info, false) -} - /// # Safety /// *Permanently delete all data* in the account. Transfer lamports to the operator. pub unsafe fn delete(account: &AccountInfo, operator: &Operator) { diff --git a/evm_loader/program/src/account/state.rs b/evm_loader/program/src/account/state.rs index 53d22e382..f4e7b147f 100644 --- a/evm_loader/program/src/account/state.rs +++ b/evm_loader/program/src/account/state.rs @@ -1,66 +1,72 @@ use std::cell::{Ref, RefMut}; +use std::collections::BTreeMap; use std::mem::size_of; -use crate::config::GAS_LIMIT_MULTIPLIER_NO_CHAINID; +use crate::account_storage::AccountStorage; +use crate::config::DEFAULT_CHAIN_ID; use crate::error::{Error, Result}; use crate::types::{Address, Transaction}; use ethnum::U256; -use solana_program::clock::Clock; -use solana_program::program_error::ProgramError; -use solana_program::sysvar::Sysvar; +use serde::{Deserialize, Serialize}; use solana_program::{account_info::AccountInfo, pubkey::Pubkey}; use super::{ - AccountsDB, BalanceAccount, Operator, ACCOUNT_PREFIX_LEN, TAG_EMPTY, TAG_HOLDER, TAG_STATE, - TAG_STATE_FINALIZED, + revision, AccountsDB, BalanceAccount, Holder, StateFinalizedAccount, HOLDER_PREFIX_LEN, + TAG_HOLDER, TAG_STATE, TAG_STATE_FINALIZED, }; +#[derive(PartialEq, Eq)] +pub enum AccountsStatus { + Ok, + RevisionChanged, +} + /// Storage data account to store execution metainfo between steps for iterative execution -#[repr(C, packed)] -pub struct Header { +#[derive(Serialize, Deserialize)] +struct Data { pub owner: Pubkey, - pub transaction_hash: [u8; 32], + pub transaction: Transaction, /// Ethereum transaction caller address pub origin: Address, - /// Ethereum transaction chain_id - pub chain_id: u64, - /// Ethereum transaction gas limit - pub gas_limit: U256, - /// Ethereum transaction gas price - pub gas_price: U256, + /// Stored accounts + pub revisions: BTreeMap, /// Ethereum transaction gas used and paid + #[serde(with = "ethnum::serde::bytes::le")] pub gas_used: U256, - /// Operator public key - pub operator: Pubkey, - /// Starting slot for this operator - pub slot: u64, - /// Stored accounts length - pub accounts_len: usize, - /// Stored EVM State length - pub evm_state_len: usize, - /// Stored EVM Machine length - pub evm_machine_len: usize, } #[repr(C, packed)] -pub struct BlockedAccount { - pub is_writable: bool, - pub blocked: bool, - pub key: Pubkey, +struct Header { + pub evm_state_len: usize, + pub evm_machine_len: usize, + pub data_len: usize, } pub struct StateAccount<'a> { account: AccountInfo<'a>, + data: Data, } -const HEADER_OFFSET: usize = ACCOUNT_PREFIX_LEN; -const BLOCKED_ACCOUNTS_OFFSET: usize = HEADER_OFFSET + size_of::
(); +const HEADER_OFFSET: usize = HOLDER_PREFIX_LEN; +const BUFFER_OFFSET: usize = HEADER_OFFSET + size_of::
(); impl<'a> StateAccount<'a> { pub fn from_account(program_id: &Pubkey, account: AccountInfo<'a>) -> Result { super::validate_tag(program_id, &account, TAG_STATE)?; - Ok(Self { account }) + let (offset, len) = { + let header = super::section::
(&account, HEADER_OFFSET); + let offset = BUFFER_OFFSET + header.evm_state_len + header.evm_machine_len; + (offset, header.data_len) + }; + + let data = { + let account_data = account.try_borrow_data()?; + let buffer = &account_data[offset..(offset + len)]; + bincode::deserialize(buffer)? + }; + + Ok(Self { account, data }) } pub fn new( @@ -68,108 +74,101 @@ impl<'a> StateAccount<'a> { info: AccountInfo<'a>, accounts: &AccountsDB<'a>, origin: Address, - trx: &Transaction, + transaction: Transaction, ) -> Result { - let tag = super::tag(program_id, &info)?; - if matches!(tag, TAG_HOLDER | TAG_STATE_FINALIZED) { - super::set_tag(program_id, &info, TAG_STATE)?; - } - - let mut state = Self::from_account(program_id, info)?; - state.validate_owner(accounts.operator())?; - - if (tag == TAG_STATE_FINALIZED) && (state.trx_hash() == trx.hash) { - return Err(Error::StorageAccountFinalized); - } - - // Set header + let owner = match super::tag(program_id, &info)? { + TAG_HOLDER => { + let holder = Holder::from_account(program_id, info.clone())?; + holder.validate_owner(accounts.operator())?; + holder.owner() + } + TAG_STATE_FINALIZED => { + let finalized = StateFinalizedAccount::from_account(program_id, info.clone())?; + finalized.validate_owner(accounts.operator())?; + finalized.validate_trx(&transaction)?; + finalized.owner() + } + tag => return Err(Error::AccountInvalidTag(*info.key, tag)), + }; + + // todo: get revision from account + let revisions = accounts + .into_iter() + .map(|account| { + let revision = revision(program_id, account).unwrap_or(0); + (*account.key, revision) + }) + .collect(); + + let data = Data { + owner, + transaction, + origin, + revisions, + gas_used: U256::ZERO, + }; + + super::set_tag(program_id, &info, TAG_STATE)?; { - let mut header = state.header_mut(); - header.transaction_hash = trx.hash(); - header.origin = origin; - header.chain_id = trx.chain_id().unwrap_or(crate::config::DEFAULT_CHAIN_ID); - header.gas_limit = trx.gas_limit(); - header.gas_price = trx.gas_price(); - header.gas_used = U256::ZERO; - header.operator = accounts.operator_key(); - header.slot = Clock::get()?.slot; - header.accounts_len = accounts.accounts_len(); - header.evm_machine_len = 0; + // Set header + let mut header = super::section_mut::
(&info, HEADER_OFFSET); header.evm_state_len = 0; - } - // Block accounts - for (block, account) in state.blocked_accounts_mut().iter_mut().zip(accounts) { - block.is_writable = account.is_writable; - block.key = *account.key; - if (account.owner == program_id) && !account.data_is_empty() { - super::block(program_id, account)?; - block.blocked = true; - } else { - block.blocked = false; - } + header.evm_machine_len = 0; + header.data_len = 0; } - Ok(state) + Ok(Self { + account: info, + data, + }) } pub fn restore( program_id: &Pubkey, info: AccountInfo<'a>, accounts: &AccountsDB, - is_canceling: bool, - ) -> Result { + ) -> Result<(Self, AccountsStatus)> { let mut state = Self::from_account(program_id, info)?; - - if state.blocked_accounts_len() != accounts.accounts_len() { - return Err(ProgramError::NotEnoughAccountKeys.into()); - } - - // Check blocked accounts - for (block, account) in state.blocked_accounts().iter().zip(accounts) { - if &block.key != account.key { - return Err(Error::AccountInvalidKey(*account.key, block.key)); - } - - if block.is_writable && !account.is_writable { - return Err(Error::AccountNotWritable(*account.key)); - } - - if !is_canceling && (account.owner == program_id) && !block.blocked { - if super::is_blocked(program_id, account)? { - return Err(Error::AccountCreatedByAnotherTransaction(*account.key)); - } - - super::validate_tag(program_id, account, TAG_EMPTY) - .map_err(|_| Error::AccountCreatedByAnotherTransaction(*account.key))?; + let mut status = AccountsStatus::Ok; + + for account in accounts { + let account_revision = revision(program_id, account).unwrap_or(0); + let stored_revision = state + .data + .revisions + .entry(*account.key) + .or_insert(account_revision); + + if stored_revision != &account_revision { + status = AccountsStatus::RevisionChanged; + *stored_revision = account_revision; } } - state.update_current_operator(&accounts.operator); - - Ok(state) + Ok((state, status)) } - pub fn finalize(self, program_id: &Pubkey, accounts: &AccountsDB) -> Result<()> { + pub fn finalize(self, program_id: &Pubkey) -> Result<()> { debug_print!("Finalize Storage {}", self.account.key); - // Unblock accounts - for (block, account) in self.blocked_accounts().iter().zip(accounts) { - if &block.key != account.key { - return Err(Error::AccountInvalidKey(*account.key, block.key)); - } - - if !block.blocked { - continue; - } - - super::unblock(program_id, account)?; - } + let owner = self.owner(); + let trx_hash = self.trx().hash(); // Change tag to finalized let account = self.account.clone(); std::mem::drop(self); - super::set_tag(account.owner, &account, TAG_STATE_FINALIZED) + super::set_tag(account.owner, &account, TAG_STATE_FINALIZED)?; + StateFinalizedAccount::from_account(program_id, account)?.update(|f| { + f.owner = owner; + f.transaction_hash = trx_hash; + }); + + Ok(()) + } + + pub fn accounts(&self) -> impl Iterator { + self.data.revisions.keys() } #[inline] @@ -184,62 +183,16 @@ impl<'a> StateAccount<'a> { super::section_mut(&self.account, HEADER_OFFSET) } - #[inline] - #[must_use] - fn blocked_accounts_len(&self) -> usize { - self.header().accounts_len - } - - #[inline] - #[must_use] - pub fn blocked_accounts(&self) -> Ref<[BlockedAccount]> { - let accounts_len = self.blocked_accounts_len(); - let accounts_len_bytes = accounts_len * size_of::(); - - let data = self.account.data.borrow(); - Ref::map(data, |d| { - let bytes = &d[BLOCKED_ACCOUNTS_OFFSET..][..accounts_len_bytes]; - - unsafe { - let ptr = bytes.as_ptr().cast(); - std::slice::from_raw_parts(ptr, accounts_len) - } - }) - } - - #[inline] - #[must_use] - fn blocked_accounts_mut(&mut self) -> RefMut<[BlockedAccount]> { - let accounts_len = self.blocked_accounts_len(); - let accounts_len_bytes = accounts_len * size_of::(); - - let data = self.account.data.borrow_mut(); - RefMut::map(data, |d| { - let bytes: &mut [u8] = &mut d[BLOCKED_ACCOUNTS_OFFSET..][..accounts_len_bytes]; - - unsafe { - let ptr = bytes.as_mut_ptr().cast(); - std::slice::from_raw_parts_mut(ptr, accounts_len) - } - }) - } - #[must_use] pub fn buffer(&self) -> Ref<[u8]> { - let accounts_len_bytes = self.blocked_accounts_len() * size_of::(); - let buffer_offset = BLOCKED_ACCOUNTS_OFFSET + accounts_len_bytes; - let data = self.account.data.borrow(); - Ref::map(data, |d| &d[buffer_offset..]) + Ref::map(data, |d| &d[BUFFER_OFFSET..]) } #[must_use] pub fn buffer_mut(&mut self) -> RefMut<[u8]> { - let accounts_len_bytes = self.blocked_accounts_len() * size_of::(); - let buffer_offset = BLOCKED_ACCOUNTS_OFFSET + accounts_len_bytes; - let data = self.account.data.borrow_mut(); - RefMut::map(data, |d| &mut d[buffer_offset..]) + RefMut::map(data, |d| &mut d[BUFFER_OFFSET..]) } #[must_use] @@ -254,69 +207,56 @@ impl<'a> StateAccount<'a> { header.evm_machine_len = evm_machine_len; } - #[must_use] - pub fn owner(&self) -> Pubkey { - self.header().owner - } + pub fn save_data(&mut self) -> Result<()> { + let (evm_state_len, evm_machine_len) = self.buffer_variables(); + let offset = BUFFER_OFFSET + evm_state_len + evm_machine_len; - fn validate_owner(&self, operator: &Operator) -> Result<()> { - let owner = self.owner(); - let operator = *operator.key; + let data_len: usize = { + let mut data = self.account.data.borrow_mut(); + let buffer = &mut data[offset..]; - if owner != operator { - return Err(Error::HolderInvalidOwner(owner, operator)); - } + let mut cursor = std::io::Cursor::new(buffer); + bincode::serialize_into(&mut cursor, &self.data)?; - Ok(()) - } + cursor.position().try_into()? + }; - fn update_current_operator(&mut self, operator: &Operator) { - let mut header = self.header_mut(); - header.operator = *operator.key; - } + self.header_mut().data_len = data_len; - #[must_use] - pub fn trx_hash(&self) -> [u8; 32] { - self.header().transaction_hash + Ok(()) } #[must_use] - pub fn trx_origin(&self) -> Address { - self.header().origin + pub fn owner(&self) -> Pubkey { + self.data.owner } #[must_use] - pub fn trx_chain_id(&self) -> u64 { - self.header().chain_id + pub fn trx(&self) -> &Transaction { + &self.data.transaction } #[must_use] - pub fn trx_gas_price(&self) -> U256 { - self.header().gas_price + pub fn trx_origin(&self) -> Address { + self.data.origin } #[must_use] - pub fn trx_gas_limit(&self) -> U256 { - self.header().gas_limit - } - - pub fn gas_limit_in_tokens(&self) -> Result { - let header = self.header(); - header - .gas_limit - .checked_mul(header.gas_price) - .ok_or(Error::IntegerOverflow) + pub fn trx_chain_id(&self, backend: &impl AccountStorage) -> u64 { + self.data + .transaction + .chain_id() + .unwrap_or_else(|| backend.default_chain_id()) } #[must_use] pub fn gas_used(&self) -> U256 { - self.header().gas_used + self.data.gas_used } #[must_use] pub fn gas_available(&self) -> U256 { - let header = self.header(); - header.gas_limit.saturating_sub(header.gas_used) + self.trx().gas_limit().saturating_sub(self.gas_used()) } pub fn consume_gas(&mut self, amount: U256, receiver: &mut BalanceAccount) -> Result<()> { @@ -324,39 +264,33 @@ impl<'a> StateAccount<'a> { return Ok(()); } - let mut header = self.header_mut(); - - if receiver.chain_id() != header.chain_id { + let trx_chain_id = self.trx().chain_id().unwrap_or(DEFAULT_CHAIN_ID); + if receiver.chain_id() != trx_chain_id { return Err(Error::GasReceiverInvalidChainId); } - let total_gas_used = header.gas_used.saturating_add(amount); - let gas_limit = header.gas_limit; + let total_gas_used = self.data.gas_used.saturating_add(amount); + let gas_limit = self.trx().gas_limit(); if total_gas_used > gas_limit { return Err(Error::OutOfGas(gas_limit, total_gas_used)); } - header.gas_used = total_gas_used; + self.data.gas_used = total_gas_used; let tokens = amount - .checked_mul(header.gas_price) + .checked_mul(self.trx().gas_price()) .ok_or(Error::IntegerOverflow)?; receiver.mint(tokens) } pub fn refund_unused_gas(&mut self, origin: &mut BalanceAccount) -> Result<()> { - assert!(origin.chain_id() == self.trx_chain_id()); + let trx_chain_id = self.trx().chain_id().unwrap_or(DEFAULT_CHAIN_ID); + + assert!(origin.chain_id() == trx_chain_id); assert!(origin.address() == self.trx_origin()); let unused_gas = self.gas_available(); self.consume_gas(unused_gas, origin) } - - pub fn use_gas_limit_multiplier(&mut self) { - let mut header = self.header_mut(); - - let gas_multiplier = U256::from(GAS_LIMIT_MULTIPLIER_NO_CHAINID); - header.gas_limit = header.gas_limit.saturating_mul(gas_multiplier); - } } diff --git a/evm_loader/program/src/account/state_finalized.rs b/evm_loader/program/src/account/state_finalized.rs index 5e73c9801..03e07d43e 100644 --- a/evm_loader/program/src/account/state_finalized.rs +++ b/evm_loader/program/src/account/state_finalized.rs @@ -1,7 +1,10 @@ use std::cell::{Ref, RefMut}; -use super::{ACCOUNT_PREFIX_LEN, TAG_STATE_FINALIZED}; -use crate::error::Result; +use super::{Operator, HOLDER_PREFIX_LEN, TAG_STATE_FINALIZED}; +use crate::{ + error::{Error, Result}, + types::Transaction, +}; use solana_program::{account_info::AccountInfo, pubkey::Pubkey}; /// Storage data account to store execution metainfo between steps for iterative execution @@ -15,7 +18,7 @@ pub struct StateFinalizedAccount<'a> { account: AccountInfo<'a>, } -const HEADER_OFFSET: usize = ACCOUNT_PREFIX_LEN; +const HEADER_OFFSET: usize = HOLDER_PREFIX_LEN; impl<'a> StateFinalizedAccount<'a> { pub fn from_account(program_id: &Pubkey, account: AccountInfo<'a>) -> Result { @@ -52,4 +55,20 @@ impl<'a> StateFinalizedAccount<'a> { pub fn trx_hash(&self) -> [u8; 32] { self.header().transaction_hash } + + pub fn validate_owner(&self, operator: &Operator) -> Result<()> { + if &self.owner() != operator.key { + return Err(Error::HolderInvalidOwner(self.owner(), *operator.key)); + } + + Ok(()) + } + + pub fn validate_trx(&self, transaction: &Transaction) -> Result<()> { + if self.trx_hash() == transaction.hash { + return Err(Error::StorageAccountFinalized); + } + + Ok(()) + } } diff --git a/evm_loader/program/src/account_storage/apply.rs b/evm_loader/program/src/account_storage/apply.rs index 0392d0a3a..ea7e50cd6 100644 --- a/evm_loader/program/src/account_storage/apply.rs +++ b/evm_loader/program/src/account_storage/apply.rs @@ -1,14 +1,15 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use ethnum::U256; use solana_program::account_info::AccountInfo; use solana_program::instruction::Instruction; use solana_program::program::{invoke_signed_unchecked, invoke_unchecked}; +use solana_program::pubkey::Pubkey; use solana_program::system_program; -use crate::account::BalanceAccount; +use crate::account::{increment_revision, BalanceAccount}; use crate::account::{AllocateResult, ContractAccount, StorageCell}; -use crate::account_storage::ProgramAccountStorage; +use crate::account_storage::{AccountStorage, ProgramAccountStorage}; use crate::config::{ ACCOUNT_SEED_VERSION, PAYMENT_TO_TREASURE, STORAGE_ENTRIES_IN_CONTRACT_ACCOUNT, }; @@ -68,6 +69,7 @@ impl<'a> ProgramAccountStorage<'a> { pub fn apply_state_change(&mut self, actions: Vec) -> Result<()> { debug_print!("Applies begin"); + let mut modified_accounts: HashSet = HashSet::with_capacity(64); let mut storage = HashMap::with_capacity(16); for action in actions { @@ -81,6 +83,9 @@ impl<'a> ProgramAccountStorage<'a> { let mut source = self.balance_account(source, chain_id)?; let mut target = self.create_balance_account(target, chain_id)?; source.transfer(&mut target, value)?; + + modified_accounts.insert(*source.pubkey()); + modified_accounts.insert(*target.pubkey()); } Action::Burn { source, @@ -89,6 +94,8 @@ impl<'a> ProgramAccountStorage<'a> { } => { let mut account = self.create_balance_account(source, chain_id)?; account.burn(value)?; + + modified_accounts.insert(*account.pubkey()); } Action::EvmSetStorage { address, @@ -103,13 +110,15 @@ impl<'a> ProgramAccountStorage<'a> { Action::EvmIncrementNonce { address, chain_id } => { let mut account = self.create_balance_account(address, chain_id)?; account.increment_nonce()?; + + // Nonce increment is not count as account modification } Action::EvmSetCode { address, chain_id, code, } => { - ContractAccount::init( + let account = ContractAccount::init( address, chain_id, 0, @@ -117,6 +126,8 @@ impl<'a> ProgramAccountStorage<'a> { &self.accounts, Some(&self.keys), )?; + + modified_accounts.insert(*account.pubkey()); } Action::EvmSelfDestruct { address: _ } => { // EIP-6780: SELFDESTRUCT only in the same transaction @@ -161,23 +172,38 @@ impl<'a> ProgramAccountStorage<'a> { } } - self.apply_storage(storage)?; + self.apply_storage(storage, &mut modified_accounts)?; + + for pubkey in modified_accounts { + let account = self.accounts.get(&pubkey); + increment_revision(self.program_id(), account)?; + } + debug_print!("Applies done"); Ok(()) } - fn apply_storage(&mut self, storage: HashMap>) -> Result<()> { + fn apply_storage( + &mut self, + storage: HashMap>, + modified_accounts: &mut HashSet, + ) -> Result<()> { const STATIC_STORAGE_LIMIT: U256 = U256::new(STORAGE_ENTRIES_IN_CONTRACT_ACCOUNT as u128); for (address, storage) in storage { - let mut contract = self.contract_account(address)?; + let mut contract: Option = None; let mut infinite_values: HashMap> = HashMap::with_capacity(storage.len()); for (index, value) in storage { if index < STATIC_STORAGE_LIMIT { + let contract = contract.get_or_insert_with(|| { + self.contract_account(address) + .expect("contract already created") + }); + // Static Storage - Write into contract account let index: usize = index.as_usize(); contract.set_storage_value(index, &value); @@ -193,10 +219,16 @@ impl<'a> ProgramAccountStorage<'a> { } } + if let Some(contract) = contract { + modified_accounts.insert(*contract.pubkey()); + } + for (index, values) in infinite_values { let cell_address = self.keys.storage_cell_address(&crate::ID, address, index); let account = self.accounts.get(cell_address.pubkey()); + modified_accounts.insert(*account.key); + if system_program::check_id(account.owner) { let (_, bump) = self.keys.contract_with_bump_seed(&crate::ID, address); let sign: &[&[u8]] = &[&[ACCOUNT_SEED_VERSION], address.as_bytes(), &[bump]]; diff --git a/evm_loader/program/src/account_storage/base.rs b/evm_loader/program/src/account_storage/base.rs index 24464937d..df43c2ae4 100644 --- a/evm_loader/program/src/account_storage/base.rs +++ b/evm_loader/program/src/account_storage/base.rs @@ -4,11 +4,12 @@ use crate::account::{ use crate::account_storage::ProgramAccountStorage; use crate::config::DEFAULT_CHAIN_ID; use crate::error::Result; -use crate::types::Address; +use crate::types::{Address, Transaction}; use ethnum::U256; use solana_program::{clock::Clock, rent::Rent, system_program, sysvar::Sysvar}; use super::keys_cache::KeysCache; +use super::AccountStorage; impl<'a> ProgramAccountStorage<'a> { pub fn new(accounts: AccountsDB<'a>) -> Result { @@ -95,4 +96,15 @@ impl<'a> ProgramAccountStorage<'a> { &self.rent, ) } + + pub fn origin( + &self, + address: Address, + transaction: &Transaction, + ) -> Result> { + let chain_id = transaction + .chain_id() + .unwrap_or_else(|| self.default_chain_id()); + self.create_balance_account(address, chain_id) + } } diff --git a/evm_loader/program/src/entrypoint.rs b/evm_loader/program/src/entrypoint.rs index 3f64b0757..eaf4edc69 100644 --- a/evm_loader/program/src/entrypoint.rs +++ b/evm_loader/program/src/entrypoint.rs @@ -125,9 +125,6 @@ fn process_instruction<'a>( instruction::create_main_treasury::process(program_id, accounts, instruction) .map_err(Error::from) } - EvmInstruction::AccountBlockAdd => { - instruction::account_block_add::process(program_id, accounts, instruction) - } EvmInstruction::AccountCreateBalance => { instruction::account_create_balance::process(program_id, accounts, instruction) } diff --git a/evm_loader/program/src/evm/mod.rs b/evm_loader/program/src/evm/mod.rs index b26c65cc1..d26a722cf 100644 --- a/evm_loader/program/src/evm/mod.rs +++ b/evm_loader/program/src/evm/mod.rs @@ -210,27 +210,13 @@ impl Machine { impl Machine { #[maybe_async] pub async fn new( - trx: Transaction, + trx: &Transaction, origin: Address, backend: &mut B, tracer: Option, ) -> Result { let trx_chain_id = trx.chain_id().unwrap_or_else(|| backend.default_chain_id()); - if !backend.is_valid_chain_id(trx_chain_id) { - return Err(Error::InvalidChainId(trx_chain_id)); - } - - let nonce = backend.nonce(origin, trx_chain_id).await?; - - if nonce == u64::MAX { - return Err(Error::NonceOverflow(origin)); - } - - if nonce != trx.nonce() { - return Err(Error::InvalidTransactionNonce(origin, nonce, trx.nonce())); - } - if backend.balance(origin, trx_chain_id).await? < trx.value() { return Err(Error::InsufficientBalance( origin, @@ -239,12 +225,6 @@ impl Machine { )); } - // TODO may be remove. This requires additional account - // Never actually happens, or at least should not - // if backend.code_size(origin).await? != 0 { - // return Err(Error::SenderHasDeployedCode(origin)); - // } - if trx.target().is_some() { Self::new_call(trx_chain_id, trx, origin, backend, tracer).await } else { @@ -255,7 +235,7 @@ impl Machine { #[maybe_async] async fn new_call( chain_id: u64, - trx: Transaction, + trx: &Transaction, origin: Address, backend: &mut B, tracer: Option, @@ -265,7 +245,6 @@ impl Machine { let target = trx.target().unwrap(); log_data(&[b"ENTER", b"CALL", target.as_bytes()]); - backend.increment_nonce(origin, chain_id)?; backend.snapshot(); backend @@ -287,7 +266,7 @@ impl Machine { gas_price: trx.gas_price(), gas_limit: trx.gas_limit(), execution_code, - call_data: trx.into_call_data(), + call_data: Buffer::from_slice(trx.call_data()), return_data: Buffer::empty(), return_range: 0..0, stack: Stack::new(), @@ -304,7 +283,7 @@ impl Machine { #[maybe_async] async fn new_create( chain_id: u64, - trx: Transaction, + trx: &Transaction, origin: Address, backend: &mut B, tracer: Option, @@ -319,7 +298,6 @@ impl Machine { return Err(Error::DeployToExistingAccount(target, origin)); } - backend.increment_nonce(origin, chain_id)?; backend.snapshot(); backend.increment_nonce(target, chain_id)?; @@ -346,7 +324,7 @@ impl Machine { pc: 0_usize, is_static: false, reason: Reason::Create, - execution_code: trx.into_call_data(), + execution_code: Buffer::from_slice(trx.call_data()), call_data: Buffer::empty(), parent: None, phantom: PhantomData, diff --git a/evm_loader/program/src/executor/action.rs b/evm_loader/program/src/executor/action.rs index 326224c16..d98eaf922 100644 --- a/evm_loader/program/src/executor/action.rs +++ b/evm_loader/program/src/executor/action.rs @@ -2,7 +2,7 @@ use ethnum::U256; use serde::{Deserialize, Serialize}; use solana_program::{instruction::AccountMeta, pubkey::Pubkey}; -use crate::types::Address; +use crate::types::{serde::bytes_32, Address}; #[derive(Debug, Clone, Serialize, Deserialize)] pub enum Action { @@ -31,7 +31,7 @@ pub enum Action { address: Address, #[serde(with = "ethnum::serde::bytes::le")] index: U256, - #[serde(with = "serde_bytes_32")] + #[serde(with = "bytes_32")] value: [u8; 32], }, EvmIncrementNonce { @@ -80,108 +80,3 @@ pub fn filter_selfdestruct(actions: Vec) -> Vec { }) .collect() } - -mod serde_bytes_32 { - pub fn serialize(value: &[u8; 32], serializer: S) -> Result - where - S: serde::ser::Serializer, - { - if serializer.is_human_readable() { - serializer.serialize_str(&hex::encode(value)) - } else { - serializer.serialize_bytes(value) - } - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error> - where - D: serde::Deserializer<'de>, - { - struct BytesVisitor; - - impl<'de> serde::de::Visitor<'de> for BytesVisitor { - type Value = [u8; 32]; - - fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_str("[u8; 32]") - } - - fn visit_str(self, value: &str) -> Result - where - E: serde::de::Error, - { - use serde::de::Unexpected::Str; - - let value = hex::decode(value) - .map_err(|_| serde::de::Error::invalid_value(Str(value), &self))?; - - let value_len = value.len(); - value - .try_into() - .map_err(|_| serde::de::Error::invalid_length(value_len, &self)) - } - - fn visit_bytes(self, value: &[u8]) -> Result - where - E: serde::de::Error, - { - value - .try_into() - .map_err(|_| serde::de::Error::invalid_length(value.len(), &self)) - } - - fn visit_seq(self, mut seq: S) -> Result - where - S: serde::de::SeqAccess<'de>, - { - let mut bytes = Vec::with_capacity(32); - while let Some(b) = seq.next_element()? { - bytes.push(b); - } - bytes - .try_into() - .map_err(|_| serde::de::Error::custom("Invalid [u8; 32] value")) - } - } - - if deserializer.is_human_readable() { - deserializer.deserialize_str(BytesVisitor) - } else { - deserializer.deserialize_bytes(BytesVisitor) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn roundtrip_bincode() { - let action = Action::EvmSetStorage { - address: Address::default(), - index: U256::from_le_bytes([ - 255, 46, 185, 41, 144, 201, 3, 36, 227, 18, 148, 147, 106, 131, 110, 6, 229, 235, - 44, 154, 71, 124, 159, 144, 47, 119, 77, 5, 154, 49, 23, 54, - ]), - value: Default::default(), - }; - let serialized = bincode::serialize(&action).unwrap(); - let _deserialized: Action = bincode::deserialize(&serialized).unwrap(); - } - - #[cfg(not(target_os = "solana"))] - #[test] - fn roundtrip_json() { - let action = Action::EvmSetStorage { - address: Address::default(), - index: U256::from_le_bytes([ - 255, 46, 185, 41, 144, 201, 3, 36, 227, 18, 148, 147, 106, 131, 110, 6, 229, 235, - 44, 154, 71, 124, 159, 144, 47, 119, 77, 5, 154, 49, 23, 54, - ]), - value: Default::default(), - }; - let serialized = serde_json::to_string(&action).unwrap(); - let _deserialized: Action = serde_json::from_str(&serialized).unwrap(); - } -} diff --git a/evm_loader/program/src/executor/state.rs b/evm_loader/program/src/executor/state.rs index 9fc3b1980..6c25f57c3 100644 --- a/evm_loader/program/src/executor/state.rs +++ b/evm_loader/program/src/executor/state.rs @@ -423,8 +423,7 @@ impl<'a, B: AccountStorage> Database for ExecutorState<'a, B> { if self.stack.is_empty() { // sanity check - assert_eq!(self.actions.len(), 1); - assert!(matches!(self.actions[0], Action::EvmIncrementNonce { .. })); + assert!(self.actions.is_empty()); } } diff --git a/evm_loader/program/src/instruction/account_block_add.rs b/evm_loader/program/src/instruction/account_block_add.rs deleted file mode 100644 index b875f6a12..000000000 --- a/evm_loader/program/src/instruction/account_block_add.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::error::Result; -use solana_program::instruction::TRANSACTION_LEVEL_STACK_HEIGHT; -use solana_program::{account_info::AccountInfo, pubkey::Pubkey}; - -pub fn process<'a>( - _program_id: &'a Pubkey, - _accounts: &'a [AccountInfo<'a>], - _instruction: &[u8], -) -> Result<()> { - let stack_height = solana_program::instruction::get_stack_height(); - assert_eq!(stack_height, TRANSACTION_LEVEL_STACK_HEIGHT); - - log_msg!("Instruction: Block Accounts"); - - todo!(); - - // let mut state = State::from_account(program_id, &accounts[0])?; - // let operator = Operator::from_account(&accounts[1])?; - - // if &state.owner != operator.key { - // return Err(Error::HolderInvalidOwner(state.owner, *operator.key)); - // } - - // let mut blocked_accounts = state.read_blocked_accounts()?; - // let mut blocked_keys: BTreeSet = blocked_accounts.iter().map(|a| a.key).collect(); - - // for account_info in &accounts[2..] { - // if blocked_keys.contains(account_info.key) { - // continue; - // } - - // let mut meta = BlockedAccountMeta { - // key: *account_info.key, - // exists: false, - // is_writable: account_info.is_writable, - // }; - - // if let Ok(mut account) = EthereumAccount::from_account(program_id, account_info) { - // account.check_blocked()?; - // account.rw_blocked = true; - - // meta.exists = true; - // } - - // blocked_accounts.push(meta); - // blocked_keys.insert(*account_info.key); - // } - - // state.update_blocked_accounts(blocked_accounts.into_iter()) -} diff --git a/evm_loader/program/src/instruction/mod.rs b/evm_loader/program/src/instruction/mod.rs index d5468775a..3a805e782 100644 --- a/evm_loader/program/src/instruction/mod.rs +++ b/evm_loader/program/src/instruction/mod.rs @@ -157,9 +157,6 @@ pub enum EvmInstruction { /// None CreateMainTreasury, - /// Block additional accounts - AccountBlockAdd, - /// Create a User Balance account /// /// Accounts: @@ -194,7 +191,6 @@ impl EvmInstruction { 0x25 => Self::HolderDelete, // 37 0x26 => Self::HolderWrite, // 38 0x29 => Self::CreateMainTreasury, // 41 - 0x2B => Self::AccountBlockAdd, // 43 0x30 => Self::AccountCreateBalance, // 48 0x31 => Self::Deposit, // 49 @@ -218,7 +214,6 @@ impl EvmInstruction { } } -pub mod account_block_add; pub mod account_create_balance; pub mod account_holder_create; pub mod account_holder_delete; diff --git a/evm_loader/program/src/instruction/transaction_cancel.rs b/evm_loader/program/src/instruction/transaction_cancel.rs index bfe195ea5..489685363 100644 --- a/evm_loader/program/src/instruction/transaction_cancel.rs +++ b/evm_loader/program/src/instruction/transaction_cancel.rs @@ -1,4 +1,5 @@ use crate::account::{AccountsDB, BalanceAccount, Operator, StateAccount}; +use crate::config::DEFAULT_CHAIN_ID; use crate::debug::log_data; use crate::error::{Error, Result}; use crate::gasometer::{CANCEL_TRX_COST, LAST_ITERATION_COST}; @@ -23,16 +24,16 @@ pub fn process<'a>( log_data(&[b"MINER", operator_balance.address().as_bytes()]); let accounts_db = AccountsDB::new(&accounts[3..], operator, Some(operator_balance), None, None); - let storage = StateAccount::restore(program_id, storage_info, &accounts_db, true)?; + let (storage, _) = StateAccount::restore(program_id, storage_info, &accounts_db)?; validate(&storage, transaction_hash)?; execute(program_id, accounts_db, storage) } fn validate(storage: &StateAccount, transaction_hash: &[u8; 32]) -> Result<()> { - if &storage.trx_hash() != transaction_hash { + if &storage.trx().hash() != transaction_hash { return Err(Error::HolderInvalidHash( - storage.trx_hash(), + storage.trx().hash(), *transaction_hash, )); } @@ -45,6 +46,8 @@ fn execute<'a>( mut accounts: AccountsDB<'a>, mut storage: StateAccount<'a>, ) -> Result<()> { + let trx_chain_id = storage.trx().chain_id().unwrap_or(DEFAULT_CHAIN_ID); + let used_gas = U256::ZERO; let total_used_gas = storage.gas_used(); log_data(&[ @@ -57,15 +60,13 @@ fn execute<'a>( let _ = storage.consume_gas(gas, accounts.operator_balance()); // ignore error let origin = storage.trx_origin(); - let (origin_pubkey, _) = origin.find_balance_address(program_id, storage.trx_chain_id()); + let (origin_pubkey, _) = origin.find_balance_address(program_id, trx_chain_id); { let origin_info = accounts.get(&origin_pubkey).clone(); let mut account = BalanceAccount::from_account(program_id, origin_info)?; - account.increment_nonce()?; - storage.refund_unused_gas(&mut account)?; } - storage.finalize(program_id, &accounts) + storage.finalize(program_id) } diff --git a/evm_loader/program/src/instruction/transaction_execute.rs b/evm_loader/program/src/instruction/transaction_execute.rs index 9bac6d615..85dbed504 100644 --- a/evm_loader/program/src/instruction/transaction_execute.rs +++ b/evm_loader/program/src/instruction/transaction_execute.rs @@ -1,5 +1,3 @@ -use solana_program::pubkey::Pubkey; - use crate::account::{AccountsDB, AllocateResult}; use crate::account_storage::ProgramAccountStorage; use crate::debug::log_data; @@ -11,20 +9,6 @@ use crate::gasometer::Gasometer; use crate::instruction::transaction_step::log_return_value; use crate::types::{Address, Transaction}; -pub fn validate(program_id: &Pubkey, accounts: &AccountsDB) -> Result<()> { - for account in accounts { - if account.owner != program_id { - continue; - } - - if crate::account::is_blocked(program_id, account)? { - return Err(Error::AccountBlocked(*account.key)); - } - } - - Ok(()) -} - pub fn execute( accounts: AccountsDB<'_>, mut gasometer: Gasometer, @@ -37,10 +21,14 @@ pub fn execute( let mut account_storage = ProgramAccountStorage::new(accounts)?; + trx.validate(origin, &account_storage)?; + + account_storage.origin(origin, &trx)?.increment_nonce()?; + let (exit_reason, apply_state) = { let mut backend = ExecutorState::new(&account_storage); - let mut evm = Machine::new(trx, origin, &mut backend, None::)?; + let mut evm = Machine::new(&trx, origin, &mut backend, None::)?; let (result, _, _) = evm.execute(u64::MAX, &mut backend)?; let actions = backend.into_actions(); diff --git a/evm_loader/program/src/instruction/transaction_execute_from_account.rs b/evm_loader/program/src/instruction/transaction_execute_from_account.rs index fbea0adfe..7d7ad6c2d 100644 --- a/evm_loader/program/src/instruction/transaction_execute_from_account.rs +++ b/evm_loader/program/src/instruction/transaction_execute_from_account.rs @@ -46,6 +46,5 @@ pub fn process<'a>( gasometer.record_address_lookup_table(accounts); gasometer.record_write_to_holder(&trx); - super::transaction_execute::validate(program_id, &accounts_db)?; super::transaction_execute::execute(accounts_db, gasometer, trx, origin) } diff --git a/evm_loader/program/src/instruction/transaction_execute_from_instruction.rs b/evm_loader/program/src/instruction/transaction_execute_from_instruction.rs index a2cd8117f..b51d593d8 100644 --- a/evm_loader/program/src/instruction/transaction_execute_from_instruction.rs +++ b/evm_loader/program/src/instruction/transaction_execute_from_instruction.rs @@ -41,6 +41,5 @@ pub fn process<'a>( gasometer.record_solana_transaction_cost(); gasometer.record_address_lookup_table(accounts); - super::transaction_execute::validate(program_id, &accounts_db)?; super::transaction_execute::execute(accounts_db, gasometer, trx, origin) } diff --git a/evm_loader/program/src/instruction/transaction_step.rs b/evm_loader/program/src/instruction/transaction_step.rs index 83dc853d9..aa377f6db 100644 --- a/evm_loader/program/src/instruction/transaction_step.rs +++ b/evm_loader/program/src/instruction/transaction_step.rs @@ -7,7 +7,6 @@ use crate::evm::tracing::NoopEventListener; use crate::evm::{ExitStatus, Machine}; use crate::executor::{Action, ExecutorState}; use crate::gasometer::Gasometer; -use crate::types::{Address, Transaction}; type EvmBackend<'a, 'r> = ExecutorState<'r, ProgramAccountStorage<'a>>; type Evm<'a, 'r> = Machine, NoopEventListener>; @@ -16,23 +15,34 @@ pub fn do_begin<'a>( accounts: AccountsDB<'a>, mut storage: StateAccount<'a>, gasometer: Gasometer, - trx: Transaction, - origin: Address, ) -> Result<()> { debug_print!("do_begin"); - let accounts = ProgramAccountStorage::new(accounts)?; + let account_storage = ProgramAccountStorage::new(accounts)?; + + let origin = storage.trx_origin(); + + storage.trx().validate(origin, &account_storage)?; - let mut backend = ExecutorState::new(&accounts); - let evm = Machine::new(trx, origin, &mut backend, None::)?; + // Increment origin nonce in the first iteration + // This allows us to run multiple iterative transactions from the same sender in parallel + // These transactions are guaranteed to start in a correct sequence + // BUT they finalize in an undefined order + let mut origin_account = account_storage.origin(origin, storage.trx())?; + origin_account.increment_nonce()?; // Burn `gas_limit` tokens from the origin account // Later we will mint them to the operator - let mut origin_balance = accounts.create_balance_account(origin, storage.trx_chain_id())?; - origin_balance.burn(storage.gas_limit_in_tokens()?)?; + // Remaining tokens are returned to the origin in the last iteration + let gas_limit_in_tokens = storage.trx().gas_limit_in_tokens()?; + origin_account.burn(gas_limit_in_tokens)?; + + // Initialize EVM and serialize it to the Holder + let mut backend = ExecutorState::new(&account_storage); + let evm = Machine::new(storage.trx(), origin, &mut backend, None)?; serialize_evm_state(&mut storage, &backend, &evm)?; - finalize(0, storage, accounts, None, gasometer) + finalize(0, storage, account_storage, None, gasometer) } pub fn do_continue<'a>( @@ -40,32 +50,35 @@ pub fn do_continue<'a>( accounts: AccountsDB<'a>, mut storage: StateAccount<'a>, gasometer: Gasometer, + reset: bool, ) -> Result<()> { debug_print!("do_continue"); - if (step_count < EVM_STEPS_MIN) && (storage.trx_gas_price() > 0) { + if (step_count < EVM_STEPS_MIN) && (storage.trx().gas_price() > 0) { return Err(Error::Custom(format!( "Step limit {step_count} below minimum {EVM_STEPS_MIN}" ))); } let account_storage = ProgramAccountStorage::new(accounts)?; - let (mut backend, mut evm) = deserialize_evm_state(&storage, &account_storage)?; + let (mut backend, mut evm) = if reset { + let mut backend = ExecutorState::new(&account_storage); + let evm = Machine::new(storage.trx(), storage.trx_origin(), &mut backend, None)?; + (backend, evm) + } else { + deserialize_evm_state(&storage, &account_storage)? + }; - let (result, steps_executed, _) = { - match backend.exit_status() { - Some(status) => (status.clone(), 0_u64, None), - None => evm.execute(step_count, &mut backend)?, - } + let (result, steps_executed, _) = match backend.exit_status() { + Some(status) => (status.clone(), 0_u64, None), + None => evm.execute(step_count, &mut backend)?, }; if (result != ExitStatus::StepLimit) && (steps_executed > 0) { backend.set_exit_status(result.clone()); } - if steps_executed > 0 { - serialize_evm_state(&mut storage, &backend, &evm)?; - } + serialize_evm_state(&mut storage, &backend, &evm)?; let results = match result { ExitStatus::StepLimit => None, @@ -115,10 +128,12 @@ fn finalize<'a>( if let Some(status) = status { log_return_value(&status); - let mut origin = accounts.balance_account(storage.trx_origin(), storage.trx_chain_id())?; + let mut origin = accounts.origin(storage.trx_origin(), storage.trx())?; storage.refund_unused_gas(&mut origin)?; - storage.finalize(accounts.program_id(), accounts.db())?; + storage.finalize(accounts.program_id())?; + } else { + storage.save_data()?; } Ok(()) diff --git a/evm_loader/program/src/instruction/transaction_step_from_account.rs b/evm_loader/program/src/instruction/transaction_step_from_account.rs index 6d7e21aa5..4cc712ba7 100644 --- a/evm_loader/program/src/instruction/transaction_step_from_account.rs +++ b/evm_loader/program/src/instruction/transaction_step_from_account.rs @@ -1,7 +1,7 @@ use crate::account::legacy::{TAG_HOLDER_DEPRECATED, TAG_STATE_FINALIZED_DEPRECATED}; use crate::account::{ - program, AccountsDB, BalanceAccount, Holder, Operator, StateAccount, Treasury, TAG_HOLDER, - TAG_STATE, TAG_STATE_FINALIZED, + program, AccountsDB, AccountsStatus, BalanceAccount, Holder, Operator, StateAccount, Treasury, + TAG_HOLDER, TAG_STATE, TAG_STATE_FINALIZED, }; use crate::debug::log_data; use crate::error::{Error, Result}; @@ -57,7 +57,7 @@ pub fn process_inner<'a>( match tag { TAG_HOLDER | TAG_HOLDER_DEPRECATED => { - let trx = { + let mut trx = { let holder = Holder::from_account(program_id, holder_or_storage.clone())?; holder.validate_owner(accounts_db.operator())?; @@ -72,6 +72,11 @@ pub fn process_inner<'a>( log_data(&[b"HASH", &trx.hash]); log_data(&[b"MINER", miner_address.as_bytes()]); + if increase_gas_limit { + assert!(trx.chain_id().is_none()); + trx.use_gas_limit_multiplier(); + } + let origin = trx.recover_caller_address()?; let mut gasometer = Gasometer::new(U256::ZERO, accounts_db.operator())?; @@ -82,32 +87,28 @@ pub fn process_inner<'a>( excessive_lamports += crate::account::legacy::update_legacy_accounts(&accounts_db)?; gasometer.refund_lamports(excessive_lamports); - let mut storage = StateAccount::new( + let storage = StateAccount::new( program_id, holder_or_storage.clone(), &accounts_db, origin, - &trx, + trx, )?; - if increase_gas_limit { - assert!(trx.chain_id().is_none()); - storage.use_gas_limit_multiplier(); - } - - do_begin(accounts_db, storage, gasometer, trx, origin) + do_begin(accounts_db, storage, gasometer) } TAG_STATE => { - let storage = - StateAccount::restore(program_id, holder_or_storage.clone(), &accounts_db, false)?; + let (storage, accounts_status) = + StateAccount::restore(program_id, holder_or_storage.clone(), &accounts_db)?; - log_data(&[b"HASH", &storage.trx_hash()]); + log_data(&[b"HASH", &storage.trx().hash()]); log_data(&[b"MINER", miner_address.as_bytes()]); let mut gasometer = Gasometer::new(storage.gas_used(), accounts_db.operator())?; gasometer.record_solana_transaction_cost(); - do_continue(step_count, accounts_db, storage, gasometer) + let reset = accounts_status != AccountsStatus::Ok; + do_continue(step_count, accounts_db, storage, gasometer, reset) } TAG_STATE_FINALIZED | TAG_STATE_FINALIZED_DEPRECATED => Err(Error::StorageAccountFinalized), _ => Err(Error::AccountInvalidTag(*holder_or_storage.key, TAG_HOLDER)), diff --git a/evm_loader/program/src/instruction/transaction_step_from_instruction.rs b/evm_loader/program/src/instruction/transaction_step_from_instruction.rs index cd41be54a..adc2b7ae6 100644 --- a/evm_loader/program/src/instruction/transaction_step_from_instruction.rs +++ b/evm_loader/program/src/instruction/transaction_step_from_instruction.rs @@ -1,7 +1,7 @@ use crate::account::legacy::{TAG_HOLDER_DEPRECATED, TAG_STATE_FINALIZED_DEPRECATED}; use crate::account::{ - program, AccountsDB, BalanceAccount, Operator, StateAccount, Treasury, TAG_HOLDER, TAG_STATE, - TAG_STATE_FINALIZED, + program, AccountsDB, AccountsStatus, BalanceAccount, Operator, StateAccount, Treasury, + TAG_HOLDER, TAG_STATE, TAG_STATE_FINALIZED, }; use crate::debug::log_data; use crate::error::{Error, Result}; @@ -63,20 +63,22 @@ pub fn process<'a>( excessive_lamports += crate::account::legacy::update_legacy_accounts(&accounts_db)?; gasometer.refund_lamports(excessive_lamports); - let storage = StateAccount::new(program_id, storage_info, &accounts_db, origin, &trx)?; + let storage = StateAccount::new(program_id, storage_info, &accounts_db, origin, trx)?; - do_begin(accounts_db, storage, gasometer, trx, origin) + do_begin(accounts_db, storage, gasometer) } TAG_STATE => { - let storage = StateAccount::restore(program_id, storage_info, &accounts_db, false)?; + let (storage, accounts_status) = + StateAccount::restore(program_id, storage_info, &accounts_db)?; - log_data(&[b"HASH", &storage.trx_hash()]); + log_data(&[b"HASH", &storage.trx().hash()]); log_data(&[b"MINER", miner_address.as_bytes()]); let mut gasometer = Gasometer::new(storage.gas_used(), accounts_db.operator())?; gasometer.record_solana_transaction_cost(); - do_continue(step_count, accounts_db, storage, gasometer) + let reset = accounts_status != AccountsStatus::Ok; + do_continue(step_count, accounts_db, storage, gasometer, reset) } _ => Err(Error::AccountInvalidTag(*storage_info.key, TAG_HOLDER)), }?; diff --git a/evm_loader/program/src/types/mod.rs b/evm_loader/program/src/types/mod.rs index 04afe313f..756a5a409 100644 --- a/evm_loader/program/src/types/mod.rs +++ b/evm_loader/program/src/types/mod.rs @@ -6,4 +6,5 @@ pub use transaction::Transaction; pub use transaction::TransactionPayload; mod address; +pub mod serde; mod transaction; diff --git a/evm_loader/program/src/types/serde.rs b/evm_loader/program/src/types/serde.rs new file mode 100644 index 000000000..eb94cfbbe --- /dev/null +++ b/evm_loader/program/src/types/serde.rs @@ -0,0 +1,130 @@ +// use ethnum::U256; + +// serde_with::serde_conv!( +// pub U256AsWords, +// U256, +// |value: &U256| { value.into_words() }, +// |words: (u128, u128)| -> Result<_, std::convert::Infallible> { Ok(U256::from_words(words.0, words.1)) } +// ); + +pub mod option_u256 { + use std::fmt::{self, Formatter}; + + use ethnum::U256; + use serde::{de::Visitor, Deserializer, Serializer}; + + pub fn serialize(value: &Option, serializer: S) -> Result + where + S: Serializer, + { + if let Some(value) = value { + serializer.serialize_bytes(&value.to_le_bytes()) + } else { + serializer.serialize_bytes(&[]) + } + } + + struct BytesVisitor; + + impl<'de> Visitor<'de> for BytesVisitor { + type Value = Option; + + fn expecting(&self, f: &mut Formatter) -> fmt::Result { + f.write_str(concat!("32 bytes in little endian")) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: serde::de::Error, + { + if v.is_empty() { + return Ok(None); + } + + let bytes = v + .try_into() + .map_err(|_| E::invalid_length(v.len(), &self))?; + + Ok(Some(U256::from_le_bytes(bytes))) + } + } + + #[doc(hidden)] + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_bytes(BytesVisitor) + } +} + +pub mod bytes_32 { + pub fn serialize(value: &[u8; 32], serializer: S) -> Result + where + S: serde::ser::Serializer, + { + if serializer.is_human_readable() { + serializer.serialize_str(&hex::encode(value)) + } else { + serializer.serialize_bytes(value) + } + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error> + where + D: serde::Deserializer<'de>, + { + struct BytesVisitor; + + impl<'de> serde::de::Visitor<'de> for BytesVisitor { + type Value = [u8; 32]; + + fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("[u8; 32]") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + use serde::de::Unexpected::Str; + + let value = hex::decode(value) + .map_err(|_| serde::de::Error::invalid_value(Str(value), &self))?; + + let value_len = value.len(); + value + .try_into() + .map_err(|_| serde::de::Error::invalid_length(value_len, &self)) + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: serde::de::Error, + { + value + .try_into() + .map_err(|_| serde::de::Error::invalid_length(value.len(), &self)) + } + + fn visit_seq(self, mut seq: S) -> Result + where + S: serde::de::SeqAccess<'de>, + { + let mut bytes = Vec::with_capacity(32); + while let Some(b) = seq.next_element()? { + bytes.push(b); + } + bytes + .try_into() + .map_err(|_| serde::de::Error::custom("Invalid [u8; 32] value")) + } + } + + if deserializer.is_human_readable() { + deserializer.deserialize_str(BytesVisitor) + } else { + deserializer.deserialize_bytes(BytesVisitor) + } + } +} diff --git a/evm_loader/program/src/types/transaction.rs b/evm_loader/program/src/types/transaction.rs index bae39cf34..c3d82173e 100644 --- a/evm_loader/program/src/types/transaction.rs +++ b/evm_loader/program/src/types/transaction.rs @@ -1,13 +1,22 @@ use ethnum::U256; +use maybe_async::maybe_async; +use serde::{Deserialize, Serialize}; use std::convert::TryInto; -use crate::error::Error; +use crate::{ + account_storage::AccountStorage, config::GAS_LIMIT_MULTIPLIER_NO_CHAINID, error::Error, +}; -use super::Address; +use super::{ + serde::{bytes_32, option_u256}, + Address, +}; #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] -pub struct StorageKey([u8; 32]); +#[derive( + Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize, +)] +pub struct StorageKey(#[serde(with = "bytes_32")] [u8; 32]); impl rlp::Decodable for StorageKey { fn decode(rlp: &rlp::Rlp) -> Result { @@ -67,17 +76,25 @@ impl TransactionEnvelope { } } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct LegacyTx { pub nonce: u64, + #[serde(with = "ethnum::serde::bytes::le")] pub gas_price: U256, + #[serde(with = "ethnum::serde::bytes::le")] pub gas_limit: U256, pub target: Option
, + #[serde(with = "ethnum::serde::bytes::le")] pub value: U256, + #[serde(with = "serde_bytes")] pub call_data: Vec, + #[serde(with = "ethnum::serde::bytes::le")] pub v: U256, + #[serde(with = "ethnum::serde::bytes::le")] pub r: U256, + #[serde(with = "ethnum::serde::bytes::le")] pub s: U256, + #[serde(with = "option_u256")] pub chain_id: Option, pub recovery_id: u8, } @@ -148,16 +165,23 @@ impl rlp::Decodable for LegacyTx { } } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct AccessListTx { pub nonce: u64, + #[serde(with = "ethnum::serde::bytes::le")] pub gas_price: U256, + #[serde(with = "ethnum::serde::bytes::le")] pub gas_limit: U256, pub target: Option
, + #[serde(with = "ethnum::serde::bytes::le")] pub value: U256, + #[serde(with = "serde_bytes")] pub call_data: Vec, + #[serde(with = "ethnum::serde::bytes::le")] pub r: U256, + #[serde(with = "ethnum::serde::bytes::le")] pub s: U256, + #[serde(with = "ethnum::serde::bytes::le")] pub chain_id: U256, pub recovery_id: u8, pub access_list: Vec<(Address, Vec)>, @@ -245,17 +269,19 @@ impl rlp::Decodable for AccessListTx { // TODO: Will be added as a part of EIP-1559 // struct DynamicFeeTx {} -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub enum TransactionPayload { Legacy(LegacyTx), AccessList(AccessListTx), } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct Transaction { pub transaction: TransactionPayload, pub byte_len: usize, + #[serde(with = "bytes_32")] pub hash: [u8; 32], + #[serde(with = "bytes_32")] pub signed_hash: [u8; 32], } @@ -491,6 +517,12 @@ impl Transaction { } } + pub fn gas_limit_in_tokens(&self) -> Result { + self.gas_price() + .checked_mul(self.gas_limit()) + .ok_or(Error::IntegerOverflow) + } + #[must_use] pub fn target(&self) -> Option
{ match self.transaction { @@ -515,16 +547,6 @@ impl Transaction { } } - #[must_use] - pub fn into_call_data(self) -> crate::evm::Buffer { - match self.transaction { - TransactionPayload::Legacy(LegacyTx { call_data, .. }) - | TransactionPayload::AccessList(AccessListTx { call_data, .. }) => { - crate::evm::Buffer::from_vec(call_data) - } - } - } - #[must_use] pub fn r(&self) -> U256 { match self.transaction { @@ -582,6 +604,40 @@ impl Transaction { TransactionPayload::Legacy(_) => None, } } + + pub fn use_gas_limit_multiplier(&mut self) { + let gas_multiplier = U256::from(GAS_LIMIT_MULTIPLIER_NO_CHAINID); + + match &mut self.transaction { + TransactionPayload::AccessList(AccessListTx { gas_limit, .. }) + | TransactionPayload::Legacy(LegacyTx { gas_limit, .. }) => { + *gas_limit = gas_limit.saturating_mul(gas_multiplier); + } + } + } + + #[maybe_async] + pub async fn validate( + &self, + origin: Address, + backend: &impl AccountStorage, + ) -> Result<(), crate::error::Error> { + let chain_id = self + .chain_id() + .unwrap_or_else(|| backend.default_chain_id()); + + if !backend.is_valid_chain_id(chain_id) { + return Err(Error::InvalidChainId(chain_id)); + } + + let origin_nonce = backend.nonce(origin, chain_id).await; + if origin_nonce != self.nonce() { + let error = Error::InvalidTransactionNonce(origin, origin_nonce, self.nonce()); + return Err(error); + } + + Ok(()) + } } #[inline] From 9d58ae78ef5d16b7cad673aaf1bf4c1d7f3e9c09 Mon Sep 17 00:00:00 2001 From: Kristina Nikolaeva <112946046+kristinaNikolaevaa@users.noreply.github.com> Date: Thu, 22 Feb 2024 13:47:49 +0100 Subject: [PATCH 6/8] added test solana programs (#284) --- .github/workflows/deploy.py | 9 ++++++--- .github/workflows/github_api_client.py | 9 +++++++++ .github/workflows/pipeline.yml | 5 +++-- Dockerfile | 6 +++--- ci/solana-run-neon.sh | 12 ++++++++---- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/.github/workflows/deploy.py b/.github/workflows/deploy.py index 1c5eecfb6..20d7ef6a1 100644 --- a/.github/workflows/deploy.py +++ b/.github/workflows/deploy.py @@ -105,16 +105,19 @@ def run_subprocess(command): @cli.command(name="run_tests") @click.option('--github_sha') @click.option('--neon_test_branch') -def run_tests(github_sha, neon_test_branch): +@click.option('--base_ref_branch') +def run_tests(github_sha, neon_test_branch, base_ref_branch): os.environ["EVM_LOADER_IMAGE"] = f"{IMAGE_NAME}:{github_sha}" - if neon_test_branch in GithubClient.get_branches_list(NEON_TESTS_ENDPOINT) \ + if GithubClient.is_branch_exist(NEON_TESTS_ENDPOINT, neon_test_branch) \ and neon_test_branch not in ('master', 'develop'): neon_test_image_tag = neon_test_branch + elif re.match(VERSION_BRANCH_TEMPLATE, base_ref_branch): # PR to version branch + neon_test_image_tag = base_ref_branch else: neon_test_image_tag = 'latest' os.environ["NEON_TESTS_IMAGE"] = f"{NEON_TEST_IMAGE_NAME}:{neon_test_image_tag}" - + click.echo(f"NEON_TESTS_IMAGE: {os.environ['NEON_TESTS_IMAGE']}") project_name = f"neon-evm-{github_sha}" stop_containers(project_name) diff --git a/.github/workflows/github_api_client.py b/.github/workflows/github_api_client.py index 198a4892a..3aa3fd850 100644 --- a/.github/workflows/github_api_client.py +++ b/.github/workflows/github_api_client.py @@ -42,6 +42,15 @@ def get_branches_list(endpoint): proxy_branches_obj = requests.get( f"{endpoint}/branches?per_page=100").json() return [item["name"] for item in proxy_branches_obj] + @staticmethod + def is_branch_exist(endpoint, branch): + if branch: + response = requests.get(f"{endpoint}/branches/{branch}") + if response.status_code == 200: + click.echo(f"The branch {branch} exist in the {endpoint} repository") + return True + else: + return False def get_proxy_run_info(self, id): response = requests.get( diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index 0418a9cf7..e1e58d6e3 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -64,7 +64,7 @@ jobs: elif [[ "${{ steps.tag_creation.outputs.base_branch }}" != "" ]]; then # tag creation tag=${{ steps.tag_creation.outputs.base_branch }} - elif [[ "${{ github.head_ref }}" != "" ]]; then # pr to feature or version branch + elif [[ "${{ github.head_ref }}" != "" ]]; then # pr to feature or version branch or develop tag=${{ github.head_ref }} else tag='develop' @@ -75,7 +75,8 @@ jobs: run: | python3 ./.github/workflows/deploy.py run_tests \ --github_sha=${GITHUB_SHA} \ - --neon_test_branch=${{ steps.neon_test_branch.outputs.value }} + --neon_test_branch=${{ steps.neon_test_branch.outputs.value }} \ + --base_ref_branch=${{ github.base_ref }} trigger-proxy-tests: runs-on: trigger-runner needs: diff --git a/Dockerfile b/Dockerfile index 04bf9c5c8..75f4656c4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 @@ -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 \ diff --git a/ci/solana-run-neon.sh b/ci/solana-run-neon.sh index 8f58a1122..0deda3f77 100755 --- a/ci/solana-run-neon.sh +++ b/ci/solana-run-neon.sh @@ -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 @@ -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) From 505443d447914b626ecb48bd3ee4f5d3caa3278c Mon Sep 17 00:00:00 2001 From: Semen Medvedev Date: Fri, 23 Feb 2024 14:47:20 +0700 Subject: [PATCH 7/8] Remove usage syscalls in new methods --- evm_loader/program/src/instruction/transaction_execute.rs | 2 +- .../instruction/transaction_execute_from_account_solana_call.rs | 2 +- .../transaction_execute_from_instruction_solana_call.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evm_loader/program/src/instruction/transaction_execute.rs b/evm_loader/program/src/instruction/transaction_execute.rs index 41a666725..781411d83 100644 --- a/evm_loader/program/src/instruction/transaction_execute.rs +++ b/evm_loader/program/src/instruction/transaction_execute.rs @@ -93,7 +93,7 @@ pub fn execute_with_solana_call( return Err(Error::OutOfGas(gas_limit, used_gas)); } - solana_program::log::sol_log_data(&[b"GAS", &used_gas.to_le_bytes(), &used_gas.to_le_bytes()]); + log_data(&[b"GAS", &used_gas.to_le_bytes(), &used_gas.to_le_bytes()]); let gas_cost = used_gas.saturating_mul(gas_price); account_storage.transfer_gas_payment(origin, chain_id, gas_cost)?; diff --git a/evm_loader/program/src/instruction/transaction_execute_from_account_solana_call.rs b/evm_loader/program/src/instruction/transaction_execute_from_account_solana_call.rs index 805e0db72..95fa8019a 100644 --- a/evm_loader/program/src/instruction/transaction_execute_from_account_solana_call.rs +++ b/evm_loader/program/src/instruction/transaction_execute_from_account_solana_call.rs @@ -13,7 +13,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Execute Transaction from Account with Solana call"); + log_msg!("Instruction: Execute Transaction from Account with Solana call"); let treasury_index = u32::from_le_bytes(*array_ref![instruction, 0, 4]); diff --git a/evm_loader/program/src/instruction/transaction_execute_from_instruction_solana_call.rs b/evm_loader/program/src/instruction/transaction_execute_from_instruction_solana_call.rs index 3b3ecc8a1..3bae7620f 100644 --- a/evm_loader/program/src/instruction/transaction_execute_from_instruction_solana_call.rs +++ b/evm_loader/program/src/instruction/transaction_execute_from_instruction_solana_call.rs @@ -13,7 +13,7 @@ pub fn process<'a>( accounts: &'a [AccountInfo<'a>], instruction: &[u8], ) -> Result<()> { - solana_program::msg!("Instruction: Execute Transaction from Instruction with Solana call"); + log_msg!("Instruction: Execute Transaction from Instruction with Solana call"); let treasury_index = u32::from_le_bytes(*array_ref![instruction, 0, 4]); let messsage = &instruction[4..]; From 4c3a26b9d463a1079510333baf0cd3180de5e7d8 Mon Sep 17 00:00:00 2001 From: Anton Lisanin Date: Fri, 23 Feb 2024 09:30:20 +0300 Subject: [PATCH 8/8] Fix account migration from before revision (#285) --- evm_loader/lib/src/account_update.rs | 13 ++++++++----- evm_loader/program/src/account/legacy/update.rs | 7 +++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/evm_loader/lib/src/account_update.rs b/evm_loader/lib/src/account_update.rs index 9927fb020..a51dda7b2 100644 --- a/evm_loader/lib/src/account_update.rs +++ b/evm_loader/lib/src/account_update.rs @@ -13,13 +13,16 @@ fn from_before_revision(account: &mut Account, new_tag: u8) { const PREFIX_BEFORE: usize = ACCOUNT_PREFIX_LEN_BEFORE_REVISION; const PREFIX_AFTER: usize = ACCOUNT_PREFIX_LEN; - assert!(account.data.len() > PREFIX_BEFORE); + let data: &mut Vec = &mut account.data; - let required_len = account.data.len() + PREFIX_AFTER - PREFIX_BEFORE; - account.data.resize(required_len, 0); + assert!(data.len() > PREFIX_BEFORE); + let data_len = data.len() - PREFIX_BEFORE; - account.data.copy_within(PREFIX_BEFORE.., PREFIX_AFTER); - account.data[0] = new_tag; + let required_len = data.len() + PREFIX_AFTER - PREFIX_BEFORE; + data.resize(required_len, 0); + + data.copy_within(PREFIX_BEFORE..(PREFIX_BEFORE + data_len), PREFIX_AFTER); + data[0] = new_tag; } pub fn update_account(program_id: &Pubkey, key: &Pubkey, account: &mut Account) -> NeonResult<()> { diff --git a/evm_loader/program/src/account/legacy/update.rs b/evm_loader/program/src/account/legacy/update.rs index 19670df64..27e9ff817 100644 --- a/evm_loader/program/src/account/legacy/update.rs +++ b/evm_loader/program/src/account/legacy/update.rs @@ -17,18 +17,21 @@ pub fn from_before_revision<'a>( const PREFIX_BEFORE: usize = ACCOUNT_PREFIX_LEN_BEFORE_REVISION; const PREFIX_AFTER: usize = ACCOUNT_PREFIX_LEN; + assert!(account.data_len() > PREFIX_BEFORE); + let data_len = account.data_len() - PREFIX_BEFORE; + let required_len = account.data_len() + PREFIX_AFTER - PREFIX_BEFORE; account.realloc(required_len, false)?; - let minimum_balance = rent.minimum_balance(account.data_len()); + let minimum_balance = rent.minimum_balance(required_len); if account.lamports() < minimum_balance { let required_lamports = minimum_balance - account.lamports(); system.transfer(operator, account, required_lamports)?; } let mut account_data = account.try_borrow_mut_data()?; + account_data.copy_within(PREFIX_BEFORE..(PREFIX_BEFORE + data_len), PREFIX_AFTER); account_data[0] = new_tag; - account_data.copy_within(PREFIX_BEFORE.., PREFIX_AFTER); Ok(()) }