use context::{ContextTr, Database, JournalTr};
use context_interface::{
journaled_state::{account::JournaledAccountTr, JournalCheckpoint, JournalLoadError},
Cfg, Transaction,
};
use interpreter::{
CallInput, CallInputs, CallScheme, CallValue, CreateInputs, CreateScheme, FrameInput,
GasTracker,
};
use primitives::TxKind;
use state::Bytecode;
use std::boxed::Box;
#[inline]
pub fn create_init_frame<CTX: ContextTr>(
ctx: &mut CTX,
gas: &mut GasTracker,
) -> Result<Option<FrameInput>, <<CTX::Journal as JournalTr>::Database as Database>::Error> {
let is_eip2780 = ctx.cfg().is_amsterdam_eip2780_enabled();
let params = ctx.cfg().gas_params();
let new_account_state_gas = params.new_account_state_gas();
let create_state_gas = params.create_state_gas();
let warm_access_cost = params.warm_storage_read_cost();
let cold_account_additional_cost = params.cold_account_additional_cost();
let (tx, journal) = ctx.tx_journal_mut();
let input = tx.input().clone();
match tx.kind() {
TxKind::Call(target_address) => {
let account = &journal.load_account_with_code(target_address)?.info;
let recipient_is_empty = account.is_empty();
let delegated_address = account.code.as_ref().and_then(Bytecode::eip7702_address);
let mut known_bytecode = (
account.code_hash(),
account.code.clone().unwrap_or_default(),
);
let mut charged_new_account_state_gas = false;
if is_eip2780 && !tx.value().is_zero() && recipient_is_empty {
if !gas.record_state_cost(new_account_state_gas) {
return Ok(None);
}
charged_new_account_state_gas = true;
}
if let Some(delegated_address) = delegated_address {
if is_eip2780 {
if !gas.record_regular_cost(warm_access_cost) {
return Ok(None);
}
let skip_cold_load = gas.remaining() < cold_account_additional_cost;
let acc = match journal.load_account_info_skip_cold_load(
delegated_address,
true,
skip_cold_load,
) {
Ok(acc) => acc,
Err(JournalLoadError::ColdLoadSkipped) => return Ok(None),
Err(JournalLoadError::DBError(e)) => return Err(e),
};
if acc.is_cold && !gas.record_regular_cost(cold_account_additional_cost) {
return Ok(None);
}
known_bytecode = (acc.code_hash(), acc.code.clone().unwrap_or_default());
} else {
let account = &journal.load_account_with_code(delegated_address)?.info;
known_bytecode = (
account.code_hash(),
account.code.clone().unwrap_or_default(),
);
}
}
Ok(Some(FrameInput::Call(Box::new(CallInputs {
input: CallInput::Bytes(input),
gas_limit: gas.remaining(),
target_address,
bytecode_address: target_address,
known_bytecode,
caller: tx.caller(),
value: CallValue::Transfer(tx.value()),
scheme: CallScheme::Call,
is_static: false,
return_memory_offset: 0..0,
reservoir: gas.reservoir(),
charged_new_account_state_gas,
}))))
}
TxKind::Create => {
let mut charged_create_state_gas = false;
if is_eip2780 {
let created_address = tx.caller().create(tx.nonce());
let target_is_empty = journal.load_account(created_address)?.info.is_empty();
if target_is_empty {
if !gas.record_state_cost(create_state_gas) {
return Ok(None);
}
charged_create_state_gas = true;
}
}
let mut inputs = CreateInputs::new(
tx.caller(),
CreateScheme::Create,
tx.value(),
input,
gas.remaining(),
gas.reservoir(),
);
inputs.set_charged_create_state_gas(charged_create_state_gas);
Ok(Some(FrameInput::Create(Box::new(inputs))))
}
}
}
#[inline]
pub fn runtime_oog_unwind<CTX: ContextTr>(
ctx: &mut CTX,
checkpoint: JournalCheckpoint,
) -> Result<(), <<CTX::Journal as JournalTr>::Database as Database>::Error> {
let (tx, journal) = ctx.tx_journal_mut();
journal.checkpoint_revert(checkpoint);
if tx.kind().is_create() {
journal.load_account_mut(tx.caller())?.data.bump_nonce();
}
Ok(())
}