Skip to content

Commit

Permalink
Hybrid call stack (#202)
Browse files Browse the repository at this point in the history
  • Loading branch information
sorpaas authored Nov 7, 2023
1 parent b9f6be0 commit 2337db3
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 26 deletions.
3 changes: 1 addition & 2 deletions gasometer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ impl Gas for U256 {}
pub enum GasometerMergeStrategy {
Commit,
Revert,
Discard,
}

pub trait Gasometer<S, H>: Sized {
Expand All @@ -54,8 +53,8 @@ pub trait Gasometer<S, H>: Sized {
pub fn run_with_gasometer<S, H, Tr, G, F>(
machine: &mut Machine<S>,
gasometer: &mut G,
handler: &mut H,
is_static: bool,
handler: &mut H,
etable: &Etable<S, H, Tr, F>,
) -> Capture<ExitResult, Tr>
where
Expand Down
1 change: 0 additions & 1 deletion gasometer/src/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ impl<'config, H: Handler> Gasometer<RuntimeState, H> for StandardGasometer<'conf
GasometerMergeStrategy::Revert => {
self.used_gas -= Gasometer::<RuntimeState, H>::gas(&other);
}
GasometerMergeStrategy::Discard => (),
}
}
}
Expand Down
213 changes: 192 additions & 21 deletions src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use crate::{
Capture, Control, Etable, ExitError, ExitResult, Machine, Opcode, TransactionalBackend,
TransactionalMergeStrategy,
};
use core::convert::Infallible;

pub trait Invoker<S, H, Tr, G> {
type Interrupt;
type TrapData;

fn feedback_trap_data(
Expand All @@ -20,14 +22,15 @@ pub trait Invoker<S, H, Tr, G> {
fn prepare_trap_data(
&self,
trap: Tr,
depth: usize,
machine: &mut Machine<S>,
gasometer: &mut G,
handler: &mut H,
) -> Capture<Result<Self::TrapData, ExitError>, Tr>;
) -> Capture<Result<Self::TrapData, ExitError>, Self::Interrupt>;

fn build_child_stack(
&self,
trap_data: Self::TrapData,
trap_data: &Self::TrapData,
handler: &mut H,
) -> Result<(Machine<S>, G, bool), ExitError>;
}
Expand Down Expand Up @@ -61,6 +64,7 @@ enum LastCallStackData<S, Tr, G> {
pub struct CallStack<'backend, 'invoker, S, H, Tr, G, I: Invoker<S, H, Tr, G>> {
stack: Vec<TrappedCallStackData<S, G, I::TrapData>>,
last: LastCallStackData<S, Tr, G>,
initial_depth: usize,
backend: &'backend mut H,
invoker: &'invoker I,
}
Expand All @@ -75,6 +79,7 @@ where
machine: Machine<S>,
gasometer: G,
is_static: bool,
initial_depth: usize,
backend: &'backend mut H,
invoker: &'invoker I,
) -> Self {
Expand All @@ -87,14 +92,52 @@ where
let call_stack = Self {
stack: Vec::new(),
last,
initial_depth,
backend,
invoker,
};

call_stack
}

pub fn run<F>(mut self, etable: &Etable<S, H, Tr, F>) -> (Self, Capture<(), Tr>)
/// Calling `expect_exit` after `execute` returns `Capture::Exit` is safe.
pub fn expect_exit(self) -> (Machine<S>, G, ExitResult) {
match self.last {
LastCallStackData::Exited {
machine,
gasometer,
result: Capture::Exit(exit),
..
} => (machine, gasometer, exit),
_ => panic!("expected exit"),
}
}

pub fn execute<F>(
machine: Machine<S>,
gasometer: G,
is_static: bool,
initial_depth: usize,
backend: &'backend mut H,
invoker: &'invoker I,
etable: &Etable<S, H, Tr, F>,
) -> (Self, Capture<(), I::Interrupt>)
where
F: Fn(&mut Machine<S>, &mut H, Opcode, usize) -> Control<Tr>,
{
let call_stack = Self::new(
machine,
gasometer,
is_static,
initial_depth,
backend,
invoker,
);

call_stack.run(etable)
}

pub fn run<F>(mut self, etable: &Etable<S, H, Tr, F>) -> (Self, Capture<(), I::Interrupt>)
where
F: Fn(&mut Machine<S>, &mut H, Opcode, usize) -> Control<Tr>,
{
Expand All @@ -108,11 +151,11 @@ where
}
}

