Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EVM: Add support for access list creation for eth namespace RPC #2827

Merged
merged 16 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 67 additions & 52 deletions lib/ain-evm/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use crate::{
block::INITIAL_BASE_FEE,
blocktemplate::BlockTemplate,
eventlistener::ExecutionStep,
executor::{AinExecutor, ExecutorContext, TxResponse},
executor::{AccessListInfo, AinExecutor, ExecutorContext, TxResponse},
fee::calculate_max_prepay_gas_fee,
gas::check_tx_intrinsic_gas,
receipt::ReceiptService,
Expand Down Expand Up @@ -685,6 +685,42 @@ impl EVMCoreService {
)
}

pub fn get_backend_from_block(
&self,
block_number: U256,
caller: Option<H160>,
gas_price: Option<U256>,
overlay: Option<Overlay>,
) -> Result<EVMBackend> {
let block_header = self
.storage
.get_block_by_number(&block_number)?
.map(|block| block.header)
.ok_or(format_err!("Block number {:x?} not found", block_number))?;
let state_root = block_header.state_root;
debug!(
"Calling EVM at block number : {:#x}, state_root : {:#x}",
block_number, state_root
);

let mut vicinity = Vicinity::from(block_header);
if let Some(gas_price) = gas_price {
vicinity.gas_price = gas_price;
}
if let Some(caller) = caller {
vicinity.origin = caller;
}
debug!("Vicinity: {:?}", vicinity);

EVMBackend::from_root(
state_root,
Arc::clone(&self.trie_store),
Arc::clone(&self.storage),
vicinity,
overlay,
)
}

pub fn get_next_account_nonce(&self, address: H160, state_root: H256) -> Result<U256> {
let state_root_nonce = self.get_nonce(address, state_root)?;
let mut nonce_store = self.nonce_store.lock();
Expand Down Expand Up @@ -748,36 +784,9 @@ impl EVMCoreService {
access_list,
block_number,
} = arguments;
debug!("[call] caller: {:?}", caller);

let block_header = self
.storage
.get_block_by_number(&block_number)?
.map(|block| block.header)
.ok_or(format_err!(
"[call] Block number {:x?} not found",
block_number
))?;
let state_root = block_header.state_root;
debug!(
"Calling EVM at block number : {:#x}, state_root : {:#x}",
block_number, state_root
);

let mut vicinity = Vicinity::from(block_header);
vicinity.gas_price = gas_price;
vicinity.origin = caller;
debug!("[call] vicinity: {:?}", vicinity);

let mut backend = EVMBackend::from_root(
state_root,
Arc::clone(&self.trie_store),
Arc::clone(&self.storage),
vicinity,
overlay,
)
.map_err(|e| format_err!("Could not restore backend {}", e))?;

let mut backend = self
.get_backend_from_block(block_number, Some(caller), Some(gas_price), overlay)
.map_err(|e| format_err!("Could not restore backend {}", e))?;
Ok(AinExecutor::new(&mut backend).call(ExecutorContext {
caller,
to,
Expand All @@ -788,32 +797,38 @@ impl EVMCoreService {
}))
}

pub fn create_access_list(&self, arguments: EthCallArgs) -> Result<AccessListInfo> {
let EthCallArgs {
caller,
to,
value,
data,
gas_limit,
gas_price,
access_list,
block_number,
} = arguments;
let mut backend = self
.get_backend_from_block(block_number, Some(caller), Some(gas_price), None)
.map_err(|e| format_err!("Could not restore backend {}", e))?;
AinExecutor::new(&mut backend).exec_access_list(ExecutorContext {
caller,
to,
value,
data,
gas_limit,
access_list,
})
}

pub fn call_with_tracer(
&self,
tx: &SignedTx,
block_number: U256,
) -> Result<(Vec<ExecutionStep>, bool, Vec<u8>, u64)> {
let block_header = self
.storage
.get_block_by_number(&block_number)?
.ok_or_else(|| format_err!("Block not found"))
.map(|block| block.header)?;
let state_root = block_header.state_root;
debug!(
"Calling EVM at block number : {:#x}, state_root : {:#x}",
block_number, state_root
);

let vicinity = Vicinity::from(block_header);
let mut backend = EVMBackend::from_root(
state_root,
Arc::clone(&self.trie_store),
Arc::clone(&self.storage),
vicinity,
None,
)
.map_err(|e| format_err!("Could not restore backend {}", e))?;
backend.update_vicinity_from_tx(tx)?;
let mut backend = self
.get_backend_from_block(block_number, None, None, None)
.map_err(|e| format_err!("Could not restore backend {}", e))?;
AinExecutor::new(&mut backend).exec_with_tracer(tx)
}
}
24 changes: 22 additions & 2 deletions lib/ain-evm/src/eventlistener.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::VecDeque;
use std::collections::{HashMap, HashSet, VecDeque};

