Skip to content

Commit

Permalink
NDEV-1837 Solana composability (#251)
Browse files Browse the repository at this point in the history
* NDEV-1837 Call solana programs

* NDEV-1837 Implement instruction to execute transaction with call solana programs

* NDEV-1837 Add emulation of calls to external Solana programs

* NDEV-1837 Extract SolanaEmulator to separate file

* NDEV-1837 Add solana_emulator to API/CLI (multithread environment)

* NDEV-1837 Fix format

* NDEV-1837 Fix clippy errors

* NDEV-1837 Format fix

* NDEV-1837 Use solana_emulator to get config from historical EvmLoader

* NDEV-1837 Review fixes

* NDEV-1837 Review fixes

* NDEV-1837 Format after review fixes

* NDEV-1837 Implement charging payer account before executing external instruction

* NDEV-1837 Fix tests

* Update call_solana.sol

* Update mod.rs

* NDEV-1837 Fix review comments

* update version to 1.9.0-dev

* NDEV-1837 Implement deserialize instruction in Solidity form

* NDEV-1837 Implement return_data

* NDEV-1837 Fix `external_solana_calls` field inside EmulatorState

* NDEV-1837 Fix format

---------

Co-authored-by: Semen Medvedev <[email protected]>
Co-authored-by: OlehSyzonov <[email protected]>
  • Loading branch information
3 people authored Jan 24, 2024
1 parent 9b55e5e commit 1efefb6
Show file tree
Hide file tree
Showing 40 changed files with 3,613 additions and 1,345 deletions.
7 changes: 5 additions & 2 deletions evm_loader/Cargo.lock

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

12 changes: 9 additions & 3 deletions evm_loader/api/src/api_server/state.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::Config;
use neon_lib::rpc::{CallDbClient, CloneRpcClient, RpcEnum};
use neon_lib::solana_emulator::init_solana_emulator;
use neon_lib::types::TracerDb;
use neon_lib::NeonError;

Expand All @@ -10,10 +11,15 @@ pub struct State {
}

impl State {
pub fn new(config: Config) -> Self {
pub async fn new(config: Config) -> Self {
let db_config = config.db_config.as_ref().expect("db-config not found");
let rpc_client = config.build_clone_solana_rpc_client();
let _solana_emulator = init_solana_emulator(config.evm_loader, &rpc_client).await;
// let solana_emulator = SolanaEmulator::new(config.evm_loader, &rpc_client).await
// .expect("Create solana emulator");
Self {
tracer_db: TracerDb::new(config.db_config.as_ref().expect("db-config not found")),
rpc_client: config.build_clone_solana_rpc_client(),
tracer_db: TracerDb::new(db_config),
rpc_client,
config,
}
}
Expand Down
2 changes: 1 addition & 1 deletion evm_loader/api/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ async fn main() -> NeonApiResult<()> {

let config = config::create_from_api_config(&api_config)?;

let state: NeonApiState = Data::new(api_server::state::State::new(config));
let state: NeonApiState = Data::new(api_server::state::State::new(config).await);

let listener_addr = options
.value_of("host")
Expand Down
2 changes: 1 addition & 1 deletion evm_loader/cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "neon-cli"
version = "1.7.0-dev"
version = "1.9.0-dev"
authors = ["NeonLabs Maintainers <[email protected]>"]
edition = "2021"

Expand Down
7 changes: 5 additions & 2 deletions evm_loader/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use neon_lib::{
cancel_trx, collect_treasury, emulate, get_balance, get_config, get_contract, get_holder,
get_neon_elf, get_storage_at, init_environment, trace,
},
solana_emulator::init_solana_emulator,
types::{BalanceAddress, EmulateRequest},
Config,
};
Expand Down Expand Up @@ -161,7 +162,7 @@ async fn build_rpc(options: &ArgMatches<'_>, config: &Config) -> Result<RpcEnum,
.value_of("slot")
.map(|slot_str| slot_str.parse().expect("slot parse error"));

Ok(if let Some(slot) = slot {
let rpc_client = if let Some(slot) = slot {
RpcEnum::CallDbClient(
CallDbClient::new(
TracerDb::new(config.db_config.as_ref().expect("db-config not found")),
Expand All @@ -172,7 +173,9 @@ async fn build_rpc(options: &ArgMatches<'_>, config: &Config) -> Result<RpcEnum,
)
} else {
RpcEnum::CloneRpcClient(config.build_clone_solana_rpc_client())
})
};
init_solana_emulator(config.evm_loader, &rpc_client).await;
Ok(rpc_client)
}

fn print_result(result: &NeonCliResult) {
Expand Down
1 change: 1 addition & 0 deletions evm_loader/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ scroll = "0.11.0"
tokio = { version = "1", features = ["full"] }
clickhouse = "0.11.5"
tracing = "0.1"
maybe-async = "0.2.7"
async-trait = "0.1.73"
build-info = { version = "0.0.31", features = ["serde"] }
enum_dispatch = "0.3.12"
Expand Down
27 changes: 25 additions & 2 deletions evm_loader/lib/src/account_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ use evm_loader::types::Address;
use solana_sdk::rent::Rent;
use solana_sdk::system_program;
use solana_sdk::sysvar::{slot_hashes, Sysvar};
use std::collections::HashSet;
use std::collections::{BTreeMap, HashSet};
use std::{cell::RefCell, collections::HashMap, convert::TryInto, rc::Rc};

use crate::solana_emulator::get_solana_emulator;
use crate::{rpc::Rpc, NeonError};
use ethnum::U256;
use evm_loader::{
Expand All @@ -23,7 +24,13 @@ use evm_loader::{
use log::{debug, info, trace};
use serde::{Deserialize, Serialize};
use solana_client::client_error;
use solana_sdk::{account::Account, account_info::AccountInfo, pubkey, pubkey::Pubkey};
use solana_sdk::{
account::Account,
account_info::AccountInfo,
instruction::{AccountMeta, Instruction},
pubkey,
pubkey::Pubkey,
};

use crate::commands::get_config::{BuildConfigSimulator, ChainInfo};
use crate::tracing::{AccountOverride, AccountOverrides, BlockOverrides};
Expand Down Expand Up @@ -724,6 +731,22 @@ impl<T: Rpc> AccountStorage for EmulatorAccountStorage<'_, T> {
let info = account_info(address, &mut account);
action(&info)
}

async fn emulate_solana_call(
&self,
program_id: &Pubkey,
instruction_data: &[u8],
meta: &[AccountMeta],
accounts: &mut BTreeMap<Pubkey, OwnedAccountInfo>,
seeds: &[Vec<Vec<u8>>],
) -> evm_loader::error::Result<()> {
let instruction = Instruction::new_with_bytes(*program_id, instruction_data, meta.to_vec());
let solana_emulator = get_solana_emulator().await;
//let solana_emulator = self.solana_emulator.lock().expect("Lock solana_emulator");
solana_emulator
.emulate_solana_call(self, &instruction, accounts, seeds)
.await
}
}

/// Creates new instance of `AccountInfo` from `Account`.
Expand Down
22 changes: 16 additions & 6 deletions evm_loader/lib/src/commands/emulate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ 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::types::{EmulateRequest, TxParams};
use crate::{
account_storage::{EmulatorAccountStorage, SolanaAccount},
emulator_state::EmulatorState,
errors::NeonError,
NeonResult,
};
use evm_loader::evm::tracing::TracerType;
use evm_loader::{
config::{EVM_STEPS_MIN, PAYMENT_TO_TREASURE},
evm::{ExitStatus, Machine},
executor::{Action, ExecutorState},
executor::Action,
gasometer::LAMPORTS_PER_SIGNATURE,
};
use serde_with::{hex::Hex, serde_as};
Expand All @@ -27,6 +27,9 @@ use serde_with::{hex::Hex, serde_as};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmulateResponse {
pub exit_status: String,
pub external_solana_calls: bool,
pub reverts_before_solana_calls: bool,
pub reverts_after_solana_calls: bool,
#[serde_as(as = "Hex")]
pub result: Vec<u8>,
pub steps_executed: u64,
Expand All @@ -41,6 +44,9 @@ impl EmulateResponse {
let exit_status = ExitStatus::Revert(revert_message);
Self {
exit_status: exit_status.to_string(),
external_solana_calls: false,
reverts_before_solana_calls: false,
reverts_after_solana_calls: false,
result: exit_status.into_result().unwrap_or_default(),
steps_executed: 0,
used_gas: 0,
Expand Down Expand Up @@ -77,7 +83,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
}

Expand All @@ -94,8 +99,8 @@ async fn emulate_trx(
info!("origin: {:?}", origin);
info!("tx: {:?}", tx);

let (exit_status, actions, steps_executed) = {
let mut backend = ExecutorState::new(storage);
let (exit_status, actions, steps_executed, execute_status) = {
let mut backend = EmulatorState::new(storage);
let mut evm = match Machine::new(tx, origin, &mut backend, tracer).await {
Ok(evm) => evm,
Err(e) => return Ok(EmulateResponse::revert(e)),
Expand All @@ -106,8 +111,10 @@ async fn emulate_trx(
return Err(NeonError::TooManySteps);
}

let execute_status = backend.execute_status.clone();
let actions = backend.into_actions();
(result, actions, steps_executed)

(result, actions, steps_executed, execute_status)
};

storage.apply_actions(actions.clone()).await?;
Expand All @@ -130,6 +137,9 @@ async fn emulate_trx(

Ok(EmulateResponse {
exit_status: exit_status.to_string(),
external_solana_calls: execute_status.external_solana_calls,
reverts_before_solana_calls: execute_status.reverts_before_solana_calls,
reverts_after_solana_calls: execute_status.reverts_after_solana_calls,
steps_executed,
used_gas,
solana_accounts,
Expand Down
37 changes: 12 additions & 25 deletions evm_loader/lib/src/commands/get_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use base64::Engine;
use enum_dispatch::enum_dispatch;
use std::collections::BTreeMap;
use std::str::FromStr;
use tokio::sync::MutexGuard;

use serde::{Deserialize, Serialize};
use solana_program_test::{ProgramTest, ProgramTestContext};
use solana_sdk::{
account::{Account, AccountSharedData},
account_utils::StateMut,
Expand All @@ -21,10 +21,11 @@ use solana_sdk::{
use crate::{rpc::Rpc, NeonError, NeonResult};

use crate::rpc::{CallDbClient, CloneRpcClient};
use crate::solana_emulator::{get_solana_emulator, SolanaEmulator};
use serde_with::{serde_as, DisplayFromStr};
use solana_client::client_error::Result as ClientResult;
use solana_client::rpc_config::{RpcLargestAccountsConfig, RpcSimulateTransactionConfig};
use tokio::sync::{Mutex, MutexGuard, OnceCell};
use solana_program_test::ProgramTestContext;

#[derive(Debug, Serialize)]
pub enum Status {
Expand Down Expand Up @@ -85,20 +86,6 @@ impl CallDbClient {
}
}

async fn program_test_context() -> MutexGuard<'static, ProgramTestContext> {
static PROGRAM_TEST_CONTEXT: OnceCell<Mutex<ProgramTestContext>> = OnceCell::const_new();

async fn init_program_test_context() -> Mutex<ProgramTestContext> {
Mutex::new(ProgramTest::default().start_with_context().await)
}

PROGRAM_TEST_CONTEXT
.get_or_init(init_program_test_context)
.await
.lock()
.await
}

fn set_program_account(
program_test_context: &mut ProgramTestContext,
program_id: Pubkey,
Expand All @@ -118,7 +105,7 @@ fn set_program_account(

pub enum ConfigSimulator<'r> {
CloneRpcClient(Pubkey, &'r CloneRpcClient),
ProgramTestContext(Pubkey, MutexGuard<'static, ProgramTestContext>),
ProgramTestContext(Pubkey, MutexGuard<'static, SolanaEmulator>),
}

#[async_trait(?Send)]
Expand All @@ -139,15 +126,13 @@ impl BuildConfigSimulator for CallDbClient {
async fn build_config_simulator(&self, program_id: Pubkey) -> NeonResult<ConfigSimulator> {
let program_data = self.read_program_data_from_account(program_id).await?;

let mut program_test_context = program_test_context().await;
let mut emulator = get_solana_emulator().await;
let program_test_context = emulator.emulator_context.get_mut();
set_program_account(program_test_context, program_id, program_data);

set_program_account(&mut program_test_context, program_id, program_data);
program_test_context.get_new_latest_blockhash().await?;

Ok(ConfigSimulator::ProgramTestContext(
program_id,
program_test_context,
))
Ok(ConfigSimulator::ProgramTestContext(program_id, emulator))
}
}

Expand Down Expand Up @@ -262,8 +247,10 @@ impl ConfigSimulator<'_> {
.simulate_solana_instruction(instruction)
.await
}
ConfigSimulator::ProgramTestContext(_, program_test_context) => {
program_test_context
ConfigSimulator::ProgramTestContext(_, solana_emulator) => {
solana_emulator
.emulator_context
.get_mut()
.simulate_solana_instruction(instruction)
.await
}
Expand Down
Loading

0 comments on commit 1efefb6

Please sign in to comment.