fn step<F>(mut self, etable: &Etable<S, H, Tr, F>) -> (Self, Option<Capture<(), Tr>>)
fn step<F>(mut self, etable: &Etable<S, H, Tr, F>) -> (Self, Option<Capture<(), I::Interrupt>>)
where
F: Fn(&mut Machine<S>, &mut H, Opcode, usize) -> Control<Tr>,
{
let mut step_ret: Option<Capture<(), Tr>> = None;
let mut step_ret: Option<Capture<(), I::Interrupt>> = None;

self.last = match self.last {
LastCallStackData::ExternalTrapped {
Expand All @@ -132,8 +175,8 @@ where
let result = run_with_gasometer(
&mut machine,
&mut gasometer,
self.backend,
is_static,
self.backend,
etable,
);
LastCallStackData::Exited {
Expand Down Expand Up @@ -183,9 +226,6 @@ where
Err(_) => {
self.backend
.pop_substate(TransactionalMergeStrategy::Discard);
upward
.gasometer
.merge(gasometer, GasometerMergeStrategy::Discard);
}
};

Expand Down Expand Up @@ -216,25 +256,40 @@ where
Capture::Trap(trap) => {
match self.invoker.prepare_trap_data(
trap,
self.initial_depth + self.stack.len() + 1,
&mut machine,
&mut gasometer,
self.backend,
) {
Capture::Exit(Ok(trap_data)) => {
self.backend.push_substate();

match self.invoker.build_child_stack(trap_data, self.backend) {
Ok((machine, gasometer, is_static)) => LastCallStackData::Running {
machine,
gasometer,
is_static,
},
Err(err) => LastCallStackData::Exited {
machine,
gasometer,
is_static,
result: Capture::Exit(Err(err)),
},
match self.invoker.build_child_stack(&trap_data, self.backend) {
Ok((sub_machine, sub_gasometer, sub_is_static)) => {
self.stack.push(TrappedCallStackData {
trap_data,
machine,
gasometer,
is_static,
});

LastCallStackData::Running {
machine: sub_machine,
gasometer: sub_gasometer,
is_static: sub_is_static,
}
}
Err(err) => {
self.backend
.pop_substate(TransactionalMergeStrategy::Discard);

LastCallStackData::Exited {
machine,
gasometer,
is_static,
result: Capture::Exit(Err(err)),
}
}
}
}
Capture::Exit(Err(err)) => LastCallStackData::Exited {
Expand All @@ -260,3 +315,119 @@ where
(self, step_ret)
}
}

pub fn execute<S, H, Tr, G, I, F>(
mut machine: Machine<S>,
mut gasometer: G,
is_static: bool,
initial_depth: usize,
heap_depth: Option<usize>,
backend: &mut H,
invoker: &I,
etable: &Etable<S, H, Tr, F>,
) -> (Machine<S>, G, ExitResult)
where
G: Gasometer<S, H>,
H: TransactionalBackend,
I: Invoker<S, H, Tr, G, Interrupt = Infallible>,
F: Fn(&mut Machine<S>, &mut H, Opcode, usize) -> Control<Tr>,
{
let mut result = run_with_gasometer(&mut machine, &mut gasometer, is_static, backend, etable);

loop {
match result {
Capture::Exit(exit) => return (machine, gasometer, exit),
Capture::Trap(trap) => {
let prepared_trap_data: Capture<Result<I::TrapData, ExitError>, Infallible> =
invoker.prepare_trap_data(
trap,
initial_depth + 1,
&mut machine,
&mut gasometer,
backend,
);

match prepared_trap_data {
Capture::Exit(Ok(trap_data)) => {
backend.push_substate();

match invoker.build_child_stack(&trap_data, backend) {
Ok((sub_machine, sub_gasometer, sub_is_static)) => {
let (sub_machine, sub_gasometer, sub_result) = if heap_depth
.map(|hd| initial_depth + 1 >= hd)
.unwrap_or(false)
{
let (call_stack, _infallible) = CallStack::execute(
sub_machine,
sub_gasometer,
sub_is_static,
initial_depth + 1,
backend,
invoker,
etable,
);

call_stack.expect_exit()
} else {
execute(
sub_machine,
sub_gasometer,
sub_is_static,
initial_depth + 1,
heap_depth,
backend,
invoker,
etable,
)
};

match sub_result {
Ok(_) => {
backend.pop_substate(TransactionalMergeStrategy::Commit);
gasometer
.merge(sub_gasometer, GasometerMergeStrategy::Commit);
}
Err(ExitError::Reverted) => {
backend.pop_substate(TransactionalMergeStrategy::Discard);
gasometer
.merge(sub_gasometer, GasometerMergeStrategy::Revert);
}
Err(_) => {
backend.pop_substate(TransactionalMergeStrategy::Discard);
}
}

match invoker.feedback_trap_data(
sub_result,
sub_machine,
trap_data,
&mut machine,
&mut gasometer,
backend,
) {
Ok(()) => {
result = run_with_gasometer(
&mut machine,
&mut gasometer,
is_static,
backend,
etable,
);
}
Err(err) => return (machine, gasometer, Err(err)),
}
}
Err(err) => {
backend.pop_substate(TransactionalMergeStrategy::Discard);

return (machine, gasometer, Err(err));
}
}
}
Capture::Exit(Err(err)) => return (machine, gasometer, Err(err)),
Capture::Trap(infallible) => match infallible {},
}
}
}
}
}
3 changes: 1 addition & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
extern crate alloc;

mod backend;
mod executor;
pub mod executor;

pub use evm_gasometer as gasometer;
pub use evm_interpreter::*;

pub use crate::backend::{TransactionalBackend, TransactionalMergeStrategy};
pub use crate::executor::{CallStack, Invoker};
pub use crate::gasometer::Config;

0 comments on commit 2337db3

Please sign in to comment.