use crate::{
frame::EthFrame, instructions::InstructionProvider, ExecuteCommitEvm, ExecuteEvm, Handler,
MainnetHandler, PrecompileProvider,
};
use context::{result::ExecResultAndState, ContextSetters, ContextTr, Evm, JournalTr, TxEnv};
use database_interface::DatabaseCommit;
use interpreter::{interpreter::EthInterpreter, InterpreterResult};
use primitives::{address, Address, Bytes, TxKind};
use state::EvmState;
pub const SYSTEM_ADDRESS: Address = address!("0xfffffffffffffffffffffffffffffffffffffffe");
pub trait SystemCallTx: Sized {
fn new_system_tx(system_contract_address: Address, data: Bytes) -> Self {
Self::new_system_tx_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
}
fn new_system_tx_with_caller(
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Self;
}
impl SystemCallTx for TxEnv {
fn new_system_tx_with_caller(
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Self {
TxEnv::builder()
.caller(caller)
.data(data)
.kind(TxKind::Call(system_contract_address))
.gas_limit(30_000_000)
.build()
.unwrap()
}
}
pub trait SystemCallEvm: ExecuteEvm {
fn system_call_one_with_caller(
&mut self,
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error>;
fn system_call_one(
&mut self,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error> {
self.system_call_one_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
}
fn system_call(
&mut self,
system_contract_address: Address,
data: Bytes,
) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
self.system_call_with_caller(SYSTEM_ADDRESS, system_contract_address, data)
}
fn system_call_with_caller(
&mut self,
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
let result = self.system_call_one_with_caller(caller, system_contract_address, data)?;
let state = self.finalize();
Ok(ExecResultAndState::new(result, state))
}
#[deprecated(since = "0.1.0", note = "Use `system_call_one_with_caller` instead")]
fn transact_system_call_with_caller(
&mut self,
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error> {
self.system_call_one_with_caller(caller, system_contract_address, data)
}
#[deprecated(since = "0.1.0", note = "Use `system_call_one` instead")]
fn transact_system_call(
&mut self,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error> {
self.system_call_one(system_contract_address, data)
}
#[deprecated(since = "0.1.0", note = "Use `system_call` instead")]
fn transact_system_call_finalize(
&mut self,
system_contract_address: Address,
data: Bytes,
) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
self.system_call(system_contract_address, data)
}
#[deprecated(since = "0.1.0", note = "Use `system_call_with_caller` instead")]
fn transact_system_call_with_caller_finalize(
&mut self,
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Result<ExecResultAndState<Self::ExecutionResult, Self::State>, Self::Error> {
self.system_call_with_caller(caller, system_contract_address, data)
}
}
pub trait SystemCallCommitEvm: SystemCallEvm + ExecuteCommitEvm {
fn system_call_commit(
&mut self,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error> {
self.system_call_with_caller_commit(SYSTEM_ADDRESS, system_contract_address, data)
}
#[deprecated(since = "0.1.0", note = "Use `system_call_commit` instead")]
fn transact_system_call_commit(
&mut self,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error> {
self.system_call_commit(system_contract_address, data)
}
fn system_call_with_caller_commit(
&mut self,
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error>;
#[deprecated(since = "0.1.0", note = "Use `system_call_with_caller_commit` instead")]
fn transact_system_call_with_caller_commit(
&mut self,
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error> {
self.system_call_with_caller_commit(caller, system_contract_address, data)
}
}
impl<CTX, INSP, INST, PRECOMPILES> SystemCallEvm
for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
where
CTX: ContextTr<Journal: JournalTr<State = EvmState>, Tx: SystemCallTx> + ContextSetters,
INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
{
fn system_call_one_with_caller(
&mut self,
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error> {
self.set_tx(CTX::Tx::new_system_tx_with_caller(
caller,
system_contract_address,
data,
));
MainnetHandler::default().run_system_call(self)
}
}
impl<CTX, INSP, INST, PRECOMPILES> SystemCallCommitEvm
for Evm<CTX, INSP, INST, PRECOMPILES, EthFrame<EthInterpreter>>
where
CTX: ContextTr<Journal: JournalTr<State = EvmState>, Db: DatabaseCommit, Tx: SystemCallTx>
+ ContextSetters,
INST: InstructionProvider<Context = CTX, InterpreterTypes = EthInterpreter>,
PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
{
fn system_call_with_caller_commit(
&mut self,
caller: Address,
system_contract_address: Address,
data: Bytes,
) -> Result<Self::ExecutionResult, Self::Error> {
self.system_call_with_caller(caller, system_contract_address, data)
.map(|output| {
self.db_mut().commit(output.state);
output.result
})
}
}
#[cfg(test)]
mod tests {
use crate::{MainBuilder, MainContext};
use super::*;
use context::{
result::{ExecutionResult, Output, ResultGas, SuccessReason},
Context, Transaction,
};
use database::InMemoryDB;
use primitives::{b256, bytes, StorageKey, U256};
use state::{AccountInfo, Bytecode};
const HISTORY_STORAGE_ADDRESS: Address = address!("0x0000F90827F1C53a10cb7A02335B175320002935");
static HISTORY_STORAGE_CODE: Bytes = bytes!("0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500");
#[test]
fn test_system_call() {
let mut db = InMemoryDB::default();
db.insert_account_info(
HISTORY_STORAGE_ADDRESS,
AccountInfo::default().with_code(Bytecode::new_legacy(HISTORY_STORAGE_CODE.clone())),
);
let block_hash =
b256!("0x1111111111111111111111111111111111111111111111111111111111111111");
let mut evm = Context::mainnet()
.with_db(db)
.modify_block_chained(|b| b.number = U256::ONE)
.build_mainnet();
let output = evm
.system_call(HISTORY_STORAGE_ADDRESS, block_hash.0.into())
.unwrap();
assert_eq!(evm.ctx.tx().gas_limit(), 30_000_000);
assert_eq!(
output.result,
ExecutionResult::Success {
reason: SuccessReason::Stop,
gas: ResultGas::default().with_total_gas_spent(22143),
logs: vec![],
output: Output::Call(Bytes::default())
}
);
assert_eq!(output.state.len(), 1);
assert_eq!(
output.state[&HISTORY_STORAGE_ADDRESS]
.storage
.get(&StorageKey::from(0))
.map(|slot| slot.present_value)
.unwrap_or_default(),
U256::from_be_bytes(block_hash.0),
"State is not updated {:?}",
output.state
);
}
}