use crate::opcode;
use ethereum_types::H256;
use ethereum_types::{H160, H256};
use evm::gasometer::tracing::{Event as GasEvent, EventListener as GasEventListener};
use evm_runtime::{
tracing::{Event as RuntimeEvent, EventListener as RuntimeEventListener},
Expand Down Expand Up @@ -156,3 +156,23 @@ impl GasEventListener for GasListener {
}
}
}

#[derive(Default)]
pub struct StorageAccessListener {
pub access_list: HashMap<H160, HashSet<H256>>,
}

impl RuntimeEventListener for StorageAccessListener {
fn event(&mut self, event: RuntimeEvent<'_>) {
debug!("event runtime : {:#?}", event);
match event {
RuntimeEvent::SLoad { address, index, .. } => {
self.access_list.entry(address).or_default().insert(index);
}
RuntimeEvent::SStore { address, index, .. } => {
self.access_list.entry(address).or_default().insert(index);
}
_ => {}
}
}
}
95 changes: 93 additions & 2 deletions lib/ain-evm/src/executor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ain_contracts::{get_transfer_domain_contract, FixedContract};
use anyhow::format_err;
use ethereum::{AccessList, EIP658ReceiptData, Log, ReceiptV3};
use ethereum::{AccessList, AccessListItem, EIP658ReceiptData, Log, ReceiptV3};
use ethereum_types::{Bloom, H160, H256, U256};
use evm::{
backend::{ApplyBackend, Backend},
Expand All @@ -20,7 +20,7 @@ use crate::{
dst20_deploy_info, DST20BridgeInfo, DeployContractInfo,
},
core::EVMCoreService,
eventlistener::{ExecListener, ExecutionStep, GasListener},
eventlistener::{ExecListener, ExecutionStep, GasListener, StorageAccessListener},
fee::{calculate_current_prepay_gas_fee, calculate_gas_fee},
precompiles::MetachainPrecompiles,
transaction::{
Expand All @@ -42,6 +42,11 @@ impl From<SignedTx> for ExecuteTx {
}
}

pub struct AccessListInfo {
pub access_list: AccessList,
pub gas_used: U256,
}

#[derive(Debug)]
pub struct ExecutorContext<'a> {
pub caller: H160,
Expand All @@ -56,6 +61,7 @@ pub struct AinExecutor<'backend> {
pub backend: &'backend mut EVMBackend,
}

// State update methods
impl<'backend> AinExecutor<'backend> {
pub fn new(backend: &'backend mut EVMBackend) -> Self {
Self { backend }
Expand Down Expand Up @@ -95,6 +101,7 @@ impl<'backend> AinExecutor<'backend> {
}
}

// EVM executor methods
impl<'backend> AinExecutor<'backend> {
const CONFIG: Config = Config::shanghai();

Expand Down Expand Up @@ -249,6 +256,7 @@ impl<'backend> AinExecutor<'backend> {
))
}

