ExecuteExecute阶段是整个流程中最重要的Execute所有交易将在这里执行直接跳到源码实现的部分参数二init_and_floor_gas是Validate阶段的返回值是交易的“初始化gas消耗+地板gas限制”gas_limit=tx设置的Gas
Execute 阶段是整个流程中最重要的 Execute\ 所有交易将在这里执行
直接跳到源码实现的部分\ 参数二 init_and_floor_gas 是 Validate 阶段的返回值\ 是交易的“初始化 gas 消耗 + 地板 gas 限制”\ gas_limit = tx设置的GasLimit- init_and_floor_gas.initial_gas
// crates/handler/src/handler.rs
#[inline]
fn execution(
&mut self,
evm: &mut Self::Evm,
init_and_floor_gas: &InitialAndFloorGas,
) -> Result<FrameResult, Self::Error> {
let gas_limit = evm.ctx().tx().gas_limit() - init_and_floor_gas.initial_gas;
// Create first frame action
let first_frame_input = self.first_frame_input(evm, gas_limit)?;
// Run execution loop
let mut frame_result = self.run_exec_loop(evm, first_frame_input)?;
// Handle last frame result
self.last_frame_result(evm, &mut frame_result)?;
Ok(frame_result)
}
使用交易参数、gas限制和配置创建初始帧输入。\ 直接跳到代码实现部分
// crates/handler/src/handler.rs
#[inline]
fn first_frame_input(
&mut self,
evm: &mut Self::Evm,
gas_limit: u64,
) -> Result<FrameInit, Self::Error> {
let ctx = evm.ctx_mut();
let mut memory = SharedMemory::new_with_buffer(ctx.local().shared_memory_buffer().clone());
memory.set_memory_limit(ctx.cfg().memory_limit());
let (tx, journal) = ctx.tx_journal_mut();
let bytecode = if let Some(&to) = tx.kind().to() {
let account = &journal.load_account_with_code(to)?.info;
if let Some(Bytecode::Eip7702(eip7702_bytecode)) = &account.code {
let delegated_address = eip7702_bytecode.delegated_address;
let account = &journal.load_account_with_code(delegated_address)?.info;
Some((
account.code.clone().unwrap_or_default(),
account.code_hash(),
))
} else {
Some((
account.code.clone().unwrap_or_default(),
account.code_hash(),
))
}
} else {
None
};
Ok(FrameInit {
depth: 0,
memory,
frame_input: execution::create_init_frame(tx, bytecode, gas_limit),
})
}
ctx.local() 返回的是 LocalContext, 作用是为当前交易(tx)提供一个“本地、临时的、共享的上下文缓存”,用来在整个交易执行过程中(包括多层调用)复用一些资源和状态,避免重复分配和拷贝,提高性能,同时处理一些特殊场景(如 initcode 交易、precompile 错误传递.
SharedMemory::new_with_buffer 就是在外面加一层封装,把 Rc<RefCell<Vec<u8>>>,也就是ctx.local().shared_memory_buffer() 包装成一个更安全、更易用、支持预分配的共享内存对象,目的是让交易执行中的内存拷贝和切片操作更高效、更不容易出错,同时保持整个 tx 共用一个缓冲区的性能优势。
SharedMemory 和 LocalContext 我也还没细看,上面是 Grok 解释的.\ 先走完流程,后面再在后面单独的章节介绍
tx.kind 之前介绍,分为 Create 和 Call.\ 创建合约属于 Create, 调用合约 和 Transfer 属于Call\ EIP-4844和EIP-7702 只能 Call, 不能 Create\ 这里获取tx的to
if let Some(&to) = tx.kind().to()
获取to的code,判断是不是 EIP7702\ 如果是的话找出被委托地址再加载被委托地址的code
if let Some(Bytecode::Eip7702(eip7702_bytecode)) = &account.code {
let delegated_address = eip7702_bytecode.delegated_address;
let account = &journal.load_account_with_code(delegated_address)?.info;
Some((
account.code.clone().unwrap_or_default(),
account.code_hash(),
))
}
如果不是 EIP7702 , 说明是合约,直接获取合约的code
else {
Some((
account.code.clone().unwrap_or_default(),
account.code_hash(),
))
}
这里先介绍下Frame\ Frame 是调用帧(Call Frame),在EVM执行过程每次合约间的调用(不是函数调用),例如CALL、DELEGATECALL 、 STATICCALL 、CREATE,都会生成一个Frame,里面包含本次调用的上下文.\ 后面还会有一个 frame_stack, 负责保存所有生成的frame
first_frame_input 的返回结果\ depth 指的是frame调用深度,例如A合约调用B合约,再调用C合约,C合约的frame depth就是2
FrameInit {
depth: 0,
memory,
frame_input: execution::create_init_frame(tx, bytecode, gas_limit),
}
create_init_frame 也没有什么要特别讲的,返回一个FrameInput类型
// crates/handler/src/execution.rs
#[inline]
pub fn create_init_frame(
tx: &impl Transaction,
bytecode: Option<(Bytecode, B256)>,
gas_limit: u64,
) -> FrameInput {
let input = tx.input().clone();
match tx.kind() {
TxKind::Call(target_address) => {
let known_bytecode = bytecode.map(|(code, hash)| (hash, code));
FrameInput::Call(Box::new(CallInputs {
input: CallInput::Bytes(input),
gas_limit,
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,
}))
}
TxKind::Create => FrameInput::Create(Box::new(CreateInputs::new(
tx.caller(),
CreateScheme::Create,
tx.value(),
input,
gas_limit,
))),
}
}
注释中提到的 run_exec_loop 的功能:\ 执行主帧处理循环,此循环管理帧堆栈,处理每一帧直至执行完成。\ 每次迭代:
直接看代码吧\ 输入参数是 EVM 和 上一步 first_frame_input 返回的参数 FrameInit
// crates/handler/src/handler.rs
#[inline]
fn run_exec_loop(
&mut self,
evm: &mut Self::Evm,
first_frame_input: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameInit,
) -> Result<FrameResult, Self::Error> {
let res = evm.frame_init(first_frame_input)?;
if let ItemOrResult::Result(frame_result) = res {
return Ok(frame_result);
}
loop {
let call_or_result = evm.frame_run()?;
let result = match call_or_result {
ItemOrResult::Item(init) => {
match evm.frame_init(init)? {
ItemOrResult::Item(_) => {
continue;
}
// Do not pop the frame since no new frame was created
ItemOrResult::Result(result) => result,
}
}
ItemOrResult::Result(result) => result,
};
if let Some(result) = evm.frame_return_result(result)? {
return Ok(result);
}
}
}
先看 frame_init 的实现
// crates/handler/src/evm.rs
#[inline]
fn frame_init(
&mut self,
frame_input: <Self::Frame as FrameTr>::FrameInit,
) -> Result<FrameInitResult<'_, Self::Frame>, ContextDbError<CTX>> {
let is_first_init = self.frame_stack.index().is_none();
let new_frame = if is_first_init {
self.frame_stack.start_init()
} else {
self.frame_stack.get_next()
};
let ctx = &mut self.ctx;
let precompiles = &mut self.precompiles;
let res = Self::Frame::init_with_context(
new_frame,
ctx,
precompiles,
frame_input,
self.instruction.gas_params(),
)?;
Ok(res.map_frame(|token| {
if is_first_init {
unsafe { self.frame_stack.end_init(token) };
} else {
unsafe { self.frame_stack.push(token) };
}
self.frame_stack.get()
}))
}
根据 frame_stack.index 判断是否是第一次初始化.\ 分别调用 start_init 和 get_next
start_init
get_next
out_frame_at 返回的是 传入参数 位置的元素引用
// crates/context/interface/src/local.rs
#[inline]
pub fn start_init(&mut self) -> OutFrame<'_, T> {
self.index = None;
if self.stack.is_empty() {
self.stack.reserve(8);
}
self.out_frame_at(0)
}
#[inline]
pub fn get_next(&mut self) -> OutFrame<'_, T> {
if self.index.unwrap() + 1 == self.stack.capacity() {
// allocate 8 more items
self.stack.reserve(8);
}
self.out_frame_at(self.index.unwrap() + 1)
}
继续往下,主要关注 init_with_context
init_with_context的 作用是使用给定的上下文初始化帧并进行预编译。\ 跳转到到实现部分.先说明一下传入的参数:
// crates/handler/src/frame.rs
pub fn init_with_context<
CTX: ContextTr,
PRECOMPILES: PrecompileProvider<CTX, Output = InterpreterResult>,
>(
this: OutFrame<'_, Self>,
ctx: &mut CTX,
precompiles: &mut PRECOMPILES,
frame_init: FrameInit,
gas_params: GasParams,
) -> Result<
ItemOrResult<FrameToken, FrameResult>,
ContextError<<<CTX as ContextTr>::Db as Database>::Error>,
> {
// TODO cleanup inner make functions
let FrameInit {
depth,
memory,
frame_input,
} = frame_init;
match frame_input {
FrameInput::Call(inputs) => {
Self::make_call_frame(this, ctx, precompiles, depth, memory, inputs, gas_params)
}
FrameInput::Create(inputs) => {
Self::make_create_frame(this, ctx, depth, memory, inputs, gas_params)
}
FrameInput::Empty => unreachable!(),
}
}
}
根据 frame_input 类型来生成不同的Frame, frame_input 是 first_frame_input 返回的结果里的
函数太长,这次就不贴了\ 先讲下函数参数,只讲 init_with_context 没有的部分
继续看函数实现\ 这里是创建一个闭包用于返回错误结果
// crates/handler/src/frame.rs -> make_call_frame
let return_result = |instruction_result: InstructionResult| {
Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
result: InterpreterResult {
result: instruction_result,
gas,
output: Bytes::new(),
},
memory_offset: inputs.return_memory_offset.clone(),
was_precompile_called: false,
precompile_call_logs: Vec::new(),
})))
};
创建检查点,用于后面执行出错的时候返回到检查点.
let checkpoint = ctx.journal_mut().checkpoint();
跳进去看下checkpoint的实现\ 创建一个JournalCheckpoint并返回而已\ 后面我们再讲 Revert 的原理\ 这里的 depth 和之前说到的 depth 是一个东西,都是合约间到了第几层调用.\ 不过这里 depth 是一个全局状态
// crates/context/src/journal/inner.rs
#[inline]
pub fn checkpoint(&mut self) -> JournalCheckpoint {
let checkpoint = JournalCheckpoint {
log_i: self.logs.len(),
journal_i: self.journal.len(),
};
self.depth += 1;
checkpoint
}
接着往下,这里是如果tx中有value, 则提前扣除 from 的 Balance, 增加 to 的 Balance
// crates/handler/src/frame.rs -> make_call_frame
if let CallValue::Transfer(value) = inputs.value {
// Transfer value from caller to called account
// Target will get touched even if balance transferred is zero.
if let Some(i) =
ctx.journal_mut()
.transfer_loaded(inputs.caller, inputs.target_address, value)
{
ctx.journal_mut().checkpoint_revert(checkpoint);
return return_result(i.into());
}
}
进去看下 transfer_loaded 的实现,直接在注释中说明,就不一一说了
// crates/context/src/journal/inner.rs
#[inline]
pub fn transfer_loaded(
&mut self,
from: Address,
to: Address,
balance: U256,
) -> Option<TransferError> {
// 给自己转账的情况
if from == to {
let from_balance = self.state.get_mut(&to).unwrap().info.balance;
// Check if from balance is enough to transfer the balance.
if balance > from_balance {
return Some(TransferError::OutOfFunds);
}
return None;
}
// tx.value为0的情况
// touch_account 标记 account 为touched,方便在交易结束后处理账户状态
if balance.is_zero() {
Self::touch_account(&mut self.journal, to, self.state.get_mut(&to).unwrap());
return None;
}
// 扣除 from 的 balance
let from_account = self.state.get_mut(&from).unwrap();
Self::touch_account(&mut self.journal, from, from_account);
let from_balance = &mut from_account.info.balance;
let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
return Some(TransferError::OutOfFunds);
};
*from_balance = from_balance_decr;
// 增加to的balance
let to_account = self.state.get_mut(&to).unwrap();
Self::touch_account(&mut self.journal, to, to_account);
let to_balance = &mut to_account.info.balance;
let Some(to_balance_incr) = to_balance.checked_add(balance) else {
// Overflow of U256 balance is not possible to happen on mainnet. We don't bother to return funds from from_acc.
return Some(TransferError::OverflowPayment);
};
*to_balance = to_balance_incr;
// push journal entry,每次更改都push,用于后面revert
self.journal
.push(ENTRY::balance_transfer(from, to, balance));
None
}
继续看下 checkpoint_revert 的实现\ revert 的原理是每次涉及到更改都保存到journal中,保存的是差异更改,不是状态.\ 就像 git ,保存的是每次的差异,而不是每次都保存一份副本.\ revert 的时候从后往前撤销更改到 checkpoint .
这里的逻辑就不一一解释, 直接在注释中解释了.
// crates/context/src/journal/inner.rs
#[inline]
pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
let state = &mut self.state;
let transient_storage = &mut self.transient_storage;
// 检查点是在合约开始的时候生成的,
// 回滚也就是revert这次合约调用,所以depth-1
self.depth = self.depth.saturating_sub(1);
// 截断日志为checkpoint时保存的log 位置.
// 也就是删除checkpoint之后的所有日志
self.logs.truncate(checkpoint.log_i);
// self.journal.drain(checkpoint.journal_i..) 是删除checkpoint之后
// 的所有 entry,并以迭代器的形式返回这些元素.再rev逆序
// 也就是从后向前一步步恢复
if checkpoint.journal_i < self.journal.len() {
self.journal
.drain(checkpoint.journal_i..)
.rev()
.for_each(|entry| {
entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
});
}
}
关键的部分是 entry.revert, 里面针对每种情况进行了处理.\ 代码太长,不贴了,感兴趣的话自己去看下\ 位置在 crates/context/interface/src/journaled_state/entry.rs -> revert
继续 make_call_frame 的逻辑\ 这里是判断 tx.to 是否是 precompile 的地址.\ 如果是直接走 precompile 的逻辑,而不用走后面的解释器逻辑.节省时间、提升性能\ 这里就不深入** precompiles.run** 了
// crates/handler/src/frame.rs -> make_call_frame
let interpreter_input = InputsImpl {
target_address: inputs.target_address,
caller_address: inputs.caller,
bytecode_address: Some(inputs.bytecode_address),
input: inputs.input.clone(),
call_value: inputs.value.get(),
};
let is_static = inputs.is_static;
let gas_limit = inputs.gas_limit;
if let Some(result) = precompiles.run(ctx, &inputs).map_err(ERROR::from_string)? {
let mut logs = Vec::new();
if result.result.is_ok() {
ctx.journal_mut().checkpoint_commit();
} else {
// 如果precompile 运行错误,则revert
logs = ctx.journal_mut().logs()[checkpoint.log_i..].to_vec();
ctx.journal_mut().checkpoint_revert(checkpoint);
}
return Ok(ItemOrResult::Result(FrameResult::Call(CallOutcome {
result,
memory_offset: inputs.return_memory_offset.clone(),
was_precompile_called: true,
precompile_call_logs: logs,
})));
}
继续往下\ 这里处理的是一种特殊情况的分支.\ 是在 fork 主网、模拟交易时,调用者可能已经从 RPC 或缓存里拿到了 bytecode,直接传入 known_bytecode 更快,避免每次都走 load_account_with_code\ 如果known_bytecode不存在则从数据库中加载\ 如果是 bytecode 是空,说明to是EOA账户,直接返回
// crates/handler/src/frame.rs -> make_call_frame
let (bytecode, bytecode_hash) = if let Some((hash, code)) = inputs.known_bytecode.clone() {
(code, hash)
} else {
let account = ctx
.journal_mut()
.load_account_with_code(inputs.bytecode_address)?;
(
account.info.code.clone().unwrap_or_default(),
account.info.code_hash,
)
};
//
if bytecode.is_empty() {
ctx.journal_mut().checkpoint_commit();
return return_result(InstructionResult::Stop);
}
继续往下\ 初始化并启动新CallFrame\ get(EthFrame::invalid).clear找出stack中非法的frame并重置为新的CallFrame\ Ok(ItemOrResult::Item(this.consume())) 指示frame已经初始化
// crates/handler/src/frame.rs -> make_call_frame
this.get(EthFrame::invalid).clear(
FrameData::Call(CallFrame {
return_memory_range: inputs.return_memory_offset.clone(),
}),
FrameInput::Call(inputs),
depth,
memory,
ExtBytecode::new_with_hash(bytecode, bytecode_hash),
interpreter_input,
is_static,
ctx.cfg().spec().into(),
gas_limit,
checkpoint,
gas_params,
);
Ok(ItemOrResult::Item(this.consume()))
函数参数和上面一样:
里面好多部分都和 make_call_frame 差不多,我们主要讲解不一样的\ 这里是判断用的 Create 还是 Create2.\ 两者计算地址的方式不同,生成的地址也不一样.\ CREATE2 的生成跟 nonce 无关,所以可以每次生成一样的地址.
这里不深入细节了,感兴趣的自己去看下
// crates/handler/src/frame.rs -> make_create_frame
let mut init_code_hash = None;
let created_address = match inputs.scheme() {
CreateScheme::Create => inputs.caller().create(old_nonce),
CreateScheme::Create2 { salt } => {
let init_code_hash = *init_code_hash.insert(keccak256(inputs.init_code()));
inputs.caller().create2(salt.to_be_bytes(), init_code_hash)
}
CreateScheme::Custom { address } => address,
};
继续往下看\ 直接代码里解释了
// crates/handler/src/frame.rs -> make_create_frame
// 把交易的 init_code(初始化代码)包装成 ExtBytecode
let bytecode = ExtBytecode::new_with_optional_hash(
Bytecode::new_legacy(inputs.init_code().clone()),
init_code_hash,
);
// revm 解释器(Interpreter)的输入结构,描述“当前执行上下文”。
let interpreter_input = InputsImpl {
target_address: created_address,
caller_address: inputs.caller(),
bytecode_address: None,
input: CallInput::Bytes(Bytes::new()),
call_value: inputs.value(),
};
let gas_limit = inputs.gas_limit();
// 初始化并启动新 CreateFrame
// get(EthFrame::invalid).clear找出stack中非法的frame并重置为新的CreateFrame
this.get(EthFrame::invalid).clear(
FrameData::Create(CreateFrame { created_address }),
FrameInput::Create(inputs),
depth,
memory,
bytecode,
interpreter_input,
false,
spec,
gas_limit,
checkpoint,
gas_params,
);
继续回到 crates/handler/src/evm.rs -> frame_init
token 里面主要是bool值,指示知否初始化frame成功
Ok(res.map_frame(|token| {
if is_first_init {
unsafe { self.frame_stack.end_init(token) };
} else {
unsafe { self.frame_stack.push(token) };
}
self.frame_stack.get()
}))
还记得前面判断 is_first_init 是通过 self.frame_stack.index().is_none() 吗
let is_first_init = self.frame_stack.index().is_none()
我们进去看下 end_init\ 可以看到, end_init 会将 self.index 设置为 0 ,下次再执行 is_first_init 就会为 false
// crates/context/interface/src/local.rs
#[inline]
pub unsafe fn end_init(&mut self, token: FrameToken) {
token.assert();
if self.stack.is_empty() {
unsafe { self.stack.set_len(1) };
}
self.index = Some(0);
}
我们再去看下 push
// crates/context/interface/src/local.rs
#[inline]
pub unsafe fn push(&mut self, token: FrameToken) {
token.assert();
// 这里的index 和前面的depth效果很像
let index = self.index.as_mut().unwrap();
*index += 1;
// capacity of stack is incremented in `get_next`
debug_assert!(
*index < self.stack.capacity(),
"Stack capacity is not enough for index"
);
// 如果index是最后一个则增加长度
if *index == self.stack.len() {
unsafe { self.stack.set_len(self.stack.len() + 1) };
}
}
继续回到 crates/handler/src/handler.rs -> run_exec_loop
// to是eoa账户、预编译合约、或者其他失败情况
if let ItemOrResult::Result(frame_result) = res {
return Ok(frame_result);
}
继续往下,这里先讲下整体流程,后面再深入 frame_run 和 frame_return_result
loop {
// 尝试运行当前帧(执行一条或多条 opcode)
let call_or_result = evm.frame_run()?;
// 根据 frame_run 的返回,判断是“需要初始化新帧”还是“直接得到结果”
let result = match call_or_result {
ItemOrResult::Item(init) => {
match evm.frame_init(init)? {
// 新帧创建成功,继续循环执行这个新帧
ItemOrResult::Item(_) => {
continue;
}
// 初始化新帧时失败了
ItemOrResult::Result(result) => result,
}
}
ItemOrResult::Result(result) => result,
};
// 处理帧的返回结果(可能结束整个执行,也可能继续)
if let Some(result) = evm.frame_return_result(result)? {
return Ok(result);
}
// 如果 frame_return_result 返回 None,说明还需要继续执行(比如还有上层帧)
}
深入 frame_run
// crates/handler/src/evm.rs
#[inline]
fn frame_run(&mut self) -> Result<FrameInitOrResult<Self::Frame>, ContextDbError<CTX>> {
// 从frame_stack弹出当前frame
let frame = self.frame_stack.get();
let context = &mut self.ctx;
// instructions保存的是opcode和opcode对应的函数指针
let instructions = &mut self.instruction;
let action = frame
.interpreter
.run_plain(instructions.instruction_table(), context);
frame.process_next_action(context, action).inspect(|i| {
if i.is_result() {
frame.set_finished(true);
}
})
}
继续深入 run_plain\ 逻辑很简单.就是循环获取self.bytecode,如果不为空则执行 step
// crates/interpreter/src/interpreter.rs
#[inline]
pub fn run_plain<H: Host + ?Sized>(
&mut self,
instruction_table: &InstructionTable<IW, H>,
host: &mut H,
) -> InterpreterAction {
while self.bytecode.is_not_end() {
self.step(instruction_table, host);
}
self.take_next_action()
}
继续深入 step\ 流程都蛮清晰的\ 因为难点不是在流程这边,而是在opcode.\ 在这系列文章中我们先不深入opcode,后面会有专门的系列文章
#[inline]
pub fn step<H: Host + ?Sized>(
&mut self,
instruction_table: &InstructionTable<IW, H>,
host: &mut H,
) {
// 获取当前opcode
let opcode = self.bytecode.opcode();
// 跳到下一个opcode
self.bytecode.relative_jump(1);
// 获取opcode对应的函数
let instruction = unsafe { instruction_table.get_unchecked(opcode as usize) };
// 获取当前指令的静态gas
// 判断当前gas够不够,如果gas不够则返回gas不够的错误
if self.gas.record_cost_unsafe(instruction.static_gas()) {
return self.halt_oog();
}
let context = InstructionContext {
interpreter: self,
host,
};
// 执行
instruction.execute(context);
}
take_next_action
在 frame 运行过程中(执行opcode),会生成一些action.\ 例如 CALL、CREATE、RETURN,在前面提到过合约之间调用会生成新的 frame.\ 生成 action 后会 break 循环(while self.bytecode.is_not_end())\ take_next_action 就是将 action 取出来交给外层的 frame_run 进行处理.
// crates/interpreter/src/interpreter.rs
#[inline]
pub fn take_next_action(&mut self) -> InterpreterAction {
self.bytecode.reset_action();
// Return next action if it is some.
let action = core::mem::take(self.bytecode.action()).expect("Interpreter to set action");
action
}
继续回到 frame_run
frame.process_next_action(context, action).inspect(|i| {
if i.is_result() {
frame.set_finished(true);
}
})
跳转到具体实现
// crates/handler/src/frame.rs
pub fn process_next_action<
CTX: ContextTr,
ERROR: From<ContextTrDbError<CTX>> + FromStringError,
>(
&mut self,
context: &mut CTX,
next_action: InterpreterAction,
) -> Result<FrameInitOrResult<Self>, ERROR> {
let spec = context.cfg().spec().into();
// 在这里处理 take_next_action 返回的action
let mut interpreter_result = match next_action {
// 这里就是返回创建新的frame结果
// 实际处理不是在这里,而是外层handler的run_exec_loop,重走流程
InterpreterAction::NewFrame(frame_input) => {
let depth = self.depth + 1;
return Ok(ItemOrResult::Item(FrameInit {
frame_input,
depth,
memory: self.interpreter.memory.new_child_context(),
}));
}
// frame 结束(包含正常结束和不正常结束)
InterpreterAction::Return(result) => result,
};
// 处理frame的返回,这里很像tx.kind
// 只有call和create两种结果
let result = match &self.data {
FrameData::Call(frame) => {
// 结果正常
if interpreter_result.result.is_ok() {
context.journal_mut().checkpoint_commit();
} else {
// frame revert的情况
context.journal_mut().checkpoint_revert(self.checkpoint);
}
ItemOrResult::Result(FrameResult::Call(CallOutcome::new(
interpreter_result,
frame.return_memory_range.clone(),
)))
}
FrameData::Create(frame) => {
let max_code_size = context.cfg().max_code_size();
let is_eip3541_disabled = context.cfg().is_eip3541_disabled();
return_create(
context.journal_mut(),
self.checkpoint,
&mut interpreter_result,
frame.created_address,
max_code_size,
is_eip3541_disabled,
spec,
);
ItemOrResult::Result(FrameResult::Create(CreateOutcome::new(
interpreter_result,
Some(frame.created_address),
)))
}
};
Ok(result)
}
跳转回到 crates/handler/src/handler.rs -> run_exec_loop
if let Some(result) = evm.frame_return_result(result)? {
return Ok(result);
}
进去看下实现\ 直接在代码里进行解释了
// crates/handler/src/evm.rs
#[inline]
fn frame_return_result(
&mut self,
result: <Self::Frame as FrameTr>::FrameResult,
) -> Result<Option<<Self::Frame as FrameTr>::FrameResult>, ContextDbError<Self::Context>> {
// 判断当前frame是否完成结束
// 如果是则pop,此时栈顶变成父frame
if self.frame_stack.get().is_finished() {
self.frame_stack.pop();
}
// 前面如果pop后,index为none,说明到了最外层
// tx执行结束了
if self.frame_stack.index().is_none() {
return Ok(Some(result));
}
// 刚才pop之后,栈顶是父frame
// 这里是将当前frame 的return_result传递返回给父frame
self.frame_stack
.get()
.return_result::<_, ContextDbError<Self::Context>>(&mut self.ctx, result)?;
Ok(None)
}
继续跳转回到最外面的 crates/handler/src/handler.rs > execution
这里是对 Gas 进行退还处理
#[inline]
fn last_frame_result(
&mut self,
evm: &mut Self::Evm,
frame_result: &mut <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameResult,
) -> Result<(), Self::Error> {
let instruction_result = frame_result.interpreter_result().result;
// 当前frame的gas结构体,包含spent、remaining、refunded
let gas = frame_result.gas_mut();
// gas 的剩余,后面要返还给caller
let remaining = gas.remaining();
// 可退还的gas, SSTORE 清零、SELFDESTRUCT 等产生的 refund
let refunded = gas.refunded();
// 先预扣所有的gas,后面再计算退还
*gas = Gas::new_spent(evm.ctx().tx().gas_limit());
// 无论执行成功或者失败都退还执行剩余的gas
if instruction_result.is_ok_or_revert() {
gas.erase_cost(remaining);
}
// 只有执行成功才退还refund,如果是revert则不退还
if instruction_result.is_ok() {
gas.record_refund(refunded);
}
Ok(())
} 如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!