/// Execute tx with tracer
pub fn exec_with_tracer(
&self,
signed_tx: &SignedTx,
Expand Down Expand Up @@ -309,6 +317,89 @@ impl<'backend> AinExecutor<'backend> {
Ok((listener.trace, exec_flag, data, used_gas))
}

/// Execute tx with storage access listener
pub fn exec_access_list(&self, ctx: ExecutorContext) -> Result<AccessListInfo> {
let access_list = ctx
.access_list
.into_iter()
.map(|x| (x.address, x.storage_keys))
.collect::<Vec<_>>();

let metadata = StackSubstateMetadata::new(ctx.gas_limit, &Self::CONFIG);
let state = MemoryStackState::new(metadata.clone(), self.backend);
let al_state = MemoryStackState::new(metadata, self.backend);
let precompiles = MetachainPrecompiles;
let mut al_executor =
StackExecutor::new_with_precompiles(al_state, &Self::CONFIG, &precompiles);
let mut executor = StackExecutor::new_with_precompiles(state, &Self::CONFIG, &precompiles);
let mut listener = StorageAccessListener::default();

let (exit_reason, _) = runtime_using(&mut listener, move || match ctx.to {
Some(to) => executor.transact_call(
ctx.caller,
to,
ctx.value,
ctx.data.to_vec(),
ctx.gas_limit,
access_list,
),
None => executor.transact_create(
ctx.caller,
ctx.value,
ctx.data.to_vec(),
ctx.gas_limit,
access_list,
),
});
if !exit_reason.is_succeed() {
return Err(format_err!("[exec_access_list] tx execution failed").into());
}

// Get access list from listener
let al: AccessList = listener
.access_list
.into_iter()
.map(|(address, storage_keys)| AccessListItem {
address,
storage_keys: Vec::from_iter(storage_keys),
})
.collect();
let access_list = al
.clone()
.into_iter()
.map(|x| (x.address, x.storage_keys))
.collect::<Vec<_>>();

// Get gas usage with accumulated access list
let (exit_reason, _) = match ctx.to {
Some(to) => al_executor.transact_call(
ctx.caller,
to,
ctx.value,
ctx.data.to_vec(),
ctx.gas_limit,
access_list,
),
None => al_executor.transact_create(
ctx.caller,
ctx.value,
ctx.data.to_vec(),
ctx.gas_limit,
access_list,
),
};
if !exit_reason.is_succeed() {
return Err(format_err!("[exec_access_list] tx execution failed").into());
}
Ok(AccessListInfo {
access_list: al,
gas_used: U256::from(al_executor.used_gas()),
})
}
}

impl<'backend> AinExecutor<'backend> {
/// System tx execution
pub fn execute_tx(&mut self, tx: ExecuteTx, base_fee: U256) -> Result<ApplyTxResult> {
match tx {
ExecuteTx::SignedTx(signed_tx) => {
Expand Down
20 changes: 18 additions & 2 deletions lib/ain-grpc/src/call_request.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::{BTreeMap, HashMap};

use ain_evm::{backend::Overlay, bytes::Bytes};
use ethereum::{AccessListItem, Account};
use ain_evm::{backend::Overlay, bytes::Bytes, executor::AccessListInfo};
use ethereum::{AccessList, AccessListItem, Account};
use ethereum_types::{H160, H256, U256};
use jsonrpsee::core::Error;
use serde::Deserialize;
Expand Down Expand Up @@ -173,3 +173,19 @@ pub fn override_to_overlay(r#override: BTreeMap<H160, CallStateOverride>) -> Ove

overlay
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct AccessListResult {
pub access_list: AccessList,
pub gas_used: U256,
}

impl From<AccessListInfo> for AccessListResult {
fn from(value: AccessListInfo) -> Self {
Self {
access_list: value.access_list,
gas_used: value.gas_used,
}
}
}
3 changes: 1 addition & 2 deletions lib/ain-grpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ pub mod logging;
mod logs;
mod receipt;
pub mod rpc;
mod subscription;
mod sync;
mod transaction;
mod transaction_request;
mod utils;

mod subscription;

#[cfg(test)]
mod tests;

Expand Down
Loading
Loading